将对象包含在C ++存档(.a)中的共享库中

时间:2017-09-19 18:16:23

标签: c++ ffmpeg linker shared-libraries static-libraries

我试图将一些目标文件包含到我正在构建的共享库中。请执行以下命令(为简洁起见,省略了[ETC]中的内容):

  

/ 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, - 整个存档lib / libavcodec.a -Wl, - no-whole-archive

所以基本上我只是创建一个共享库,其中大多数对象来自我自己的源代码(即CMakeFiles / Valkka.dir / src / * .o),但其中一些来自外部静态库,位于在“lib / libavcodec.a”。我收到以下错误:

  

/ usr / bin / ld:lib / libavcodec.a(h264_cabac.o):在创建共享对象时,不能使用符号'ff_h264_cabac_tables'的重定位R_X86_64_PC32;用-fPIC重新编译   / usr / bin / ld:最终链接失败:错误值   collect2:错误:ld返回1退出状态

但那是不真实的!我可以用

提取“libavcodec.a”
ar x libavcodec.a

然后检查

readelf --relocs h264_cabac.o | egrep '(GOT|PLT|JU?MP_SLOT)' 

确实给了它一些**:

  

00000000175d 003100000004 R_X86_64_PLT32 0000000000000000 __stack_chk_fail - 4   000000001926 003100000004 R_X86_64_PLT32 0000000000000000 __stack_chk_fail - 4

     

...

同样

objdump -r h264_cabac.o | grep -i "relocation"

事实上,“libavcodec.a”中的目标文件已被编译为PIC(位置无关代码)。

为什么链接器会相信!?

相关链接:

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?

1 个答案:

答案 0 :(得分:2)

<强> TL; DR

-Wl,-Bsymbolic添加到共享库的gcc链接选项中。

为什么?

您正在测试h264_cabac.o的PICness:

readelf --relocs h264_cabac.o | egrep '(GOT|PLT|JU?MP_SLOT)

并且如果得到的话,总结对象文件是用-fPIC编译的 命中。大概是你从the favourite answer得到了这个测试 到How can I tell, with something like objdump, if an object file has been built with -fPIC?

你有一些点击,我可以通过多种方式重现:

来自源代码

$ git clone https://github.com/FFmpeg/FFmpeg.git
$ cd FFmpeg
$ ./configure --enable-shared
$ make

然后:

$ cd libavcodec
$ readelf --relocs h264_cabac.o | egrep '(GOT|PLT|JU?MP_SLOT)'
00000000175d  003100000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000001926  003100000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
00000000259f  003100000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000002f0d  003100000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000003216  003200000004 R_X86_64_PLT32    0000000000000000 av_log - 4
000000003460  00330000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_chroma422_dc_s - 4
000000003afc  003100000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000003fb6  00360000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_i_mb_type_info - 4
000000004031  00370000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_mb_sizes - 4
00000000409a  003800000004 R_X86_64_PLT32    0000000000000000 ff_init_cabac_decoder - 4
000000004248  00390000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_b_mb_type_info - 4
000000004299  003a00000004 R_X86_64_PLT32    0000000000000000 ff_h264_pred_direct_mo - 4
000000004a31  003b00000004 R_X86_64_PLT32    0000000000000000 ff_h264_check_intra4x4 - 4
000000004bd5  003200000004 R_X86_64_PLT32    0000000000000000 av_log - 4
000000004f85  003c0000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_p_mb_type_info - 4
0000000050fd  003d0000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_b_sub_mb_type_ - 4
000000005233  003a00000004 R_X86_64_PLT32    0000000000000000 ff_h264_pred_direct_mo - 4
00000000544a  003200000004 R_X86_64_PLT32    0000000000000000 av_log - 4
000000005bef  003a00000004 R_X86_64_PLT32    0000000000000000 ff_h264_pred_direct_mo - 4
000000006db5  003e00000004 R_X86_64_PLT32    0000000000000000 ff_h264_check_intra_pr - 4
000000006de9  003f0000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_p_sub_mb_type_ - 4
000000007171  003200000004 R_X86_64_PLT32    0000000000000000 av_log - 4
000000008b1b  003e00000004 R_X86_64_PLT32    0000000000000000 ff_h264_check_intra_pr - 4
00000000ad41  004000000009 R_X86_64_GOTPCREL 0000000000000000 ff_h264_chroma_dc_scan - 4
00000000ad84  004000000009 R_X86_64_GOTPCREL 0000000000000000 ff_h264_chroma_dc_scan - 4
00000000b758  003100000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4

来自Ubuntu 16.04开发包

