Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Frank O

macrumors newbie
Original poster
Apr 7, 2020
27
6
I'm interested in relearning assembly basics, writing x86_64 using NASM on my Intel-based Mac running Sonoma.

I installed NASM and refreshed Xcode command line tools, and have tried some simple "Hello, world" programs from various blogs. Most of these are a few years or more old, and various problems have come up. In general, nasm seems to run ok, but many linker errors appear. Most recently, the linker seemed to complete, but when I ran the a.out file it returned "zsh: killed ./a.out".

Rather than spending time with the errors I'm seeing, I'd be very interested if anyone who is successfully writing assembly in recent macOS versions could share some basics on linking syntax, required components of the .asm, etc. I have a strong preference for Intel coding syntax. Some sources say do put an underscore before "start", others say no, etc.
 

casperes1996

macrumors 604
Jan 26, 2014
7,433
5,575
Horsens, Denmark
So I have never been able to get a binary to start from a start label in modern macOS; I'm not sure dyld loads it that way anymore. However, I can get one to start from _main with the C standard library and program loader
Be sure to assemble a Mach-O binary.

The easiest way may be to just make a C file like the following

Code:
external void asmStart(void);

int main(void) {
    asmStart();
    return 0;
}

and link it and your nasm output together with a simple makefile. Then run the C program and let it invoke your assembly.

But you can omit the C program

Make an asm file
Code:
global: _main

section: .text

_main:
; instructions go here

assemble it
nasm -f macho64 -o asm.o asm.asm

and link it like this
ld -o asm asm.o -platform_version macos 14.0 14.0

Note that no code snippet above was checked for errors, compiled or verified, but the ideas are known to work.
 
  • Like
Reactions: Basic75 and Frank O

casperes1996

macrumors 604
Jan 26, 2014
7,433
5,575
Horsens, Denmark
I would also add that all of this works perfectly on Apple Silicon Macs too - In fact you don't even need to start an x86 zsh session; Rosetta deals with all of it transparently
 

Frank O

macrumors newbie
Original poster
Apr 7, 2020
27
6
But you can omit the C program

Make an asm file ...

Thanks. So I took code from one of the more recent blog examples, and pasted it in under your code:

global: _main section: .text _main: mov rax, 0x2000004 ; system call for write mov rdi, 1 ; file handle 1 is stdout mov rsi, msg ; address the string message mov rdx, msg.len ; number of bytes syscall ; invoke operating system to do the write mov rax, 0x2000001 ; system call for exit mov rdi, 0 ; exit code 0 syscall ; invoke operating system to exit section .data msg: db "Hello, World!", 10 ; note the newline at the end .len: equ $ - msg

When I ran nasm -f macho64 -o xyz.o xyz.asm it returned with:

xyz.asm:1: error: identifier expected after global, got `: _main' xyz.asm:3: error: unknown section name : xyz.asm:3: error: segment name `:' not recognized

Hopefully something tweak(s) can fix?
 

casperes1996

macrumors 604
Jan 26, 2014
7,433
5,575
Horsens, Denmark
Thanks. So I took code from one of the more recent blog examples, and pasted it in under your code:

global: _main section: .text _main: mov rax, 0x2000004 ; system call for write mov rdi, 1 ; file handle 1 is stdout mov rsi, msg ; address the string message mov rdx, msg.len ; number of bytes syscall ; invoke operating system to do the write mov rax, 0x2000001 ; system call for exit mov rdi, 0 ; exit code 0 syscall ; invoke operating system to exit section .data msg: db "Hello, World!", 10 ; note the newline at the end .len: equ $ - msg

When I ran nasm -f macho64 -o xyz.o xyz.asm it returned with:

xyz.asm:1: error: identifier expected after global, got `: _main' xyz.asm:3: error: unknown section name : xyz.asm:3: error: segment name `:' not recognized

Hopefully something tweak(s) can fix?

Output sounds like I was too aggressive with the colons. They probably shouldn't have been after global and section.
I did it by memory. I have my assembly folder on a different machine - Try removing those colons for now and only keeping the colon after the label _main and if it doesn't work I'll check my other machine for reference
 
  • Like
Reactions: Frank O

casperes1996

macrumors 604
Jan 26, 2014
7,433
5,575
Horsens, Denmark
Output sounds like I was too aggressive with the colons. They probably shouldn't have been after global and section.
I did it by memory. I have my assembly folder on a different machine - Try removing those colons for now and only keeping the colon after the label _main and if it doesn't work I'll check my other machine for reference

Yeah; no colon on those
1699475533860.png


