user500944
user500944

Reputation:

Linking a C program directly with ld fails with undefined reference to `__libc_csu_fini`

I'm trying to compile a C program under Linux. However, out of curiosity, I'm trying to execute some steps by hand: I use:

Now I'm stuck with the linking part.

The program is a very basic "Hello world":

#include <stdio.h>
int main() {
   printf("Hello\n");
   return 0;
}

I use the following command to produce the assembly code:

gcc hello.c -S -masm=intel

I'm telling gcc to quit after compiling and dump the assembly code with Intel syntax.

Then I use th GNU assembler to produce the object file:

as -o hello.o hello.s

Then I try using ld to produce the final executable:

ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello

But I keep getting the following error message:

/usr/lib/crt1.o: In function `_start':
(.text+0xc): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x11): undefined reference to `__libc_csu_init'

The symbols __libc_csu_fini/init seem to be a part of glibc, but I can't find them anywhere! I tried linking against libc statically (against /usr/lib/libc.a) with the same result.

What could the problem be?

Upvotes: 24

Views: 39169

Answers (8)

evan
evan

Reputation: 29

Take it:

    $ echo 'main(){puts("ok");}' > hello.c
    $ gcc -c hello.c -o hello.o
    
    $ ld hello.o -o hello.exe /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o \
-dynamic-linker /lib/ld-linux.so.2 -lc
    
    $ ./hello.exe
    ok

Path to /usr/lib/crt*.o will when glibc configured with --prefix=/usr

Upvotes: 0

In Ubuntu 14.04 (GCC 4.8), the minimal linking command is:

ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
  /usr/lib/x86_64-linux-gnu/crt1.o \
  /usr/lib/x86_64-linux-gnu/crti.o \
  -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
  -lc -lgcc -lgcc_s \
  hello.o \
  /usr/lib/x86_64-linux-gnu/crtn.o

Although they may not be necessary, you should also link to -lgcc and -lgcc_s, since GCC may emit calls to functions present in those libraries for operations which your hardware does not implement natively, e.g. long long int operations on 32-bit. See also: Do I really need libgcc?

I had to add:

  -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \

because the default linker script does not include that directory, and that is where libgcc.a was located.

As mentioned by Michael Burr, you can find the paths with gcc -v. More precisely, you need:

gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'

Upvotes: 3

Robᵩ
Robᵩ

Reputation: 168626

I found another post which contained a clue: -dynamic-linker /lib/ld-linux.so.2.

Try this:

$ gcc hello.c -S -masm=intel
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o
$ ./hello
hello, world
$ 

Upvotes: 7

detys
detys

Reputation: 11

This is how I fixed it on ubuntu 11.10:

apt-get remove libc-dev

Say yes to remove all the packages but copy the list to reinstall after.

apt-get install libc-dev

Upvotes: 1

Matthew Slattery
Matthew Slattery

Reputation: 46998

/usr/lib/libc.so is a linker script which tells the linker to pull in the shared library /lib/libc.so.6, and a non-shared portion, /usr/lib/libc_nonshared.a.

__libc_csu_init and __libc_csu_fini come from /usr/lib/libc_nonshared.a. They're not being found because references to symbols in non-shared libraries need to appear before the archive that defines them on the linker line. In your case, /usr/lib/crt1.o (which references them) appears after /usr/lib/libc.so (which pulls them in), so it doesn't work.

Fixing the order on the link line will get you a bit further, but then you'll probably get a new problem, where __libc_csu_init and __libc_csu_fini (which are now found) can't find _init and _fini. In order to call C library functions, you should also link /usr/lib/crti.o (after crt1.o but before the C library) and /usr/lib/crtn.o (after the C library), which contain initialisation and finalisation code.

Adding those should give you a successfully linked executable. It still won't work, because it uses the dynamically linked C library without specifying what the dynamic linker is. You'll need to tell the linker that as well, with something like -dynamic-linker /lib/ld-linux.so.2 (for 32-bit x86 at least; the name of the standard dynamic linker varies across platforms).

If you do all that (essentially as per Rob's answer), you'll get something that works in simple cases. But you may come across further problems with more complex code, as GCC provides some of its own library routines which may be needed if your code uses certain features. These will be buried somewhere deep inside the GCC installation directories...

You can see what gcc is doing by running it with either the -v option (which will show you the commands it invokes as it runs), or the -### option (which just prints the commands it would run, with all of the arguments quotes, but doesn't actually run anything). The output will be confusing unless you know that it usually invokes ld indirectly via one of its own components, collect2 (which is used to glue in C++ constructor calls at the right point).

Upvotes: 50

Michael Burr
Michael Burr

Reputation: 340208

Assuming that a normal invocation of gcc -o hello hello.c produces a working build, run this command:

gcc --verbose -o hello hello.c

and gcc will tell you how it's linking things. That should give you a good idea of everything that you might need to account for in your link step.

Upvotes: 5

Vinicius Kamakura
Vinicius Kamakura

Reputation: 7778

Since you are doing the link process by hand, you are forgetting to link the C run time initializer, or whatever it is called.

To not get into the specifics of where and what you should link for you platform, after getting your intel asm file, use gcc to generate (compile and link) your executable.

simply doing gcc hello.c -o hello should work.

Upvotes: 0

mwk
mwk

Reputation: 436

If you're running a 64-bit OS, your glibc(-devel) may be broken. By looking at this and this you can find these 3 possible solutions:

  1. add lib64 to LD_LIBRARY_PATH
  2. use lc_noshared
  3. reinstall glibc-devel

Upvotes: 0

Related Questions