$ sudo apt-get install libavcodec-dev
$ dpkg -S libavcodec.a
libavcodec-dev:amd64: /usr/lib/x86_64-linux-gnu/libavcodec.a
$ mkdir ~/deleteme
$ cd ~/deleteme
$ ar x /usr/lib/x86_64-linux-gnu/libavcodec.a h264_cabac.o
$ readelf --relocs h264_cabac.o | egrep '(GOT|PLT|JU?MP_SLOT)'
0000000000c7  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
0000000002fa  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
00000000179d  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000001966  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000001b09  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000001d4a  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000001ee5  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
00000000265f  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000002fcd  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
0000000032f6  002f00000004 R_X86_64_PLT32    0000000000000000 av_log - 4
000000003305  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000003bdc  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000003cb5  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4
000000004121  00320000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_mb_sizes - 4
000000004187  003300000004 R_X86_64_PLT32    0000000000000000 ff_init_cabac_decoder - 4
000000004381  003400000004 R_X86_64_PLT32    0000000000000000 ff_h264_pred_direct_mo - 4
000000004afe  003500000004 R_X86_64_PLT32    0000000000000000 ff_h264_check_intra4x4 - 4
000000005556  003400000004 R_X86_64_PLT32    0000000000000000 ff_h264_pred_direct_mo - 4
00000000576a  002f00000004 R_X86_64_PLT32    0000000000000000 av_log - 4
000000005acf  003400000004 R_X86_64_PLT32    0000000000000000 ff_h264_pred_direct_mo - 4
000000006e31  002f00000004 R_X86_64_PLT32    0000000000000000 av_log - 4
000000006e58  003600000004 R_X86_64_PLT32    0000000000000000 ff_h264_check_intra_pr - 4
000000009c20  003600000004 R_X86_64_PLT32    0000000000000000 ff_h264_check_intra_pr - 4
00000000b425  002f00000004 R_X86_64_PLT32    0000000000000000 av_log - 4
00000000b5ab  002e00000004 R_X86_64_PLT32    0000000000000000 __stack_chk_fail - 4

结果并不相同,我第一次获得26次重置,第二次获得25次重定位。但无论是哪种方式,都有大量的PIC安全重新定位 我很高兴相信h264_cabac.o的两个编辑都有-fPIC,无论他们有什么其他选择。

我将陈述显而易见的:符号ff_h264_cabac_tables,关于你的联系 抱怨:

relocation R_X86_64_PC32 against symbol 'ff_h264_cabac_tables' can not be used when making a shared object

不在其中任何一个列表中。这意味着这个目标文件 - 来自两个来源 - 包含两个 PIC安全和PIC不安全的重定位。 GCC怎么可能 错误,直到现在还没有人注意到?如果它确实如此 我刚刚运行FFmpeg的共享库构建并成功链接libavcodec.so吗?

让我们看一下PIC- 不安全的重定位:

$ readelf --relocs h264_cabac.o | egrep -v '(GOT|PLT|JU?MP_SLOT)'
000000000017  002c00000002 R_X86_64_PC32     0000000000000000 ff_h264_cabac_tables - 4
...
...

嗯,我已经消除了〜160行,但它们都描述了PC相对类型R_X86_64_PC32 重定位和提到的唯一符号,折扣部分名称和本地标签, 是我们的朋友ff_h264_cabac_tables,符号表上写着:

$ readelf -s h264_cabac.o | grep ff_h264_cabac_tables
    44: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND ff_h264_cabac_tables

它是一个全局变量,未在此目标文件中定义。

海湾合作委员会的-fPIC并未破裂。但是,知道编译了一个目标文件 -fPIC无法绝对保证其不包含与PC相关的类型 引用未定义全局符号的R_X86_64_PC32重定位。 relocation R_X86_64_PC32 against symbol 'ff_h264_cabac_tables'是 这样的搬迁。 R_X86_64_PC32类型重定位采用32位PC相对寻址模式, 这是有效的,但在设置64位链接时有一个关键限制。 链接器不能保证引用的符号不会被动态解析 在此寻址模式中无法代表的地址。它不会那样,所以 它说:

relocation R_X86_64_PC32 against symbol 'ff_h264_cabac_tables' can not be used when making a shared object

及其建议:

recompile with -fPIC

基于假设,罪魁祸首对象文件未使用-fPIC编译。 这可能是,但必然是正确的假设,并且正确 你的罪魁祸首libavcodec.a(h264_cabac.o)

使用-fPIC进行编译将保证您提供PIC安全重定位,前提是编译器 允许进行所有汇编和代码生成。但它是不允许的 用你的h264_cabac.o标本或我的任何一个标本。所有这些标本 是从FFmpeg源代码树中的FFmpeg/libavcodec/x86/h264_cabac.c编译的。 查看该文件,您将看到它定义了引用extern的函数 全局变量ff_h264_cabac_tables并以内联手工制作实现 部件。可以告诉GCC编译这些函数-fPIC,但它没有得到 机会。这些职能的地位独立性是责任 汇编代码的作者。

我们可以证明GCC能够编译只有PIC安全的h264_cabac.o 重新安置,如果允许的话。这将附带证明你的联系 失败源于我们的文件标本的手工装配,也将显示 你解决了连锁失败的问题。 FFmpeg的./configure脚本可以选择:

--disable-asm            disable all assembly optimizations

