Skip to content

Instantly share code, notes, and snippets.

Last active November 22, 2023 09:10
Star You must be signed in to star a gist
What would you like to do?
NASM Hello World for x86 and x86_64 Intel Mac OS X (get yourself an updated nasm with brew)
; /usr/local/bin/nasm -f macho 32.asm && ld -macosx_version_min 10.7.0 -o 32 32.o && ./32
global start
section .text
push dword msg.len
push dword msg
push dword 1
mov eax, 4
sub esp, 4
int 0x80
add esp, 16
push dword 0
mov eax, 1
sub esp, 12
int 0x80
section .data
msg: db "Hello, world!", 10
.len: equ $ - msg
; /usr/local/bin/nasm -f macho64 64.asm && ld -macosx_version_min 10.7.0 -lSystem -o 64 64.o && ./64
global start
section .text
mov rax, 0x2000004 ; write
mov rdi, 1 ; stdout
mov rsi, msg
mov rdx, msg.len
mov rax, 0x2000001 ; exit
mov rdi, 0
section .data
msg: db "Hello, world!", 10
.len: equ $ - msg
Copy link

Ahh, I got it working now. I replace the start with _main as it simply skip the main function. I think there's an option to avoid this but I have no idea yet what option in ld

Would love to see how you got this going. Not sure where specifically you replaced "start" with "main". I tried changing both, I got as far as:

asm/hello % ld -macosx_version_min 10.12.0 -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lSystem -o hello hello.o
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64

My code now looks like this:

global main
section .text
mov rax, 0x2000004 ;write
mov rdi, 1 ;stdout
mov rsi, msg
mov rdx, msg.len
mov rax, 0x2000001 ;exit
mov rdi, 0
section .data
msg: db "Hello world!", 10
.len equ $ - msg

Hi @kc2kth, literally use "_main" not "main". Then try to build again.

Copy link

kc2kth commented Jun 24, 2021

Hi @kc2kth, literally use "_main" not "main". Then try to build again.

Great, that did work. Happy to have a working "jumping off" point now.


Copy link

nullhook commented Jan 16, 2022

for macOS users you have two options. You can statically link or dynamically link which will require libSystem.*
ld links dynamically to your system libraries by default.

Static linking:
ld -e "start" -static -o 64 64.o

Dynamic linking:
ld -macosx_version_min 10.7.0 -e "start" -no_pie -lSystem -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -o 64 64.o

If the linker cannot find specific symbols you can inspect your object file to see all symbols
(note: your start symbol doesn't need to be renamed, just needs to be referenced as-is from the symbol table)

objdump -t ./64.o

0000000000000027 l     O __DATA,__data msg
000000000000000e l       *ABS* msg.len
0000000000000000 g     F __TEXT,__text start

Copy link

DiaperGlu commented Apr 13, 2022

A couple of things about the hello world above.

OS X deprecated syscall. It still works but if you look at the compiled code, the gas compiler is replacing the syscall with imported function calls. Since they are function calls, you are supposed to align stack. It seems to work without it, but if you want to align the stack to a 16 byte boundary without worrying about where it is when the program starts, you can AND the stack pointer with FFFFFFFFFFFFFFF0 at entry.

I think exported functions have to have _ in front of them in gas. Something to do with avoiding name collisions with imports maybe..

Mac also changed where the system libraries are for security reasons for Big Sur. If you want your program to run on a system that doesn't have the xtools command line tools installed, you have to link in a certain way. I posted a question about this on the Mac developer forums and Eskimo explained how to figure it out using -v with a c program. The link to the discussion is here:
The short answer is, this link command works: ld -e _myhelloworldstart -no_uuid -no_eh_labels -demangle -dynamic -arch x86_64 -platform_version macos 11.0.0 11.1 -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -o helloworld64 -L/usr/local/lib helloworld64.o -lSystem
The key is the -syslibroot command.
I think if you use OTOOL to look what the library is linking against, you can see what's going on.

This hello world works for gas with the filename helloworld64gas.s:


.asciz "\nHello World!\n\n"

.globl _myhelloworldstart

/* align the stack regardless of where it is when this function is called */
andq $0xfffffffffffffff0, %rsp

movq $1, %rdi /* stdout handle /
leaq _omyhelloworldmessage(%rip), %rsi /
pmessageaddress /
movq $15, %rdx /
messagelength */
call _write

call _exit

with this assembly command:

as -mmacosx-version-min=11.0 ./helloworld64gas.s -o helloworld64gas.o

and this link command:

ld -e _myhelloworldstart -no_uuid -no_eh_labels -demangle -dynamic -arch x86_64 -platform_version macos 11.0.0 11.1 -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -o helloworld64gas -L/usr/local/lib helloworld64gas.o -lSystem

If you are interested, and up for something different,
I wrote my own assembler which is available on here on github under the DiaperGlu repository.
This hello world code works with that assembler:




OSYMBOL omyhelloworldmessage
13 CODE-U8, 10 CODE-U8, // pushes crlf to the buffer
$" Hello World!" PCURRENTCOMPILEBUFFER @ $>BUF // pushes the message to the buffer
13 CODE-U8, 10 CODE-U8, // appends another crlf
13 CODE-U8, 10 CODE-U8, // and another crlf

OSYMBOL myhelloworldstart
// align the stack regardless of where it is when this function is called

1 N RDI MOV, // stdout handle
EH. omyhelloworldmessage [O] RSI LEA, // pmessageaddress
EH. myhelloworldstart EH. omyhelloworldmessage - N RDX MOV, // messagelength



1 EH[ND]

DUP $" helloworld64.o" SAVEFILE$



Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment