Static Linking for C++ Shared Objects

Prerequisites

Assuming recent-vintage linux with standard C/C++ developer setup, including g++, readelf, ld, gold etc., etc. Reference platform is Fedora 17 (F17) using gcc-4.7.2. Linking static libraries may require the installation of non-default packages. For C/C++, this means:

yum install -y glibc-static libstdc++-static

Problem Description

Create a shared library that uses C++ internally, but with an external C interface and all dependent libraries statically-linked.

Some quick background/primer on creating a shared library with current GNU tools on recent linux. Consider the default case, making a shared library.

Take the first source file, a generic library function implemented in C++ and exported via extern C.

// filename: 28811.cc
#include <iostream>
#include <string>

extern "C" void announce()
{
  const std::string s("oooohllalala");
  std::cout << s << std::endl;
}

And then the second file, which uses it.

// filename: 28811-loop.cc
extern "C" void announce();

int main()
{
  announce();
  return 0;
}

Compile via:

g++ -shared -fPIC -O2 -g 28811.cc -o libannounce.so

g++ -g -O2 -L. 28811-loop.cc -lannounce -o 28811.exe

To run the executable, first make sure that LD_RUN_PATH has the directory containing the shared library libannounce.so. Then:

28811.exe

Which should print:

oooohllalala

Now that that is confirmed to be working, here’s a closer look at the library file libannounce.so that was produced.

A look at the linked dependencies:

%ldd libannounce.so 
	linux-vdso.so.1 =>  (0x00007fffed1ff000)
	libstdc++.so.6 => /mnt/share/bld/gcc.git-trunk/x86_64-unknown-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6 (0x00007fd87e7e5000)
	libm.so.6 => /lib64/libm.so.6 (0x00007fd87e4c3000)
	libgcc_s.so.1 => /mnt/share/bld/gcc.git-trunk/gcc/libgcc_s.so.1 (0x00007fd87e4ad000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fd87e0f6000)
	/lib64/ld-linux-x86-64.so.2 (0x0000003b72200000)

This shows that the support library has a depedent link on the C language runtime (ie libc.so and libm.so, the GNU C/C++ language support libray (libgcc_s.so), the C++ language runtime (libstdc++.so), and the linux dynamic loader (linux-vdso.so).

A look at what’s in the library:

readelf -s libannounce.so | grep announce
    25: 0000000000000d20   226 FUNC    GLOBAL DEFAULT   11 announce
    61: 0000000000000d20   226 FUNC    GLOBAL DEFAULT   11 announce

This is our baseline setup. Now, try some variations.

The first variation: try with libgcc_s.so statically-linked.

g++ -shared -static-libgcc -fPIC -O2 -g 28811.cc -o libannounce.so

And then look at the library produced:

%ldd libannounce.so 
	linux-vdso.so.1 =>  (0x00007fff075ff000)
	libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f27ceeb9000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f27cebbd000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f27ce806000)
	/lib64/ld-linux-x86-64.so.2 (0x0000003b72200000)
	libgcc_s.so.1 => /mnt/share/bld/gcc.git-trunk/gcc/libgcc_s.so.1 (0x00007f27ce7f0000)

It’s missing libgcc_s.so, as expected.

The second variation: try with libgcc_s.so and libstdc++.so statically-linked.

So:

g++ -shared -static-libgcc -static-libstdc++ -fPIC -O2 -g 28811.cc -o libannounce.so

Gives:

%ldd libannounce.so 
	linux-vdso.so.1 =>  (0x00007fff819ff000)
	libm.so.6 => /lib64/libm.so.6 (0x00007fdba493a000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fdba4582000)
	/lib64/ld-linux-x86-64.so.2 (0x0000003b72200000)

As expected.

Let’s try a third variation, to try for an all-C static link too. Ie:

g++ -shared -static-libgcc -static-libstdc++ -fPIC -O2 -g 28811.cc -Wl,-Bstatic -lc -lm -o libannounce.so

This doesn’t work:

g++ -shared -static-libgcc -static-libstdc++ -fPIC -O2 -g 28811.cc -Wl, -static -lc -lm -o libannounce.so
/usr/bin/ld: /mnt/share/bin/H-x86_64-gcc/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.8.0/crtbeginT.o: relocation R_X86_64_32 against `__TMC_END__' can not be used when making a shared object; recompile with -fPIC
/mnt/share/bin/H-x86_64-gcc/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.8.0/crtbeginT.o: could not read symbols: Bad value
collect2: error: ld returned 1 exit status


Known Bugs

PR28811
PR54482

Older bugs:

PR52689

Some of these bugs are many years old, and the build machinery has changed since they were first filed.

Basics on impacted sources:

src/c++98/compatibility.cc
src/c++98/compatibility-list-2.cc
src/c++11/compatibility-c++0x.cc
src/c++11/compatibility-atomic-c++0x.cc
src/c++11/compatibility-thread-c++0x.cc

and configure via PIC_CXXFLAGS

src/c++98/Makefile.am
src/c++11/Makefile.am

testing via

./lib/libstdc++.exp
     [list "incdir=$srcdir" "additional_flags=-w -shared 
testsuite/17_intro/static.cc:
     { dg-options "-static-libstdc++ -std=gnu++11" }
testsuite/17_intro/static_pic.cc:
     { dg-options "-shared -fPIC -static-libgcc -static-libstdc++"

It looks like the C++ compiler testsuite has a bunch of -fPIC tests, some -fPIC -fvisibility=hidden tests (mostly in the context of local statics), and some -static tests.

Questions

1) Are these expectations valid for C too?

First, what the GCC Manual says about the specific link options. Use “N1975 Dynamic Shared Objects: Survey and Issues” as background.

Then figure out the uses for:

  • -static
  • partial static shorcuts: -static-libgccgcc, -static-libstdc++
  • partial static via:
    -Wl,-static -lssl -lcrypto

2) how to check to see if an object has relocations

readelf -r foo.o

Ian Taylor has a nice description of relocations.

Template-fu, Zen Linking

Welcome, weary traveller.

Please, enter the dojo. Have some tea. Sit, and listen to me expound on the state of linking today.

There are a number of new techniques for linking in C+11. Some are not widely known. Some require long nights, on cold drafty mountain tops to fully master.

The new forms:

1. Extern Template

When you want white. Nothing. A truly private implementation, with only the API exported. Use extern template on class specializations to tell the compiler to not implicitly instantiate any symbols when the class is used by user code. For template functions as well.

Smartly done on forward-declarations, after the main class has been defined, making them post-declarations.  Pretty much anything goes: the syntax is the same as the syntax for explicit instantiations. Precisely because the two are a matched pair: with the power to prohibit instantiations comes the responsibility to explicitly instantiate them in some form. Wax on, wax off.

With C++11, extern template is portable. GNU C++ users have used it widely since 2002.

2. -fvisibility=hidden

And why it’s different from extern template. There seems to be a lot of confusion out there, about this. And let’s face it, the syntax is atrocious! Absolutely abominable.

GNU extensions, apply as attribute on namespaces.

3. constexpr

Mantis-style.

4. Namespace association. Tarsier-style.

But I will not bore you, weary traveller. Sit and enjoy your beverage. There will be plenty of time to talk about new techniques and methods later, after you have rested from your voyage.