I am trying to include some object files into a shared library I am building. Take the following command (things in [ETC] have been omitted for brevity):
/usr/bin/c++ -fPIC -std=c++14 -pthread -Iinclude/ext/liveMedia -Iinclude/ext/groupsock [ETC] -g -shared -Wl,-soname,libValkka.so -o lib/libValkka.so CMakeFiles/Valkka.dir/src/avthread.cpp.o CMakeFiles/Valkka.dir/src/opengl.cpp.o [ETC] CMakeFiles/Valkka.dir/src/decoders.cpp.o -lX11 -lGLEW -lGLU -lGL -Wl,--whole-archive lib/libavcodec.a -Wl,--no-whole-archive
So basically I am just creating a shared library where most of the objects come from my own source code (i.e. CMakeFiles/Valkka.dir/src/*.o), but some of them come from an external static library, located at "lib/libavcodec.a". I get the following error:
/usr/bin/ld: lib/libavcodec.a(h264_cabac.o): relocation R_X86_64_PC32 against symbol 'ff_h264_cabac_tables' can not be used when making a shared object; recompile with -fPIC /usr/bin/ld: final link failed: Bad value collect2: error: ld returned 1 exit status
But that is so untrue! I can extract "libavcodec.a" with
ar x libavcodec.a
And after that check that
readelf --relocs h264_cabac.o | egrep '(GOT|PLT|JU?MP_SLOT)'
does give some **it:
00000000175d 003100000004 R_X86_64_PLT32 0000000000000000 __stack_chk_fail - 4 000000001926 003100000004 R_X86_64_PLT32 0000000000000000 __stack_chk_fail - 4
...
As does
objdump -r h264_cabac.o | grep -i "relocation"
So, indeed, the object files in "libavcodec.a" have been compiled to get PIC (position independent code).
Why does the linker believe otherwise!?
Related links:
How to include all objects of an archive in a shared object?
Linking archives (.a) into shared object (.so)
Is there a way to determine that a .a or .so library has been compiled as position indepenent code?
How can I tell, with something like objdump, if an object file has been built with -fPIC?
TL;DR
Add
-Wl,-Bsymbolic
to the gcc linkage options for your shared library.Why?
You're testing the PICness of
h264_cabac.o
with:and concluding the the object file was compiled with
-fPIC
if you get any hits. Presumably you got this test from the favourite answer to How can I tell, with something like objdump, if an object file has been built with -fPIC?You got some hits, and I can reproduce that more than one way:
From the source code
Then:
From the Ubuntu 16.04 dev package
The results aren't identical, and I get 26 relocations the first way, 25 the second way. But there are plenty of PIC-safe relocations either way and I'm happy to believe both compilations of
h264_cabac.o
had-fPIC
, whatever other options they had.I'll state the obvious: The symbol
ff_h264_cabac_tables
, about which your linkage complains:isn't in either of those lists. That means this object file - from both provenances - contains both PIC-safe and PIC-unsafe relocations. How could GCC get that wrong, without anybody noticing till now? And if it did, how did I just run the shared library build of FFmpeg and link
libavcodec.so
successfully?Let's have a look at the PIC-unsafe relocations:
Well I'll elide ~160 lines of that, but they all describe PC-relative type
R_X86_64_PC32
relocations and the only symbol mentioned, discounting section names and local labels, is our friendff_h264_cabac_tables
, about which the symbol table says:It's a global variable, not defined in this object file.
GCC's
-fPIC
isn't broken. However, knowing that an object file was compiled with-fPIC
can't absolutely guarantee that it contains no PC-relative typeR_X86_64_PC32
relocations that reference undefined global symbols.relocation R_X86_64_PC32 against symbol 'ff_h264_cabac_tables'
is such a relocation. TheR_X86_64_PC32
type relocation employs a 32-bit PC-relative addressing mode, which is efficient but has a critical limitation in the setting of a 64-bit linkage. The linker can't guarantee the referenced symbol will not be dynamically resolved to an address that's unrepresentable in this addressing mode. It won't have that, so it says:and its advice:
is based on the hypothesis that the culprit object file was not compiled with
-fPIC
. Which is probably, but necessarily, the right hypothesis and is not right about your culpritlibavcodec.a(h264_cabac.o)
Compiling with
-fPIC
will guarantee you PIC-safe relocations provided that the compiler is allowed to do all the assembly and code-generation. But it wasn't allowed to with your specimen ofh264_cabac.o
or either of my specimens. All those specimens were compiled fromFFmpeg/libavcodec/x86/h264_cabac.c
in the FFmpeg source tree. Look at that file and you'll see that it defines functions that refer to theextern
global variableff_h264_cabac_tables
and are implemented in inline, hand-crafted assembly. GCC can be told to compile these functions-fPIC
, but it doesn't get a chance. The position-independence of these functions is the responsibility of the author of the assembly code.We can show that GCC is able to compile a
h264_cabac.o
that has only PIC-safe relocations, if it's allowed to. That will collaterally prove that your linkage failure stems from the hand-assembly of our specimens of the file and will also show you one fix for the linkage failure. FFmpeg's./configure
script has the option:which has the effect, among others, of causing
h264_cabac.o
to be compiled from the pure C source fileFFmpeg/libavcodec/h264_cabac.c
instead of the the inline assembly sourceFFmpeg/libavcodec/x86/h264_cabac.c
. So let's try that:Now, all the relocations referencing
ff_h264_cabac_tables
are PIC-safe. We might as well prove that thish264_cabac.o
can be linked in a shared library. We know thatff_h264_cabac_tables
is undefined inh264_cabac.o
, so we'll also need to link the object file in which it is defined. It happens to be./cabac.o
.Voila:
You might still feel a little aggrieved, however, if you have to link your own shared library with this unoptimized
h264_cabac.o
and a little disappointed by the assembly coding flaw that obliges you to. These feelings would be premature.Remember, I've already built FFmpeg successfully with plain vanilla
./configure --enable-shared
. I said that linker's objection in your failing linkage is that theR_X86_64_PC32
relocation referencingff_h264_cabac_tables
might not be feasible at runtime, ifff_h264_cabac_tables
is dynamically resolved. It is not an objection to the typeR_X86_64_PC32
relocation as such. It is a precautionary objection, based on ignorance of howff_h264_cabac_tables
will ultimately be resolved.But we know that
ff_h264_cabac_tables
is in fact defined incabac.o
and that we'll include it the same linkage withh264_cabac.o
, just as they're both included in the linkage oflibavcodec.so
. And we can tell the linker that any global references in the linkage are to be statically resolved to definitions in the shared library being linked, if at all, by passing it the argument:This will remove its precautionary objection to any
R_X86_64_PC32
relocation. It knows it will be able to decide at linktime whether theR_X86_64_PC32
relocation againstff_h264_cabac_tables
is feasible. If not, it will give a different error:relocation truncated to fit:..
. Otherwise it will succeed without comment.Inevitably, that's how
libavcodec.so
is successfully linked in the stock FFmpeg build. Once more from the top:Then force a relink of
libavcodec.so
:So there is no assembly coding flaw. To link the hand-optimized
h264_cabac.o
in a shared library you just need to add-Wl,-Bsymbolic
to the gcc linkage options. It's a requirement of the optimization.Let's prove that minimally:
There's your failure again. And:
success.