Linker Error When Calling Printf from _Start

Linker error when calling printf from _start

Cause of Your Error

The reason you get undefined reference to printf while linking is because you didn't link against the C library. As well, when using your own _start label as an entry point there are other issues that must be overcome as discussed below.


Using LIBC without C Runtime Startup Code

To make sure you are using the appropriate dynamic linker at runtime and link against the C library you can use GCC as a frontend for LD. To supply your own _start label and avoid the C runtime startup code use the -nostartfiles option. Although I don't recommend this method, you can link with:

gcc -m64 -nostartfiles main.o -o main

If you wish to use LD you can do it by using a specific dynamic linker and link against the C library with -lc. To link x86_64 code you can use:

ld -melf_x86_64 --dynamic-linker=/lib64/ld-linux-x86-64.so.2 main.o -lc -o main

If you had been compiling and linking for 32-bit, then the linking could have been done with:

ld -melf_i386 --dynamic-linker=/lib/ld-linux.so.2 main.o -lc -o main

Preferred Method Using C Runtime Startup Code

Since you are using the C library you should consider linking against the C runtime. This method also works if you were to create a static executable. The best way to use the C library is to rename your _start label to main and then use GCC to link:

gcc -m64 main.o -o main

Doing it this way allows you to exit your program by returning (using RET) from main the same way a C program ends. This also has the advantage of flushing standard output when the program exits.


Flushing output buffers / exit

Using syscall with EAX=60 to exit won't flush the output buffers. Because of this you may find yourself having to add a newline character on your output to see output before exiting. This is still not a guarantee you will see what you output with printf. Rather than the sys_exit SYSCALL you might consider calling the C library function exit. the MAN PAGE for exit says:

All open stdio(3) streams are flushed and closed. Files created by tmpfile(3) are removed.

I'd recommend replacing the sys_exit SYSCALL with:

extern exit

xor edi, edi ; In 64-bit code RDI is 1st argument = return value
call exit

C Program, linker error

There's no print function in standard C. The function you are looking for is printf (f here is short for formatted.

printf("Merry Christmas and happy holidays!");

Linker error while trying to use assembler code in cpp module

If you read the error message you will see that the function it can not find is named _sayHello. Note the leading underscore.

To solve your problem you must name your assembler function with a leading underscore:

global _sayHello
_sayHello:
...

Linking an assembler program: error undefined reference to `printf'

You have a couple of options

  • Use LD to link to a final executable
  • Use GCC to link to a final executable

Using LD Method

Your command lines use LD, unfortunately that presents a number of problems. The first:

ld: i386 architecture of input file `0_strange_calc.o' is incompatible with i386:x86-6

You are on 64-bit Debian, trying to produce a 32-bit executable. -f elf on the NASM command line generates 32-bit ELF (-f elf64 generate 64 bit objects). Your LD command line is by default trying to generate a 64-bit executable thus the error above is given. You can force LD to generate a 32-bit executable by adding the -m elf_i386 option to LD's command line.

ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0

You should tell LD that your entry point is main . LD by default looks for an entry point of _start. You can add -e main to the LD command line to resolve that.

Errors like this suggest you need the C library (where printf exists):

0_strange_calc.asm:(.text+0x8): undefined reference to `printf'

Since your code doesn't use printf directly I can only assume that is required by the functions in training.s . In order to link in the C library you will need to add it after the .o files in your command line. You can do this with -lc on your LD command line. You'll also need to tell LD specifically what dynamic linker library you will need to use (In this case a 32-bit one). In a Debian environment that will usually look like: -dynamic-linker /lib/ld-linux.so.2

So your NASM and LD lines should look like this:

nasm -f elf -g 0_strange_calc.asm
ld -melf_i386 -e main -dynamic-linker /lib/ld-linux.so.2 -o 0_strange_calc 0_strange_calc.o -lc

Using GCC Method

You can simplify linking to the C library by using GCC to link you your object file to an executable. To build a 32-bit executable you could use:

nasm -f elf -g 0_strange_calc.asm
gcc -m32 0_strange_calc.o -o 0_strange_calc

The C library and runtime has a _start method that does C startup initialization and in turn calls a function called main which happens to be the function in your assembly file. -m32 tells GCC you are also linking to a 32-bit executable.


Special Considerations

You may also need to to install the Multlilib versions of gcc (and g++ if you want to) so that you can properly build and run 32-bit applications on 64-bit Debian using the appropriate C libraries. That can be done with this command line:

apt-get install gcc-multilib g++-multilib

On Ubuntu based systems you'd need to use:

sudo apt-get install gcc-multilib g++-multilib


Related Topics



Leave a reply



Submit