目标:使用可执行文件中的功能(不导出符号)的共享库。

表示:gcc -Wl,--defsym,function=0x432238
手册页指出:

"--defsym symbol=expression" Create a global symbol in the output
file, containing the absolute address given by expression.

令我沮丧的是,dlopen()在共享库的基地址(这是64位代码)中添加了0x7ffff676f000到导出的“绝对符号地址”中:
        executable        shared library
        ---------- linker --------------
symbol: 0x432238   =====> 0x7ffff6ba1238

objdump在库中显示正确的符号地址(0x432238),但是一旦加载了dlopen(),该符号的地址即为0x7ffff6ba1238

如果加载后,我手动将库符号修补到正确的地址,则一切正常(否则,库SEGFAULTs)。
  • 为什么修改“绝对地址”?
  • 如何避免呢?

  • 更新:

    我对以下答复的技术意义提出质疑,甚至对其“更新”表示质疑:

    使用--defsym在PIC库/可执行文件中定义重定位符号是没有意义的(除了污染没有任何可用功能的二进制文件外,它没有任何目的)。

    因此,--defsym在PIC共享库或PIC可执行文件中的唯一相关用途应该是定义(未重定位的)“绝对地址”。

    顺便说一句,这是--defsym的官方目的,如果您不愿意阅读手册页:

    “在输出文件中创建一个全局符号,其中包含表达式给定的absolute address。”

    充其量,这是一个Linux链接程序缺陷,修复起来很简单。对于那些迫不及待地拒绝人们意识到(并修复)他们的错误的人,解决方案是在有缺陷的链接器加载了二进制镜像之后修补重定位表。

    然后,--defsym在PIC库/可执行文件中变得有用,在我看来这是一个可喜的进步。

    最佳答案

    您似乎从根本上误解了--defsym的功能。

    --defsym=symbol=expression
       Create a global symbol in the *output* file, ...
    

    也就是说,您正在创建的库中创建新符号。这样,该符号(自然地)随库一起重定位。

    我猜你想改成这样的东西:
    // code in library
    int fn()
    {
        // exe_fn not exported from the executable, but we know where it is.
        int (*exe_fn)(void) = (int (*)(void)) 0x432238;
        return (*exe_fn)();
    }
    

    如果您不想将0x432238硬编码到库中,而是在构建时在命令行中传递值,则只需使用-DEXE_FN=0x432238即可实现。

    更新:



    您选择的方法无法实现这个目标。您将不得不使用其他方式。



    不是。当您要求链接器在绝对地址function处定义0x432238时,它会精确地将
    定义为。您可以在objdumpnmreadelf -s输出中看到它。

    但是由于符号是在共享库中定义的,因此对该符号的所有引用都将重定位,即通过共享库的加载地址进行调整(由动态加载器完成)。对于动态加载程序而言,否则将毫无意义。



    你不能使用其他方式实现您的目标。

    10-08 14:35