How to link C++ object files with ld

43,393

If you run g++ with the -v flag, you'll see the link line it uses. Here's a simple example program:

#include <iostream>

int main(void)
{
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

And the output from running g++ -v -o example example.cpp:

Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.4.4-14ubuntu5.1' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 
COLLECT_GCC_OPTIONS='-v' '-o' 'example' '-shared-libgcc' '-mtune=generic'
 /usr/lib/gcc/x86_64-linux-gnu/4.4.5/cc1plus -quiet -v -D_GNU_SOURCE example.cpp -D_FORTIFY_SOURCE=2 -quiet -dumpbase example.cpp -mtune=generic -auxbase example -version -fstack-protector -o /tmp/ccV8qjvd.s
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../x86_64-linux-gnu/include"
ignoring nonexistent directory "/usr/include/x86_64-linux-gnu"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/4.4
 /usr/include/c++/4.4/x86_64-linux-gnu
 /usr/include/c++/4.4/backward
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.4.5/include
 /usr/lib/gcc/x86_64-linux-gnu/4.4.5/include-fixed
 /usr/include
End of search list.
GNU C++ (Ubuntu/Linaro 4.4.4-14ubuntu5.1) version 4.4.5 (x86_64-linux-gnu)
    compiled by GNU C version 4.4.5, GMP version 4.3.2, MPFR version 3.0.0-p3.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: d92fbc2d715a3b7e0f4133f0c40053e4
COLLECT_GCC_OPTIONS='-v' '-o' 'example' '-shared-libgcc' '-mtune=generic'
 as -V -Qy -o /tmp/ccGHR0pc.o /tmp/ccV8qjvd.s
GNU assembler version 2.20.51 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.20.51-system.20100908
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/:/lib/../lib/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../:/lib/:/usr/lib/:/usr/lib/x86_64-linux-gnu/
COLLECT_GCC_OPTIONS='-v' '-o' 'example' '-shared-libgcc' '-mtune=generic'
 /usr/lib/gcc/x86_64-linux-gnu/4.4.5/collect2 --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o example -z relro /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../.. -L/usr/lib/x86_64-linux-gnu /tmp/ccGHR0pc.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crtn.o

Wow, what a mess. Conveniently the link line is the last one there, so you can see what's happening pretty easily.

As you noticed in your comment below, the front-end is using collect2 rather than ld. Luckily, collect2 is just an alias for ld. Here's an example using it:

First let's generate an object file:

$ ls
example.cpp
$ c++ -c example.cpp
$ ls
example.cpp example.o

Then we'll use the front-end to link it to see the link line:

$ c++ -v -o example example.o
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.4.4-14ubuntu5.1' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/:/lib/../lib/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../:/lib/:/usr/lib/:/usr/lib/x86_64-linux-gnu/
COLLECT_GCC_OPTIONS='-v' '-o' 'example' '-shared-libgcc' '-mtune=generic'
 /usr/lib/gcc/x86_64-linux-gnu/4.4.5/collect2 --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o example -z relro /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../.. -L/usr/lib/x86_64-linux-gnu example.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crtn.o

Then throw away the binary, and link ourselves (normally, I would have just copy/pasted the line, but to make it easier to read I did it the multiline way with \s):

$ ls
example  example.cpp  example.o
$ rm example
$ ls
example.cpp example.o
$ ld                                                              \
> --build-id                                                      \
> --eh-frame-hdr                                                  \
> -m elf_x86_64                                                   \
> --hash-style=gnu                                                \
> -dynamic-linker                                                 \
> /lib64/ld-linux-x86-64.so.2                                     \
> -o example                                                      \
> -z relro                                                        \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crt1.o      \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crti.o      \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtbegin.o                  \
> -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5                           \
> -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5                           \
> -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib           \
> -L/lib/../lib                                                   \
> -L/usr/lib/../lib                                               \
> -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../..                  \
> -L/usr/lib/x86_64-linux-gnu                                     \
> example.o                                                       \
> -lstdc++                                                        \
> -lm                                                             \
> -lgcc_s                                                         \
> -lgcc                                                           \
> -lc                                                             \
> -lgcc_s                                                         \
> -lgcc                                                           \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtend.o                    \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crtn.o

Finally, run it!

$ ls
example example.cpp example.o
$ ./example 
Hello, world!

You can probably significantly shorten that link line by removing some arguments. Here's the minimal set I came up with after some experimentation:

$ ld                                                              \
> -dynamic-linker                                                 \
> /lib64/ld-linux-x86-64.so.2                                     \
> -o example                                                      \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crt1.o      \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crti.o      \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtbegin.o                  \
> example.o                                                       \
> -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5                           \
> -lstdc++                                                        \
> -lc                                                             \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtend.o                    \
> /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crtn.o

This set of flags and libraries will of course depend on what library functions and language features your program uses.

Share:
43,393
gsgx
Author by

gsgx

I'm currently studying computer science at the University of Michigan. I enjoy working on operating systems and compilers, developing Android applications, and hacking on the Android Open Source Project.

Updated on July 09, 2022

Comments

  • gsgx
    gsgx almost 2 years

    I'm trying to link the output of C++ using ld and not g++. I'm only doing this to learn how to do it, not for practical purposes, so please don't suggest just to do it with g++.

    Looking at this question, the person gets the same error when they run the ld command:

    $ ld test.o -o test.out
    ld: warning: cannot find entry symbol _start; defaulting to 00000000004000e8
    test.o: In function `main':
    test.cpp:(.text+0x1c): undefined reference to `strcasecmp'
    test.cpp:(.text+0x23): undefined reference to `std::cout'
    test.cpp:(.text+0x28): undefined reference to `std::ostream::operator<<(int)'
    test.cpp:(.text+0x2d): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)'
    test.cpp:(.text+0x35): undefined reference to `std::ostream::operator<<(std::ostream& (*)(std::ostream&))'
    test.o: In function `__static_initialization_and_destruction_0(int, int)':
    test.cpp:(.text+0x75): undefined reference to `std::ios_base::Init::Init()'
    test.cpp:(.text+0x7a): undefined reference to `__dso_handle'
    test.cpp:(.text+0x84): undefined reference to `std::ios_base::Init::~Init()'
    test.cpp:(.text+0x89): undefined reference to `__cxa_atexit'
    ld: test.out: hidden symbol `__dso_handle' isn't defined
    ld: final link failed: Bad value
    

    The answers in the linked post suggest that adding the C++ library as a linker argument will fix the problem, so I tried

    ld test.o -o test.out -llibstd++
    

    which is what they suggested, and I also tried a lot of other library names like libstdc++ or stdc++. But I'll always get an error that looks like

    ld: cannot find -llibstd++
    

    What am I doing wrong and how can I link my object files using ld?

    • Jonathan Wakely
      Jonathan Wakely over 11 years
      -lstdc++ is correct, but you may also need to use -L dir to tell the linker where to find that library.
    • Kristopher Micinski
      Kristopher Micinski over 6 years
      I'm just going to give another huge shoutout to @gsingh2011 for asking how to do this. It is not at all a naive or stupid request. Even if you would never do this in real life, knowing that you can do it is a crucial exercise (I believe) in understanding how the toolchain works.
  • gsgx
    gsgx over 11 years
    Can't I use ld instead of collect2? What's the difference?
  • Carl Norum
    Carl Norum over 11 years
    @gsingh2011 - collect2 is ld. Check out my added example.
  • Employed Russian
    Employed Russian over 11 years
    @gsingh2011 You can use ld instead of g++, but you shouldn't do that without a very good reason. The command that g++ uses can change from one release of g++ to the next, and if you don't update your ld command to match, you may be in for many nasty surprises.
  • touchStone
    touchStone almost 9 years
    @CarlNorum Thats exactly the solution and beautiful explanation and also note even -### could be used which works similar to -v.
  • Carl Norum
    Carl Norum over 8 years
    @AngelusMortis, the -lstdc++ flag, presumably.
  • Carl Norum
    Carl Norum over 8 years
    @AngelusMortis, probably not. You'll almost certainly need the runtime files too. The list above is the minimum set I came up with for this particular test. You can do an experiment of your own to compare. Why don't you just use the compiler driver to link like you're supposed to?
  • Billy
    Billy about 5 years
    Just wondering why you change the g++ to c++ later, i think they are refer to the same compiler.