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.