- NASM Manual - for the syntax
- What are the calling conventions for UNIX & Linux system calls on x86-64 - comparison
- AMD64 ABI reference - A.2.1 - for the actual x86_64 calling convention
- X86 calling conventions - moar calling conventions
- Mac OS X syscalls reference
-
-
Save FiloSottile/7125822 to your computer and use it in GitHub Desktop.
; /usr/local/bin/nasm -f macho 32.asm && ld -macosx_version_min 10.7.0 -o 32 32.o && ./32 | |
global start | |
section .text | |
start: | |
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 | |
start: | |
mov rax, 0x2000004 ; write | |
mov rdi, 1 ; stdout | |
mov rsi, msg | |
mov rdx, msg.len | |
syscall | |
mov rax, 0x2000001 ; exit | |
mov rdi, 0 | |
syscall | |
section .data | |
msg: db "Hello, world!", 10 | |
.len: equ $ - msg |
I also need to do :
push dword 0
mov eax, 1 ; 0 ; should not be 1
sub esp, 4 ; 12 ; not sure about this and note there is a push dword 0 here
int 0x80
to work under OS X Sierra 10.12.6.
Also, the first few line is mysterious
section .text
start:
push dword msg.len ;4 bytes
push dword msg ;4 bytes
push dword 1 ;4 bytes. and there is push dword 1 here
mov eax, 4
sub esp, 4 ;reserves 4 bytes more
int 0x80
add esp, 16 ;restore esp to original value
; usually should be
; push ebp
; mov ebp, esp
; push ??? the 4 bytes not sure what that for
; ...
; pop ??? the 4 bytes
; pop ebp
May be c calling standard, Win32 calling standard or ...
FiloSottile:
Thank you very much. I am trying to learn assembly language on a Mac.
When attempting to link I got this:
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for inferred architecture x86_64
anybody able to shed a little light?
Hello, @2019osbjoh !
I have replaced start
to _main
and it works for me. (osx version: 10.12; nasm version: 2.14.02)
src:
global _main
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
mov rsi, msg
mov rdx, msg.len
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
section .data
msg: db "Hello, world!", 10
.len: equ $ - msg
build:
$ nasm -f macho64 hello-world.asm && ld -macosx_version_min 10.12 -lSystem -o hello-world hello-world.o
You can make a custom global start/_main to name it however you want with this:
ld -e start -macosx_version_min 10.13.0 -static -o hello-world hello-world.o
Using "static" linking instead of dynamic, in which case you can't control the name.
Anyone interested in assembly or native x64 generation for macOS in a really simple / clean language should consider offering some input on macOS x64 generation for V: vlang/v#5374 (updated)
Seeing the native x64 generation in action on Linux is quite impressive (https://twitter.com/v_language/status/1271163362739707910) and it sure would be useful for folks to have it on macOS as well.
On 11.1 I've been getting:
ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in _main from hello.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie
To get rid of this warning, you have two options:
- Pass the
-no-pie
option to the linker, like so:nasm -f macho64 hello-world.asm && ld -macosx_version_min 10.12 -lSystem -o hello-world hello-world.o -no-pie
. - (Seems to be the preferred option) Use RIP-relative addresses, as described here. Updating r-novel's code:
default rel ; add this line
global _main
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [msg] ; replace `mov rsi, msg` with this
mov rdx, msg.len
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
section .data
msg: db "Hello, world!", 10
.len: equ $ - msg
For macOS 11 it's necessary to specify the location of the System library if you get an error indicating it's not found, such as -L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib
when running the linker
Thanks @alblue!
I got error:
ld: library not found for -lSystem
@simohauml
Make sure you have xcode-cli installed. Go to Library/Developer/CommandLineTools/ and make sure SDKs is there. I had the same problem and it turns out I had Xcode but not the CLI installed, so my CommandLineTools didn't have SDK and I was getting the same error even using Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib with my linker.
Any idea,
$ ld -macosx_version_min 10.15 -lSystem -o hello hello.o -no_pie
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
fails
But,
$ ld -macosx_version_min 10.7.0 -lSystem -o hello hello.o -no_pie
ld: warning: building for macOS 10.7.0 is deprecated
works but warning.
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
Ahh, I got it working now. I replace the
start
with_main
as it simply skip themain
function. I think there's an option to avoid this but I have no idea yet what option inld
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
main:
mov rax, 0x2000004 ;write
mov rdi, 1 ;stdout
mov rsi, msg
mov rdx, msg.len
syscall
mov rax, 0x2000001 ;exit
mov rdi, 0
syscall
section .data
msg: db "Hello world!", 10
.len equ $ - msg
Ahh, I got it working now. I replace the
start
with_main
as it simply skip themain
function. I think there's an option to avoid this but I have no idea yet what option inld
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_64My code now looks like this:
global main
section .text
main:
mov rax, 0x2000004 ;write
mov rdi, 1 ;stdout
mov rsi, msg
mov rdx, msg.len
syscall
mov rax, 0x2000001 ;exit
mov rdi, 0
syscall
section .data
msg: db "Hello world!", 10
.len equ $ - msg
Hi @kc2kth, literally use "_main" not "main". Then try to build again.
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.
Thanks!
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
SYMBOL TABLE:
0000000000000027 l O __DATA,__data msg
000000000000000e l *ABS* msg.len
0000000000000000 g F __TEXT,__text start
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: https://developer.apple.com/forums/thread/669094
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:
.text
_omyhelloworldmessage:
.asciz "\nHello World!\n\n"
.globl _myhelloworldstart
_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:
NEW-FLAT-OSYMBOL-BUF
X86-WORDLIST >SEARCH-ORDER
DECIMAL
OSYMBOL omyhelloworldmessage
13 CODE-U8, 10 CODE-U8, // pushes crlf 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
HEX -10 N RSP AND,
1 N RDI MOV, // stdout handle
EH. omyhelloworldmessage [O] RSI LEA, // pmessageaddress
EH. myhelloworldstart EH. omyhelloworldmessage - N RDX MOV, // messagelength
IMP CALL, OSYMBOL-IMPORT write
IMP CALL, OSYMBOL-IMPORT exit
PCURRENTCOMPILEBUFFER @
EH
1 EH[ND]
BUF>NEWEXPORTIMPORT.OBUF
DUP
FREEBUFFER
SEARCH-ORDER> DROP
FREE-FLAT-OSYMBOL-BUF
When I run the program, the return value is 1, but if I change
sub esp, 12
tosub esp, 4
as it was in old revisions, then it works, return value 0. Why does it happen like that ? On OS X Sierra 10.12.6