(it says _start not _main because I just tried once more to see what the linker would say to that; It says it can't find _main)
 

Frank O

macrumors newbie
Original poster
Apr 7, 2020
27
6
Yeah; no colon on those
View attachment 2309455

(it says _start not _main because I just tried once more to see what the linker would say to that; It says it can't find _main)

Great, I removed those two colons, and nasm produced an .o file, no messages.

When I ran the linker line you suggested, it completed but issued two warnings:

ld: warning: no platform load command found in '/Users/myname/asm/xyz2.o', assuming: macOS
ld: warning: pointer not aligned at _main+0xC from /Users/myname/asm/xyz2.o

When I ran the executable, it just produced another terminal prompt (i.e. as if I'd just hit return). I wonder if there might be something about the setup for the system call to print from the blog code I used? Is there another simple piece of code known to run on current-day Macs that I could try using instead?
 

Frank O

macrumors newbie
Original poster
Apr 7, 2020
27
6
With some more tinkering, I was able to compile an executable that produces the "Hello, world!" text when run.

The source:

Code:
section .data
    hello db 'Hello, world!', 0xa  ; String with a newline character at the end
    helloLen equ $ - hello          ; Length of the 'hello' string

section .text
    global _main

_main:
    ; write syscall
    mov rax, 0x2000004           ; syscall number for write
    mov rdi, 1                   ; file descriptor 1 is stdout
    lea rsi, [rel hello]         ; address of the string to output
    mov rdx, helloLen            ; length of the string
    syscall                      ; make the syscall

    ; exit syscall
    mov rax, 0x2000001           ; syscall number for exit
    xor rdi, rdi                 ; exit code 0
    syscall                      ; make the syscall

Then:

Code:
nasm -f macho64 -o xyz.o xyz.asm

ld -macos_version_min 10.7.0 -L$(xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib -lSystem -o xyz xyz.o

The linker works, but returns a warning, "ld: warning: no platform load command found in '/Users/myname/asm/xyz.o', assuming: macOS". I saw one option for avoiding this by inserting a "__LINKEDIT" section in the source, but for the time being I think I'll just ignore the warning.
 

casperes1996

macrumors 604
Jan 26, 2014
7,433
5,575
Horsens, Denmark
With some more tinkering, I was able to compile an executable that produces the "Hello, world!" text when run.

The source:

Code:
section .data
    hello db 'Hello, world!', 0xa  ; String with a newline character at the end
    helloLen equ $ - hello          ; Length of the 'hello' string

section .text
    global _main

_main:
    ; write syscall
    mov rax, 0x2000004           ; syscall number for write
    mov rdi, 1                   ; file descriptor 1 is stdout
    lea rsi, [rel hello]         ; address of the string to output
    mov rdx, helloLen            ; length of the string
    syscall                      ; make the syscall

    ; exit syscall
    mov rax, 0x2000001           ; syscall number for exit
    xor rdi, rdi                 ; exit code 0
    syscall                      ; make the syscall

Then:

Code:
nasm -f macho64 -o xyz.o xyz.asm

ld -macos_version_min 10.7.0 -L$(xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib -lSystem -o xyz xyz.o

The linker works, but returns a warning, "ld: warning: no platform load command found in '/Users/myname/asm/xyz.o', assuming: macOS". I saw one option for avoiding this by inserting a "__LINKEDIT" section in the source, but for the time being I think I'll just ignore the warning.

I honestly don't know how to get rid of the linker warning about platform load commands. It's a warning that doesn't matter as its assumption is correct. If you make a C program and get clang to emit assembly, it has a
.build_version macOS 14, 0
directive at the top that I assume is what the linker wants, but this is not a legal field in nasm. I'm sure there is a solution, but I don't know it.

And as you've already solved it I'm not sure if you know this, but I'll elaborate a bit on why your older version did not print "Hello World" correctly.

macOS aggressively follows a 64-bit ABI. As a result it has some strict requirements for its program code that are not present on, for example, Linux. This includes alignment constraints, like the base-pointer being 16-bit aligned and, in this case importantly, no absolute addressing being permitted.

As you do in your revised example you can use lea (load effective address) instead of mov. And then you use [rel hello] in your example; To make this a bit nicer as you will need to do this everywhere you reference data, you can add

default rel
in the top of your .data section; Then you don't need the "rel" inside every lea reference.

With this, the assembler will perform rip-relative addressing
 
  • Like
Reactions: Frank O

Frank O

macrumors newbie
Original poster
Apr 7, 2020
27
6
Great, thanks for that explanation. I was really just trying bit and pieces from various sources, so the background is really helpful.

Are there any good resources on writing 64-bit assembly on Intel-based Macs running recent macOS versions? I have a copy of Ray Seyfarth's book "Introduction to 64 Bit Assembly Programming for Linux and OS X." But the last update was in 2013, and I didn't know if that would be an issue.
 
Last edited:

casperes1996

macrumors 604
Jan 26, 2014
7,433
5,575
Horsens, Denmark
Great, thanks for that explanation. I was really just trying bit and pieces from various sources, so the background is really helpful.

Are there any good resources on writing 64-bit assembly on Intel-based Macs running recent macOS versions? I have a copy of Ray Sefarth's book "Introduction to 64 Bit Assembly Programming for Linux and OS X." But the last update was in 2013, and I didn't know if that would be an issue.
Aside from small little gotchas here and there all the information in that book is probably still relevant. New instructions are added but nothing is fundamentally different now in x86_64 to what it was then. macOs has changed a little though.

You also don't necessarily need to specify it's an Intel Mac when you search around. You're writing x86_64 assembly so it's for Intel, but Apple Silicon Macs will work just the same with nasm and all, just through Rosetta 2.

This seems like a decent page explaining a few of the gotchas for macOS relative to what will work on Linux

Similarly this also seems like a good resource on macOS assembly and system interaction in general

This here is the AMD64 ABI; Very useful for referencing things like which registers should be caller vs callee safe and such

Intel's processor manual; Similarly useful to the above ABI spec

Apple's list of system calls and their numbers:

But aside from the little nuance differences; Assembly is assembly. Any information you find on writing x86_64 will hold true on macOS as well as Linux inside your own process' space. Just gotta follow the system ABI when calling outside of it.
 
  • Like
Reactions: Frank O
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.