其中包括导致h264_cabac.o被编译的效果 来自纯C源文件FFmpeg/libavcodec/h264_cabac.c而不是 内联汇编源FFmpeg/libavcodec/x86/h264_cabac.c。所以,让我们尝试一下:

$ cd FFmpeg
$ make clean
$ ./configure --enable-shared --disable-asm
$ make
$ cd libavcodec
$ readelf --relocs h264_cabac.o | grep ff_h264_cabac_tables
00000000000a  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
0000000000ca  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
000000001eb5  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
0000000021c6  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
0000000026fe  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
000000002a17  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
000000002f13  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
00000000324c  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
000000003509  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
00000000362a  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
0000000037d7  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4
00000000592b  00300000002a R_X86_64_REX_GOTP 0000000000000000 ff_h264_cabac_tables - 4

现在,所有引用ff_h264_cabac_tables的重定位都是PIC安全的。 我们也可以证明这个h264_cabac.o可以在共享库中链接。我们知道 ff_h264_cabac_tables中未定义h264_cabac.o,因此我们还需要链接。{1}} 定义的目标文件。它恰好是./cabac.o

$ gcc -shared -o libfoo.so h264_cabac.o cabac.o

瞧:

$ file libfoo.so
libfoo.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=ed63107b715b357853da94d4a031c0b06c30c5f2, not stripped

但是,如果你必须链接自己的话,你可能仍会感到有点委屈 这个未经优化的h264_cabac.o共享库,有点失望 装配编码的缺陷迫使你。这些感觉为时尚早。

请记住,我已经使用普通香草./configure --enable-shared成功构建了FFmpeg。 我说链接器在你失败的链接中的反对意见是R_X86_64_PC32 如果ff_h264_cabac_tables,重定位引用ff_h264_cabac_tables 可能在运行时不可行 是动态解决的。这不是对R_X86_64_PC32类型的反对意见 搬迁这样的。这是一个预防性的反对意见,基于对ff_h264_cabac_tables的无知 最终会得到解决。

但我们知道ff_h264_cabac_tables实际上是在cabac.o中定义的 我们将其与h264_cabac.o包含相同的链接,就像它们都包括在内一样 在libavcodec.so的联系中。我们可以告诉链接器任何全局 链接中的引用将被静态地解析为中的定义 共享库,如果有的话,通过传递参数:

-Bsymbolic

这将取消对任何R_X86_64_PC32重新安置的预防性异议。 它知道它能够在链接时决定是否R_X86_64_PC32 重新安置ff_h264_cabac_tables是可行的。如果没有,它会给 一个不同的错误:relocation truncated to fit:..。否则它会成功 没有评论。

不可避免的是,{FF}在库存FFmpeg构建中成功链接libavcodec.so。 再次从顶部开始:

$ cd FFmpeg
$ make clean
$ ./configure --enable-shared
$ make

然后强制重新链接libavcodec.so

$ rm libavcodec/h264_cabac.o
$ $ make libavcodec/libavcodec.so V=1
gcc -I. -I./ -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE \
-D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -DPIC -DZLIB_CONST -DHAVE_AV_CONFIG_H \
-std=c11 -fomit-frame-pointer -fPIC -pthread  -g -Wdeclaration-after-statement \
-Wall -Wdisabled-optimization -Wpointer-arith -Wredundant-decls -Wwrite-strings \
-Wtype-limits -Wundef -Wmissing-prototypes -Wno-pointer-to-int-cast -Wstrict-prototypes \
-Wempty-body -Wno-parentheses -Wno-switch -Wno-format-zero-length -Wno-pointer-sign \
-O3 -fno-math-errno -fno-signed-zeros -fno-tree-vectorize -Werror=format-security \
-Werror=implicit-function-declaration -Werror=missing-prototypes -Werror=return-type \
-Werror=vla -Wformat -fdiagnostics-color=auto -Wno-maybe-uninitialized  \
-MMD -MF libavcodec/h264_cabac.d -MT libavcodec/h264_cabac.o -c \
-o libavcodec/h264_cabac.o libavcodec/h264_cabac.c
sed 's/MAJOR/57/' libavcodec/libavcodec.v | cat > libavcodec/libavcodec.ver
gcc -shared -Wl,-soname,libavcodec.so.57 -Wl,-Bsymbolic ... etc. etc. ...
                                         ^^^^^^^^^^^^^^   

因此没有汇编编码缺陷。要链接手动优化的h264_cabac.o 在共享库中,您只需要将-Wl,-Bsymbolic添加到gcc链接选项中。它&#39; S 优化的要求。

让我们最低限度地证明:

$ cd libavcodec/
$ gcc -shared -o libfoo.so h264_cabac.o cabac.o
/usr/bin/ld: 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

你又失败了。和

$ gcc -shared -Wl,-Bsymbolic -o libfoo.so h264_cabac.o cabac.o
$ file libfoo.so 
libfoo.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=7dc86aeae353c4d92cdb5fa35d169bf019b47eb2, not stripped

成功。