GNU 链接器:适应名称修改算法的更改

Posted

技术标签:

【中文标题】GNU 链接器:适应名称修改算法的更改【英文标题】:GNU linker: Adapt to change of name mangling algorithm 【发布时间】:2018-11-29 14:57:12 【问题描述】:

我正在尝试重新编译现有的 C++ 应用程序。 不幸的是,我必须依赖一个专有库,我只有一个预编译的静态存档。

我使用 g++ 7.3.0 版和 ld 2.30 版。 无论编译时使用的 GCC 版本是什么,它都是古老的。

头文件定义方法:

class foo 
    int bar(int & i);

正如nm lib.a 所示,库存档包含相应的导出函数:

T bar__4fooRi

nm app.o 显示我最近的编译器使用了另一种名称修饰:

U _ZN4foo9barERi

因此链接器无法解析库提供的符号。

是否可以选择名称修饰算法? 我可以引入地图或明确定义错误名称吗?

【问题讨论】:

您可以访问原始编译器吗?你能找到版本吗? 随库提示提供给 GCC 版本 2.91.66、2.95.2 和 2.95.3 的示例程序中的注释。可能与那个时代的 EGCS 分支相关或不相关的版本。我想这些版本的源代码在某个地方飘荡,但我手边没有编译器,不。 您可以生成一个linker script,为旧式名称提供新式别名。 @Hermann GCC 在#include <cxxabi.h> 中提供了一个名为abi::__cxa_demangle() 的名称解构函数。如果旧编译器具有相同的功能,您可以使用它来对名称进行解构,并使用新编译器对其进行重新编排。那么您也许可以按照@Botje 的建议创建一个链接器脚本? (但可能存在其他 ABI 不兼容问题) 即使你设法翻译了所有的符号名称,整个 ABI 也不一样不是吗? 【参考方案1】:

@Botje 的建议引导我编写这样的链接器脚本(PROVIDE 节中的空格很重要):

EXTERN(bar__4fooRi);
PROVIDE(_ZN4foo9barERi = bar__4fooRi);

据我了解,这会将bar__4fooRi 视为外部定义的符号(确实如此)。如果搜索了 _ZN4foo9barERi 但未定义,则 bar__4fooRi 将取代它。

我正在像这样从 GNU 工具链调用链接器(注意顺序 - 脚本需要在依赖对象之后但在定义库之前):

g++ -o application application.o script.ld -lfoo

看起来这可以工作。 至少在理论上。 链接器现在考虑库的其他部分,这又依赖于其他无法解析的符号,包括(但不限于)__throw__cp_pop_exception__builtin_delete。我不知道现在这些函数是在哪里定义的。 Joxean Koret 根据猜测显示了this blog post 中的一些位置(__builtin_new 可能是malloc)——但我没有那么自信。

这些发现使我得出结论,该库依赖于不同风格的异常处理,可能还依赖于内存管理。

编辑: 由于@eukaryota 指出的 ABI 更改,结果可能纯粹是学术性的,链接描述文件确实可以用于“别名”符号。这是一个完整的最小示例:

foo.h:

class Foo 
    public:
    int bar(int);
;

foo.cpp:

#include "foo.h"
int Foo::bar(int i) 
    return i+21;

main.cpp:

class Foo 
    public:
    int baa(int); // use in-place "header" to simulate different name mangling algorithm
;

int main(int, char**) 
    Foo f;
    return f.baa(21);

script.ld:

EXTERN(_ZN3Foo3barEi);
PROVIDE(_ZN3Foo3baaEi = _ZN3Foo3barEi); /* declare "alias" */

构建过程:

g++ -o libfoo.o -c foo.c
ar rvs libfoo.a libfoo.o # simulate building a library
g++ -o app main.o -L. script.ld -lfoo

app已编译,可以执行并返回预期结果。

【讨论】:

我想到的另一种可能性是为旧编译器获取反编译器,反编译然后用新编译器重新编译它的输出。 另一种选择——但是这个软件已经有 20 年的历史了。我已经到了可以自信地走到老板面前宣布我们应该正式放弃这个库,而是购买新的东西的地步,这次最好是开源的。 呵呵。您也可以直接在静态存档中编辑名称,只要您将名称编辑为。因此,您可以创建所需名称的较短版本,但使用新的修改约定并将其直接编辑到存档中......

以上是关于GNU 链接器:适应名称修改算法的更改的主要内容,如果未能解决你的问题,请参考以下文章

STM32 Eclipse + ARM GNU 工具链错误链接器

GNU Linker - 孤立的部分和符号赋值

AVR - GNU 链接器脚本 - 如何获取 .data 部分的加载地址

是否有 GCC 编译器/链接器选项来更改 main 的名称? [复制]

使用 GNU 链接器的正确方法

GNU 链接器脚本 - 将闪存移动到新区域