前提条件
第三方提供了使用共享对象fooapp
的C++可执行文件libfoo.so
。该库还带有 header foo.hpp
,因此开发人员可以构建其他应用程序:
/* foo.hpp */
namespace foo {
void bar(int a, int b);
// More code below here <--- NOTE!!!
}
成功例子
这是一个基于
LD_PRELOAD
的标准函数插入工作流。首先,我编写自己的库版本
myfoo.cpp
,它完全反射(reflect)了 foo.hpp
的部分:/* myfoo.hpp */
# include <ofstream>
namespace foo {
void bar(int a, int b) {
std::cout << a << "," << b << std::endl;
}
// NOTHING below here <-- NOTE!!!
}
然后,将我的库编译为
libmyfoo.so
并看到以下内容:$ nm libfoo.so -C | fgrep bar
0000000000021fc0 T foo::bar(int, int)
$ nm libmyfoo.so -C | fgrep bar
0000000000010c30 T foo::bar(int, int)
$ LD_DEBUG=bindings LD_DEBUG_OUTPUT=ld_debug.log LD_PRELOAD=./libmyfoo.so fooapp
成功!
ld_debug.log
按预期显示绑定(bind),并且bar(...)
生成输出到控制台。失败例子
对于失败示例,我将要(1)更改
myfoo.hpp
中的一个字符,然后(2)然后使用objcopy
将其替换为二进制文件中的字符:/* myfoo.hpp */
# include <ofstream>
namespace foq { // <-- NAME CHANGE!
void bar(int a, int b) {
std::cout << a << "," << b << std::endl;
}
// NOTHING below here <-- NOTE!!!
}
当我将库编译成
libmyfoq.so
时,会看到以下内容:$ nm libfoo.so -C | fgrep bar
0000000000021fc0 T foo::bar(int, int) # <-- Sames as before
$ nm libmyfoq.so -C | fgrep bar
0000000000010c30 T foq::bar(int, int) # <-- New name as expected
$ objcopy --redefine-syms=sym.map libmyfoq.so libmyfoo.so # <-- NEW STEP!
$ nm libmyfoo.so -C | fgrep bar
0000000000010c30 T foo::bar(int, int) # <-- SUCCESSful name update
$ LD_DEBUG=bindings LD_DEBUG_OUTPUT=ld_debug.log LD_PRELOAD=./libmyfoo.so fooapp
失败!
ld_debug.log
不显示fooapp
符号与libmyfoo.so
的绑定(bind)。(PS-如果您好奇,
sym.map
包含foq::bar
的错位名称和foo::bar
的错位名称之间的映射。如果从-C
命令中删除nm
,则可以看到它们。有关其他详细信息,请参阅man objcopy
。)为什么?
总之:
objcopy
正确重命名该符号。 这是什么故事?
最佳答案
objcopy -redefine-syms
将仅重新定义.symtab
和.strtab
符号表中的调试符号,而不是重新定义用于动态加载的.dynsym
和.dynstr
符号表中的调试符号。
如果检查使用objcopy
或nm -D
或readelf -s
创建的库,您会看到动态加载程序使用的名称仍然是_ZN3foq3barEii
或nm -DC
,foq::bar(int, int)
。
所以,这就是正在发生的事情。
我能找到的最官方的引用资料表明objcopy
无法用于更新动态符号,这是2010年以来未分配的bugzilla条目:
Bug 11386 - objcopy should be able to update dynamic symbols visibility
我对官方binutils
邮件列表的查询:
Redefining dynamic symbols
附带说明一下,我认为没有可用的工具可以正确地重新定义动态符号,但是我可能是错的。
2015年的cjacker/elfhash项目似乎正在尝试。我试过了,它似乎实际上是重命名了动态符号名-但是之后加载.so
失败了,而是使用了原始的libs符号。
% elfhash -f _ZN3foq3barEii -t _ZN3foo3barEii libmyfoq.so
% mv libmyfoq.so libmyfoo.so
% LD_DEBUG=bindings LD_DEBUG_OUTPUT=ld_debug.log LD_PRELOAD=./libmyfoo.so ./fooapp
ERROR: ld.so: object './libmyfoo.so' from LD_PRELOAD cannot be preloaded (cannot change memory protections): ignored.
关于c++ - objcopy --redefine-syms的绑定(bind)失败,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54332797/