17

I am trying to dive into some x86 assembly programming on my Mac, but am having trouble producing an executable. The problem seems to be at the linking stage.

helloWorld.s:

.data

    HelloWorldString:
    .ascii "Hello World\n"

.text

.globl _start

_start:
    # load all the arguments for write()
    movl $4, %eax
    movl $1, %ebx
    movl $HelloWorldString, %ecx
    movl $12, %edx
    # raises software interrupt to call write()
    int $0x80

    # call exit()
    movl $1, %eax
    movl $0, %ebx
    int $0x80

Assemble the program:

as -o helloWorld.o helloWorld.s

Link the object file:

ld -o helloWorld helloWorld.o

The error I get at this point is:

ld: could not find entry point "start" (perhaps missing crt1.o) for inferred architecture x86_64

Any advice on what I'm doing wrong / missing would be very helpful. thanks

starblue
  • 55,348
  • 14
  • 97
  • 151
D.C.
  • 15,340
  • 19
  • 71
  • 102

5 Answers5

21

You'll probably find it easier to build with gcc rather than trying to micro-manage the assembler and linker, e.g.

$ gcc helloWorld.s -o helloWorld

(You'll probably want to change _start to _main if you go this route.)

Incidentally, it can be instructive to start with a working C program, and study the generated asm from this. E.g.

#include <stdio.h>

int main(void)
{
    puts("Hello world!\n");

    return 0;
}

when compiled with gcc -Wall -O3 -m32 -fno-PIC hello.c -S -o hello.S generates:

    .cstring
LC0:
    .ascii "Hello world!\12\0"
    .text
    .align 4,0x90
.globl _main
_main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $LC0, (%esp)
    call    _puts
    xorl    %eax, %eax
    leave
    ret
    .subsections_via_symbols

You might want to consider using this as a template for your own "Hello world" or other experimental asm programs, especially given that it already builds and runs:

$ gcc -m32 hello.S -o hello
$ ./hello 
Hello world!

One final comment: beware of taking examples from Linux-oriented asm books or tutorials and trying to apply them under OS X - there are important differences !

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • Any idea why I'd get this error while doing `gcc -m32 hello.S -o hello`? ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in _main from /var/folders/9b/n3lsk87513d57pzh0qvxjmz00000gn/T/hello-r4fQK2.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie – michaelsnowden Feb 15 '14 at 09:28
  • Are you using an unusual platform ? – Paul R Feb 15 '14 at 14:02
  • 2
    @michaelsnowden the code was generated with -fno-PIC, so if you want to compile it you need to use `gcc -m32 -Wl,-no_pie hello.S -o hello`. Alternatively, generate the code without `-fno-PIC`. That will give you slightly more complex boilerplate, but then you can compile it without having to deal with the PIE issue. – Rikkles Dec 02 '15 at 08:37
  • 1
    2023 update: `i386` architecture is deprecated and no longer supported. Instead of the `-m32` parameter, you should use `-m64`. Surprisingly, after so many years, just these two small changes are needed to compile and link the code above. – RAllen Feb 05 '23 at 01:54
4

Try:

ld -e _start -arch x86_64 -o HelloWorld HelloWorld.S

then:

./HelloWorld

Info:

-e <entry point>
-arch <architecture>, You can check your architecture by uname -a 
-o <output file>
Rafał Rawicki
  • 22,324
  • 5
  • 59
  • 79
abdimuna1
  • 41
  • 1
  • Yes that will assemble+link, but the code in the question is *not* for x86-64 MacOS. It's for i386 Linux. After fixing that (see NoOffenceIntended's answer), then this answer will work. You can leave out the `-e _start` part if you give your entry point the standard (for MacOS) label of `start:` instead of `_start:` – Peter Cordes Jul 09 '19 at 04:54
2

hello.asm

.data

    HelloWorldString:
    .ascii "Hello World!\n"

.text

.globl start

start:
    ; load all the arguments for write()
    movl $0x2000004, %eax
    movl $1, %ebx
    movq HelloWorldString@GOTPCREL(%rip), %rsi
    movq $100, %rdx
    ; raises software interrupt to call write()
    syscall

    ; call exit()
    movl $0x2000001, %eax
    movl $0, %ebx
    syscall

Then run:

$ as -arch x86_64  -o hello.o hello.asm
$ ld -o hello hello.o
$ ./hello

