使用objcopy --redefine-syms时绑定失败

14

前提条件

第三方提供了一个 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正确重命名了符号。
  • 符号名称的大小没有改变。
  • 但是加载程序在加载时忽略它。

这里的故事是什么?

1个回答

10
为什么?这里的故事是什么? objcopy -redefine-syms 仅会重新定义 .symtab.strtab 符号表中的调试符号,而不是用于动态加载的 .dynsym.dynstr 符号表中的符号。
如果您使用 nm -Dreadelf -s 检查使用 objcopy 创建的库,则会看到动态加载程序使用的名称仍然是 _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 之后失败,并且使用原始库的符号。
% 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.

你有提到的objcopy限制的引用吗? - BrianTheLion
大多数都很古老,比如objcopy --redefine-sym on .dynsym section,但现在你已经知道发生了什么,搜索“在动态符号表中重命名ELF符号”(或类似内容)可能会给出我找到的所有符号。 - Ted Lyngmo
看起来这可能是正确的答案,但我正在寻找官方参考来奖励悬赏。 - BrianTheLion
我怀疑这会很难。我查看了源代码objcopy.c,只发现其中提到了SHT_SYMTAB,而没有像readelf.c这样的工具中可以找到的SHT_DYNSYM。也许这表明无法使用objcopy完成此操作。 - Ted Lyngmo
@BrianTheLion,我在binutils邮件列表上发布了一个查询链接。也许有人会在那里给出官方声明。我还克隆了最新版本的binutils/objcopy,但没有发现任何暗示可以完成这个任务的内容。 - Ted Lyngmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接