前提条件

第三方提供了使用共享对象fooapp的C++可执行文件libfoo.so。该库还带有 header foo.hpp,因此开发人员可以构建其他应用程序:

/* foo.hpp */
namespace foo {
  void bar(int a, int b);
  // More code below here <--- NOTE!!!
}

成功例子

这是一个基于LD_PRELOAD的标准函数插入工作流。

首先,我编写自己的库版本myfoo.cpp,它完全反射(reflect)了 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按预期显示绑定(bind),并且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的绑定(bind)。

(PS-如果您好奇,sym.map包含foq::bar的错位名称和foo::bar的错位名称之间的映射。如果从-C命令中删除nm,则可以看到它们。有关其他详细信息,请参阅man objcopy。)

为什么?

总之:
  • objcopy正确重命名该符号。
  • 符号名称未更改大小。
  • 则是加载程序在加载时忽略了它。

  • 这是什么故事?

    最佳答案


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

    如果检查使用objcopynm -Dreadelf -s创建的库,您会看到动态加载程序使用的名称仍然是_ZN3foq3barEiinm -DCfoq::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失败了,而是使用了原始的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.
    

    关于c++ - objcopy --redefine-syms的绑定(bind)失败,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54332797/

    10-11 18:27