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.

About sunglint
----

Comments are closed.