objcopy 绑定失败 --redefine-syms
Binding failure with objcopy --redefine-syms
先决条件
third-party 提供了一个使用共享 object libfoo.so
的 C++ 可执行文件 fooapp
。该库还附带一个 header 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
显示 NO 将 fooapp
符号绑定到 libmyfoo.so
。
(PS -- 如果您好奇,sym.map
包含 foq::bar
的错位名称和 foo::bar
的错位名称之间的映射。您可以如果您从 nm
命令中删除 -C
,请查看这些内容。有关更多详细信息,请参阅 man objcopy
。)
为什么?
总结:
objcopy
正在正确重命名符号。
- 符号名称没有改变大小。
- 但是加载程序在加载时忽略了它。
这里有什么故事?
WHY? What's the story here?
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
邮件列表中的查询:
附带说明一下,我认为没有可用于(正确)重新定义动态符号的工具,但我可能是错的。
2015年的cjacker/elfhash项目似乎在做尝试。我试过了,它似乎实际上重命名了动态符号名称 - 但之后加载 .so
失败,而是使用原始库符号。
% 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.
先决条件
third-party 提供了一个使用共享 object libfoo.so
的 C++ 可执行文件 fooapp
。该库还附带一个 header 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
显示 NO 将 fooapp
符号绑定到 libmyfoo.so
。
(PS -- 如果您好奇,sym.map
包含 foq::bar
的错位名称和 foo::bar
的错位名称之间的映射。您可以如果您从 nm
命令中删除 -C
,请查看这些内容。有关更多详细信息,请参阅 man objcopy
。)
为什么?
总结:
objcopy
正在正确重命名符号。- 符号名称没有改变大小。
- 但是加载程序在加载时忽略了它。
这里有什么故事?
WHY? What's the story here?
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
邮件列表中的查询:
附带说明一下,我认为没有可用于(正确)重新定义动态符号的工具,但我可能是错的。
2015年的cjacker/elfhash项目似乎在做尝试。我试过了,它似乎实际上重命名了动态符号名称 - 但之后加载 .so
失败,而是使用原始库符号。
% 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.