14

前提条件

第三方提供了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.-Cnmman objcopy

为什么?

总之:

  • objcopy正在正确重命名符号。
  • 符号名称没有改变大小。
  • 加载器在加载时忽略了它。

这里有什么故事?

4

1 回答 1

10

为什么?这里有什么故事?

objcopy -redefine-syms只会重新定义.symtab.strtab符号表中的调试符号,而不是.dynsym用于.dynstr动态加载的符号表中的符号。

如果您检查使用创建的库objcopynm -D或者readelf -s您会看到动态加载器使用的名称仍然是_ZN3foq3barEii,或使用nm -DCfoq::bar(int, int)

所以,这就是正在发生的事情。

我能找到的最官方的参考资料表明objcopy不能用于更新动态符号是 2010 年的这个未分配的 bugzilla 条目:

错误 11386 - objcopy 应该能够更新动态符号的可见性

还有我自己在官方binutils邮件列表上的查询:

重新定义动态符号


作为旁注,我认为没有可用于重新定义动态符号的工具(正确),但我可能是错的。

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.
于 2019-01-29T14:16:17.313 回答