Can't link object file using ld - Mac OS X

tutormike picture tutormike · May 28, 2013 · Viewed 16.7k times · Source
/*********
exit.asm
*/

[SECTION .text]

global _start


_start:
xor eax, eax
xor ebx, ebx
mov al, 1
int 0x80

//****************************

First I used nasm -f elf exit.asm to generate the object file.

then I ran the following "ld" command on my Mac OS X 10.7, it has the these outputs and warning, I tried to run it on my 32 bit linux machine, everything went through just fine, Could you please explain why would not the linker work on my Mac?

Thank you!

Alfred says: ld -o exiter exit.o
ld: warning: -arch not specified
ld: warning: -macosx_version_min not specified, assuming 10.7
ld: warning: ignoring file exit.o, file was built for unsupported file format ( 0x7f 0x45      0x4c 0x46 0x 1 0x 1 0x 1 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 ) which is not the   architecture being linked (x86_64): exit.o
Undefined symbols for architecture x86_64:
  "start", referenced from:
    implicit entry/start for main executable
ld: symbol(s) not found for inferred architecture x86_64

after I specify my arch and version, I got:

Alfred says: ld -arch x86_64 -macosx_version_min 10.7 -o exiter exit.o
ld: warning: ignoring file exit.o, file was built for unsupported file format ( 0x7f 0x45     0x4c 0x46 0x 1 0x 1 0x 1 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 0x 0 ) which is not the   architecture being linked (x86_64): exit.o
 Undefined symbols for architecture x86_64:
 "start", referenced from:
    implicit entry/start for main executable
 ld: symbol(s) not found for architecture x86_64

Answer

scottt picture scottt · May 28, 2013

Getting the program to link is the easy part:

  • Change _start to start
  • $ nasm -f macho exit.asm
  • $ ld -arch i386 -o exiter exit.o

The problem is that exit.asm is calling the i386 Linux exit() system call (EAX = 1) and the program would NOT exit with a zero status as intended on OS X.

System Calls

A system call is a request to the kernel. exit(), unlike sqrt(), must make a request to a software component with higher privileges in its implementation since it terminates a running program. Apps can't create or terminate processes by themselves. System calls provide a way for apps to ask the kernel to perform actions on their behalf.

Making a syscall goes something like this:

  • Apps describe the operation they want to perform by placing data in CPU registers (or memory pointed to by registers), e.g.
    • The value 1 in EAX is the system call number of exit.
    • The value 0 in EBX (EBX was cleared by xor) is the first argument to the syscall, the exit status.
  • Apps issue an instruction that causes control to transfer to the kernel, e.g.
    • int 80 on i386
    • sycall on x86-64
    • svc in Thumb mode on ARMv7
  • The kernel inspects the request and decides to perform or deny it.
  • The kernel transfers control back to the app with the return value in an agreed upon location, e.g. EAX on i386.

Linux and OS X both provide a void exit(int) function for C programs but don't agree on the details on how to describe this request to the kernel. The code in exit.asm is at the same level as the implementation of the _exit() function in libc.

Even between different architectures running Linux the syscall numbers and calling convention differ. e.g. On x86-64 Linux, exit(0) is more commonly issued like this:

xor rdi, rdi
mov al, 60
syscall

You can see this by disassembling _exit in /lib64/libc.so.6.

Can't We Just Call exit() from libc Instead?

You can. But you'd have to link the program with libc. It's the difference between linking exit.asm above with:

$ cc -m32 -nostdlib exit.o -o exiter

and

exit-libc.asm

extern exit
global main
main:
push 0
call exit

which has to be linked with:

$ cc -m32 exit-libc.o -o exit-libc

Try this and take a look at the file size.