前提条件
第三方提供了fooapp
使用共享对象的 C++ 可执行文件libfoo.so
。该库还带有一个标头foo.hpp
,因此开发人员可以构建其他应用程序:
/* foo.hpp */
namespace foo {
void bar(int a, int b);
// More code below here <--- NOTE!!!
}
成功范例
这是一个LD_PRELOAD
基于标准的函数插入工作流。
首先,我编写了自己的库版本,myfoo.cpp
它完全反映了以下内容的一部分 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
按预期显示绑定,并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
。
(PS——如果你好奇sym.map
的话,包含foq::bar
(PS——它和foo::bar
.-C
nm
man objcopy
为什么?
总之:
objcopy
正在正确重命名符号。- 符号名称没有改变大小。
- 但加载器在加载时忽略了它。
这里有什么故事?