This is a working solution for Mac OS X Mach-0 GNU-based assemblers

  • This worked for me but had to remove the commented code in .asm file. Also passing in `-macosx_version_min 10.6` to linker command avoids warning, though it compiles anyway. – lacostenycoder Nov 17 '16 at 11:32
  • Printing 100 bytes when your string is shorter will include a bunch of garbage in the output. Probably all `0` bytes which don't have any effect on a terminal so you didn't fix this bug. Also, `exit()` takes its arg from EDI, not EBX. Maybe you're thinking of the 32-bit Linux int 0x80 ABI? Oh, that was copied from the question. Also there's no need to fetch a pointer from memory (from the GOT) for access to your own static data. Just calculate it directly with a RIP-relative LEA like @NoOffenceIntended's answer. – Peter Cordes Jul 09 '19 at 03:50
  • Also, `;` is not the comment character for `as` for x86-64. It separate statements / instructions on the same line, so the assembler will try to parse your comments as code. If you tested this at all, I assume it was before adding comments. – Peter Cordes Jul 09 '19 at 03:52
1

The code in the question looks like it's for 32-bit Linux using the int $0x80 ABI with args in EBX, ECX, EDX.

x86-64 code on MacOS uses the syscall instruction, with arg-passing and return value similar to what's documented in the x86-64 System V ABI for Linux. It's completely different from int $0x80, the only similarity being that the call number is passed in EAX/RAX. But the call numbers are different: https://sigsegv.pl/osx-bsd-syscalls/ ORed with 0x2000000.

Args go in the same registers as for function calls. (except R10 instead of RCX.)

See also basic assembly not working on Mac (x86_64+Lion)? and How to get this simple assembly to run?


I think this is a lot neater and more intuitive version of what was suggested in another answer.

OS X uses start, not _start, for the process entry point.

.data
str:
  .ascii "Hello world!\n"
  len = . - str                  # length = start - end.   . = current position

.text
.globl start
start:
    movl   $0x2000004, %eax
    movl   $1, %edi
    leaq   str(%rip), %rsi  
    movq   $len, %rdx          
    syscall                       # write(1, str, len)

    movl   $0x2000001, %eax 
    movl   $0, %edi
    syscall                       # _exit(0)

Normally you'd omit the operand-size suffix when a register implies it. And use xor %edi,%edi to zero RDI.

And use mov $len, %edx because you know the size is smaller than 4GB so a more efficient 32-bit zero-extended mov-immediate will work, like you're doing to set RAX to the call number.

Notice the use of a RIP-relative LEA to get the address of static data into a register. x86-64 code on MacOS can't use 32-bit absolute addressing because the base address where your executable will be mapped is above 2^32.

There are no relocation types for 32-bit absolute addresses so you can't use them. (And you want RIP-relative, not 64-bit absolute, even though that's also supported.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Yes, this code is better. It still had one bug which I fixed, now I think it's a good example. I also added some explanatory text. I didn't fix the inefficiencies of wasted code-size (I left the operand-sizes matching the documented system call arg widths, instead of using implicit zero-extension). And I didn't put the read-only string data into a read-only-data section. (Like `.rodata` on Linux, IDK what OS X calls it.) – Peter Cordes Jul 09 '19 at 04:12
0

To assemble and link the code in @NoOffenceIntended's answer on MacOS 10.15, the following changes need to be made:

Change .global _start to .global main, and _start: to main:

To assemble and link the code use:

as -arch x86_64 -o hello.o hello.asm
ld -arch x86_64 -o hello hello.o -lSystem

This is assuming that "Apple clang version 12.0.0" of "as" is being used and the corresponding "ld" is used.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
znih
  • 1
  • 2
  • Which "above code" are you talking about? There are multiple other answers with code, if you meant one of those, include a link to it (and maybe mention the author's name). Or if you mean the code in the question, it uses 32-bit Linux `int $0x80` system calls with args in registers per the Linux 32-bit ABI; MacOS/Darwin puts args for `int $0x80` on the stack. But IIRC, 10.15 doesn't support 32-bit code at all, so I doubt you're talking about the question. – Peter Cordes Nov 27 '21 at 05:44
  • Sorry about that. The code I was referring to was posted by @NoOffenceIntended on 7/9/19 at 3:39 and posted / edited by Peter Cordes on7/9/19 at 4:24. (I'm not sure how to link directly to that code example.) – znih Nov 30 '21 at 05:33
  • Every answer has a "share" link under it, which you can use to get a link directly to it. I edited this to add the link. – Peter Cordes Nov 30 '21 at 06:03