首页 > 解决方案 > 有没有办法以某种方式将来自不同文件的多个符号组合在一起,以便如果引用其中一个符号,则所有符号都被链接?

问题描述

先来点上下文。

我目前正在开发一个模块化(嵌入式)微操作系统,其中驱动程序、端口、分区和其他此类对象表示为结构,具有不同的“操作”结构,其中包含指向您将调用它们的方法的指针。那里没什么好看的。

我有宏和链接器脚本位来制作它,以便给定类型的所有对象(例如,所有驱动程序),虽然它们的定义分散在源文件中,但在闪存中的某处以数组的形式进行处理,但以某种方式让链接器(我使用 GNU GCC/LD。)垃圾收集那些没有在代码中明确引用的垃圾。

然而,经过几年改进系统并提高其灵活性后,我发现它对于中小型微控制器来说过于贪婪了。(我只使用 32 位架构,没有什么太小了。)你可能会说,我本来是可以预期的,但我正试图走得更远,做得更好,目前我怀疑 LD 会让我这样做。

我想得到的是代码不使用的方法/函数也会被垃圾收集。目前情况并非如此,因为它们都由拥有它们的对象的指针结构引用。我想避免使用宏配置开关,并且应用程序开发人员必须通过几层代码来确定当前使用哪些功能以及可以安全禁用哪些功能。我真的很喜欢链接器的垃圾收集让我自动化所有过程。

首先,我想我可以在部分之间拆分每个方法结构,例如,所有端口的“探针”方法最终都在一个名为 .struct_probe 的部分中,并且包装 port_probe() 函数可以引用内部的零长度对象该函数,以便当且仅当在某处调用 port_probe() 函数时,所有端口的探针引用才会链接。

但是我错了,对于链接器,“输入部分”是他对垃圾收集的解决方案(因为此时内部没有更多对齐信息,并且它无法利用符号 - 并且传入对象 - 通过重新排序包含部分的内部并缩小它来删除)不仅仅由部分名称标识,而是由部分名称源文件标识。因此,如果我实现了我的意图,我的任何方法都不会链接到最终的可执行文件中,我会很高兴。

这就是我目前所处的位置,坦率地说,我很茫然。我想知道这里是否有人会有更好的主意,或者让每个方法“向后引用”包装函数或其他一些对象,这些对象反过来被函数引用并带走所有方法,或者如标题所说,以某种方式分组这些方法/部分(请不要将所有代码收集在一个文件中),以便引用一个意味着将它们全部链接起来。

我对所有永恒的感激之情就在这里。;)

标签: gcclinkerld

解决方案


由于我花了一些时间记录和试验以下线索,即使没有成功,我想在这里公开我的发现。

ELF 有一个称为“组部分”的功能,用于定义部分组或部分组,例如如果组的一个成员部分是活动的(因此链接),那么所有部分都是。

我希望这是我问题的答案。TL;DR:不是,因为分组部分是为了对模块内的部分进行分组。实际上,当前唯一定义的组类型是 COMDAT 组,根据定义,这些组与其他模块中定义的同名组不同。

至少可以说有关该功能及其实现的文档很少。目前,可以在此处找到标准对组部分的定义。

GCC 不提供用于操作节组(或任何类型的节标志/属性)的构造。GNU 汇编器的文档在此处指定了如何将节影响到组。

我在任何 GNU 文档中都没有发现任何关于 LD 处理组部分的内容。不过,这里这里都提到了它。

作为奖励,我找到了一种使用 GCC 在 C 代码中指定节属性(包括分组)的方法。这是一个肮脏的 hack,所以当你读到这篇文章时,它可能不再起作用了。

显然,当你写

int bar __attribute__((section("<name>")));

GCC 将引号之间的内容盲目地粘贴到程序集输出中:

.section    <name>,"aw"

(如果名称与几个预定义模式之一匹配,则实际标志可能会有所不同。)

从那里开始,这完全是代码注入的问题。如果你写

int bar __attribute__((section("<name>,\"awG\",%probbits,<group> //")));

你得到

.section  <name>,"awG",%progbits,<group> //"aw"

工作就完成了。如果您想知道为什么在单独的内联汇编语句中简单地描述该部分是不够的,如果这样做,您会得到一个空的分组部分和一个具有相同名称的填充单独部分,这在链接时不会产生任何影响。所以。


推荐阅读