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

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:
the gcc frontend to produce assembler code
then run the GNU assembler to get an object file
and then link it with the C runtime to get a working executable.
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?

/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).

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
$

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.

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'

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

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:
add lib64 to LD_LIBRARY_PATH
use lc_noshared
reinstall glibc-devel

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.

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

Related

a linker issue when learning static library [duplicate]

When I try to build the following program:
#include <stdio.h>
int main(void)
{
printf("hello world\n");
return 0;
}
On OS X 10.6.4, with the following flags:
gcc -static -o blah blah.c
It returns this:
ld: library not found for -lcrt0.o
collect2: ld returned 1 exit status
Has anyone else encountered this, or is it something that noone else has been affected with yet? Any fixes?
Thanks
This won’t work. From the man page for gcc:
This option will not work on Mac OS X unless all libraries (including libgcc.a) have also been compiled with -static. Since neither a static version of libSystem.dylib nor crt0.o are provided, this option is not useful to most people.
Per Nate's answer, a completely static application is apparently not possible - see also man ld:
-static Produces a mach-o file that does not use the dyld. Only used building the kernel.
The problem in linking with static libraries is that, if both a static and a dynamic version of a library are found in the same directory, the dynamic version will be taken in preference. Three ways of avoiding this are:
Do not attempt to find them via the -L and -l options; instead, specify the full paths, to the libraries you want to use, on the compiler or linker command line.
$ g++ -Wall -Werror -o hi /usr/local/lib/libboost_unit_test_framework.a hi.cpp
Create a separate directory, containing symbolic links to the static libraries, use the -L option to have this directory searched first, and use the -l option to specify the libraries you want to use.
$ g++ -Wall -Werror -L ./staticBoostLib -l boost_unit_test_framework -o hi hi.cpp
Instead of creating a link of the same name in a different directory, create a link of a different name in the same directory, and specify that name in a -l argument.
$ g++ -Wall -Werror -l boost_unit_test_framework_static -o hi hi.cpp
You may also try LLVM LLD linker - I did prebuilt version for my two major OSes - https://github.com/VerKnowSys/Sofin-llds
This one allows me to link for exmple: "Qemu" properly - which is impossible with ld preinstalled by Apple.
And last one is - to build GCC yourself with libstdc++ (don't).

Error "undefined reference" while linking in MinGW

I have got the object-file from source code using MinGW.
But on linking:
ld -o test.exe test.o
I get errors, for example the following:
undefined reference to printf
First, why are you using ld directly?
The following is an excerpt from the "GCC and Make" tutorial found at http://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html.
Compile and Link Separately
The above command compile the source file into object file and link with other object files (system library) into executable in one step. You may separate compile and link in two steps as follows:
// Compile-only with -c option
> g++ -c -Wall -g Hello.cpp
// Link object file(s) into an executable
> g++ -g -o Hello.exe Hello.o
Note g++ (you can substitute gcc if you are using C and not C++) is used both for compiling and linking. ld is not used at all.
The benefit of using g++ or gcc to link is that it will link with default libraries, such as the one you need to link with for printf, automatically.
To link with other libraries, you specify the library name with the -l parameter, as in -lmylib.
We can view commands ran by compiler via command
c99 -v test.o
We'll get some text. All after string which contains "COLLECT_CGG_OPTIONS" will be arguments of ld.
But size of executable file is much more then size of file got by previous way.

ld : _start not found defaulting to

Hi I made a simple hello world C program and I am compiling it like this :
gcc -c hello.c
ld hello.o -lc -o out
I get a warning from ld : ld : _start not found defaulting to ....
I do an objdump -D hello.o and I cannot find the _start routine in the output.
What am I missing here ?
You're missing the crt* stuff which you will see if you link with gcc -v: crt1.o, crtend.o, crtn.o. Look at how gcc invokes collect2 (it's visible with gcc -v) and use the same options for ld.
main function is not the executable entry point: some initialization for standard library is done before main (because it's either impossible or illogical to do otherwise). Real entry point, which is _start by default, is in crt1.o which is always linked into your executable.
It's because you don't have a main function, also you can try
gcc -v hello.c -o hello
See if it compiles successfully.
On my system (Angstrom Linux, gcc 4.3.3), it happened due me installing libgcc-s-dev instead of libgcc-dev. No binary on the system contained _start string, I checked that. Installing libgcc-dev helped.

How to make clang compile to llvm IR

I want clang to compile my C/C++ code to LLVM bitcode rather than a binary executable. How can I achieve that?
And if I have the LLVM bitcode, how can I further compile it to a binary executable?
I want to add some of my own code to the LLVM bitcode before compiling to a binary executable.
Given some C/C++ file foo.c:
> clang -S -emit-llvm foo.c
Produces foo.ll which is an LLVM IR file.
The -emit-llvm option can also be passed to the compiler front-end directly, and not the driver by means of -cc1:
> clang -cc1 foo.c -emit-llvm
Produces foo.ll with the IR. -cc1 adds some cool options like -ast-print. Check out -cc1 --help for more details.
To compile LLVM IR further to assembly, use the llc tool:
> llc foo.ll
Produces foo.s with assembly (defaulting to the machine architecture you run it on). llc is one of the LLVM tools - here is its documentation.
Use
clang -emit-llvm -o foo.bc -c foo.c
clang -o foo foo.bc
If you have multiple source files, you probably actually want to use link-time-optimization to output one bitcode file for the entire program. The other answers given will cause you to end up with a bitcode file for every source file.
Instead, you want to compile with link-time-optimization
clang -flto -c program1.c -o program1.o
clang -flto -c program2.c -o program2.o
and for the final linking step, add the argument -Wl,-plugin-opt=also-emit-llvm
clang -flto -Wl,-plugin-opt=also-emit-llvm program1.o program2.o -o program
This gives you both a compiled program and the bitcode corresponding to it (program.bc). You can then modify program.bc in any way you like, and recompile the modified program at any time by doing
clang program.bc -o program
although be aware that you need to include any necessary linker flags (for external libraries, etc) at this step again.
Note that you need to be using the gold linker for this to work. If you want to force clang to use a specific linker, create a symlink to that linker named "ld" in a special directory called "fakebin" somewhere on your computer, and add the option
-B/home/jeremy/fakebin
to any linking steps above.
If you have multiple files and you don't want to have to type each file, I would recommend that you follow these simple steps (I am using clang-3.8 but you can use any other version):
generate all .ll files
clang-3.8 -S -emit-llvm *.c
link them into a single one
llvm-link-3.8 -S -v -o single.ll *.ll
(Optional) Optimise your code (maybe some alias analysis)
opt-3.8 -S -O3 -aa -basicaaa -tbaa -licm single.ll -o optimised.ll
Generate assembly (generates a optimised.s file)
llc-3.8 optimised.ll
Create executable (named a.out)
clang-3.8 optimised.s
Did you read clang documentation ? You're probably looking for -emit-llvm.

Linking C function lib to x86 assembly program in modern 64bit Linux

I'm going through a book focusing on x86 programming (Professional Assembly Language, WROX 2005). I had some problems last night and I was hoping to sort this out before returning home today so I can get a running-start and continue the text. My machine runs x64 Ubuntu (11.04 if I'm not mistaken) so the text focusing on 32bit x86 is slightly 'outdated' (I have to add --32 when assembling etc).
I am trying to dynamically link C-library functions with my assembly program but I am unsuccesfull (below commands are from memory).
ld -dynamic-linking /lib/ld-linux.so.2 -o complex -lc complex.o -m elf_i386
Running the above command in Linux gives me the message that it can't understand -lc. Okay, so I removed it.
ld -dynamic-linking /lib/ld-linux.so.2 -o complex complex.o -m elf_i386
I then get the notification that 'printf' is not recognised. The hopes was for the dynamic linker to link to the library but it does not seem to do so. Going to \lib\ I could not locate ld-linux.so.2 (strangely it didn't give me an error on this) but I did locate ld-linux-86-64.so.2. My code is 32bit but I thought what the heck, let's try this:
ld -dynamic-linking /lib/ld-linux-86-64.so.2 -o complex complex.o -m elf_i386
Still it gave the same error that 'call printf' was not recognized.
Need help dynamically linking C library functions with my 32bit Assembly program using 64bit Linux and standard GNU tools.
Sounds like you need to install the 32-bit C-runtime. Under Fedora this is:
yum install glibc-devel.i686
But I don't know the name of the equivalent Ubunutu package; perhaps:
apt-get install libc6-dev-i386
It is almost always a bad idea to try to construct a ld command line yourself. Let GCC do it for you; it automatically handles all sorts of subtleties that you don't want to have to worry about. For a 32-bit program, you do need one special command line switch, -m32:
gcc -m32 -o complex complex.o
If you have more .o files, just stack them up at the end. If you need to link against any system libraries other than libc, put appropriate -lwhatever options after all the object files.
trojanfoe is also correct; the 32-bit toolchain is an optional component. But you need more than just the 32-bit C library. Try this first:
apt-get install gcc-multilib
it should pull in most of what you need.
Try the following order please(suppose your code fil is try.s):
as --32 -g -o try.o try.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -lc -o try try.o
For x86-64 format executable file:
as -g -o try.o try.s
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -o try try.o

Resources