在编译C / C ++程序时,有没有办法在Linux中链接到库导出而没有嵌入版本信息?
我正在构建一个链接到Firefox中的libxul.so的共享对象(它是一个Firefox二进制扩展)。我想构建我的共享对象并链接到libxul.so,以便在运行时,加载器不关心libxul.so的版本。
现在,我的输出.so文件具有以下依赖项:
readelf -V myext.so
0x0080: Version: 1 File: libxul.so Cnt: 1
0x0090: Name: xul24.0 Flags: none Version: 9
(请注意,它取决于版本'xul24.0')
导出的功能在Firefox版本之间不会更改。所以,我想删除这个版本说明。
尝试加载到Firefox 26时,LD_DEBUG = file提供以下错误:
/usr/lib/firefox/libxul.so: error: version lookup error:
version `xul24.0' not found (required by /.../myext.so) (fatal)
对于Firefox 26版本的libxul.so,版本为'xul26'
。
那么,我怎样才能阻止ld将版本信息嵌入到我的库中?
答案 0 :(得分:1)
我能够根据需要构建库,没有版本信息。
我最终做的是创建一个带有相应导出符号的虚拟库 - 没有版本(或soname)信息 - 并在编译和链接我的库时链接到该虚拟库。在运行时,加载程序根据版本控制问题加载真实库(libxul.so)而不会失败,因为我的库不包含真实库的版本信息。为了找出我需要的导出符号,我首先链接到真正的libxul.so,然后使用readelf --dyn-syms
来确定实际需要的符号。
当ARCH为32或64(对于32位或64位编译)时:
gcc -o $ARCH/libxul.so -fPIC -shared -DM$ARCH -m$ARCH xulstubs.c
xulstubs.c:
/*
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_Realloc@xul26 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_UTF16ToCString@xul26 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_CStringCloneData@xul26 (2)
13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_GetMemoryManager@xul26 (2)
...
*/
void NS_Realloc() {}
void NS_UTF16ToCString() {}
void NS_CStringCloneData() {}
void NS_GetMemoryManager() {}
/* ... etc. ... */
现在,我库中导入的符号没有版本附件:
23: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_Realloc
29: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_UTF16ToCString
33: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_CStringCloneData
37: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_GetMemoryManager
并按需运行。
我仍然想要一个更“优雅”的解决方案,一个不需要创建虚拟库的解决方案。但是,如果这是使用标准工具链的唯一解决方法,我可以忍受它。
答案 1 :(得分:0)
如果考虑使用最广泛的GNU工具链,您可以加载任何没有“lib”前缀和版本后缀的共享库,但只能直接使用代码中的dlopen()。在二进制文件启动或其他库加载期间自动加载将不起作用,因为链接器将找到库文件,从库头读取其“soname”和“rpath”属性并应用它们以在构建的目标加载期间查找库。 Soname对于链接器比库文件名更重要。更重要的是,在某些系统中,ldconfig可以重命名公共搜索路径中的库以匹配内部记录的soname(我在Linux上看到过这种情况)。
一个小例子。考虑一个文件t.c,其中main()打印一个来自库函数的值和一个库源文件tt.c,其中函数返回一个常量。制作它们:
$ make clean; make rm -f t t.o libtt.so tt.o gcc -o libtt.so -shared -fPIC tt.c gcc -o t t.c -L. -ltt
使用这些命令,。/ t将失败,因为libtt.so不在公共搜索路径中,因此需要LD_LIBRARY_PATH来启动它。但我们可以解决这个问题:
$ gcc -o libtt.so -shared -fPIC tt.c $ gcc -o t t.c -L. -ltt -Wl,-rpath=`pwd` $ ldd ./t ./t: libtt.so => /var2/homes/netch/prog/tests/soname/libtt.so (0x80081a000) libc.so.7 => /lib/libc.so.7 (0x800a1b000)
但是,如果我在构建期间请求另一个库名称,搜索将失败:
$ gcc -o libtt.so -shared -fPIC tt.c -Wl,-soname=libtt2.so $ gcc -o t t.c -L. -ltt -Wl,-rpath=`pwd` $ ldd ./t ./t: libtt2.so => not found (0) libc.so.7 => /lib/libc.so.7 (0x80081a000)
请注意,如果没有看到libtt.so,链接器将会失败,因为它不知道在哪里可以找到未解析的链接。
所有这些都适用于其他风格的名称,尽管链接器显示了一些奇怪的东西:
$ gcc -o lybtt.so -shared -fPIC tt.c $ gcc -o t t.c lybtt.so -Wl,-rpath=`pwd` $ ./t 233 $ ldd ./t ./t: lybtt.so (0x80081a000) libc.so.7 => /lib/libc.so.7 (0x800a1b000)
如果您是库构建器,则可以在没有版本的情况下命名它,所有工具都会识别它。但这通常被认为是不好的风格,并在建议中被禁止。公共搜索路径中的所有库都声明其名称中包含版本,严格建议用于任何非家用软件包装。
对于你的问题,如果你真的需要徒步射击,你可以复制libxul,更改其中的soname并再次进入正确的搜索路径。但我怀疑这就是你想要的。