前提条件
第三方提供了一个 C++ 可执行文件 fooapp
,它使用了一个共享对象 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
的名称和foo::bar
的名称之间的映射。如果您从nm
命令中删除-C
,则可以看到这些内容。有关详细信息,请参见man objcopy
。)为什么?
简而言之:
objcopy
正确重命名了符号。- 符号名称的大小没有改变。
- 但是加载程序在加载时忽略它。
这里的故事是什么?
SHT_SYMTAB
,而没有像readelf.c这样的工具中可以找到的SHT_DYNSYM
。也许这表明无法使用objcopy
完成此操作。 - Ted Lyngmo