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 工具链错误链接器
AVR - GNU 链接器脚本 - 如何获取 .data 部分的加载地址