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

Is there a way to somehow group several symbols from different files together so that if one of them is referenced, all are linked?

先了解一下上下文。

我目前正在开发一个模块化(嵌入式)microOS,其中包含驱动程序、端口、分区和其他类似的 object 表示为结构,不同的 "operations" 结构包含指向您想要的内容的指针调用他们的方法。没什么好看的。

我有宏和链接器脚本位来使所有 object 给定类型(例如,所有驱动程序),尽管它们的定义分散在源文件中,但都像数组一样处理,在 flash 中的某个地方,但在某种程度上让链接器(我使用 GNU GCC/LD。)垃圾收集代码中未明确引用的那些。

然而,经过几年对系统的改进和灵活性的提高,我发现它对于中小型微控制器来说太 flash-greedy 了。 (我只使用 32 位架构,没有什么太小了。)你可能会说,我本应被期望,但我正在努力走得更远,做得更好,目前我怀疑 LD 是否会让我这样做。

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

首先,我认为我可以将每个方法结构拆分为多个部分,这样一来,所有端口的 "probe" 方法都在一个名为 .struct_probe 的部分中结束,并且包装 port_probe() 函数可以在该函数内部引用 zero-length object,因此当且仅当 port_probe() 函数在某处被调用时,所有端口的探针引用才会链接。

但我错了,对于链接器,"input sections" 是他对垃圾的解析 collection(因为此时内部没有更多的对齐信息,它不能负担得起利用符号 - 和传入 object - 通过重新排序包含部分的内部并缩小它来删除)不仅仅由部分名称标识,而是由部分名称标识 一个源文件。因此,如果我实现了我的意图,none 我的方法将链接到最终的可执行文件中,我会干杯。

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

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

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

ELF 有一个叫做 "group sections" 的功能,它用于定义部分组或部分组,例如如果该组的一个成员部分是活动的(因此 linked) , 都是.

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

至少可以说,关于该功能及其实现的文档很少。目前,可以在 here.

中找到标准对组部分的定义

GCC 不提供操作节组(或任何类型的节 flags/properties)的结构。 GNU 汇编器的文档指定了如何影响一个部分到一个组 here.

我发现在任何 GNU 文档中都没有提到 LD 对组部分的处理。虽然提到了 here and here

作为奖励,我找到了一种使用 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"

工作完成。如果您想知道为什么仅在单独的内联汇编语句中描述该部分是不够的,那么如果您这样做,您会得到一个空的分组部分和一个填充的同名单独部分,这在 [=45 处不会有任何影响=] 时间。所以.

这不是完全令人满意,但由于缺乏更好的方法,这就是我所追求的:

从 link 读者的角度来看,似乎必须有效合并来自多个编译单元的部分的唯一方法是首先 link 将结果对象放在一个大对象中,然后 link 最终程序使用那个大对象而不是小对象。小对象中同名的部分将合并到大对象中。

虽然这有点脏,但也有一些缺点,例如可能出于垃圾收集的目的合并了一些您不想合并的部分,以及隐藏每个部分来自哪个文件(尽管信息保留在调试部分),比如说,如果您想在最终的 ELF 中拆分主要部分(.text、.data、.bss...),以便能够查看哪个文件对闪存和 RAM 的贡献例如用法。