如何将来自多个翻译单元的多个 const 对象放置在一个内存块中?

Posted

技术标签:

【中文标题】如何将来自多个翻译单元的多个 const 对象放置在一个内存块中?【英文标题】:How to place multiple const objects from multiple translation units sorted in one memory block? 【发布时间】:2020-03-14 22:23:49 【问题描述】:

我正在为一些设备开发固件,每个设备都包含配置对象,这些配置对象的值可以被外部应用程序更改。这是此类对象的示例:

struct TObjectParams

   ObjectId       ID;            // enum
   void*          Data;          // pointer to configuration data
   unsigned short Length;        // size of data

   TAccessControl AccessControl; //object access control
;

TObjectParams 的每个实例都是 const 且在运行时无法更改。 固件由多个模块组成,每个模块都有自己的配置对象。这样做是因为不同的设备可以包含不同的模块,并且强制每个设备包含所有关闭的对象将浪费资源。 我的目标是将所有对象分组到一个位置(数组或单个部分),并按ID 编号对它们进行排序(排序将显着降低从O(n)O(log n) 的搜索复杂性。

我的第一个想法是使用constexpr 注册对象(在对象的构造函数中),并在编译期间创建包含所有对象的constexpr 数组。但据我所知,只有当所有对象都在同一个翻译单元中时它才会起作用。

我的第二个想法是使用__attribute__((used, section("objects_section"))),现在我确保链接器不会删除任何已定义的对象并且它们都将位于同一部分。问题是排序。有没有办法强制链接器对这些对象进行排序?或者也许可以通过编辑.elf 文件?我知道我可以轻松转储objects_section,对其进行排序(通过自己的应用程序)并通过objcopy 进行更新,但它会完全弄乱调试体验和直接调用任何对象,因为更新部分内容不会更新符号地址(或者可能我错了)。

您知道如何更新部分内容和符号地址吗?或者,也许您知道解决此类问题的更好方法?我愿意接受所有建议。

【问题讨论】:

【参考方案1】:

有没有办法强制链接器对这些对象进行排序?

不,特别是考虑到排序标准可以是任意的。

但是,您实际上并不需要对对象进行排序:您可以对指向对象的指针数组(在初始化期间创建)进行排序,然后对该数组进行后续搜索。

我知道我可以轻松转储 objects_section,对其进行排序(通过自己的应用程序)并通过 objcopy 进行更新,但它会完全弄乱调试体验和直接调用任何对象,因为更新部分内容不会更新符号地址(或者我我错了)。

objcopy 不只是 会搞砸。它还会弄乱正确性:您的Data 成员指向其他一些数据,这意味着每个实例都有关联的重定位记录。如果您对TObjectParams 实例进行排序而不更新重定位记录,您的Data 指针将指向objcopy 之后的错误配置数据。

更新:

该解决方案部分令人满意。

如果您可以将配置数据直接包含到TObjectParams中,它们会变得更大,而指针的开销会更小。

如果配置数据本身没有指向其他数据的指针,那么可以直接对section内容进行排序,完全避免了开销。

如果你不能摆脱指针,那么你将不得不编写一个自定义的 ELF 操作工具来对数据和相关的重定位记录进行排序。这样的工具还可以更新符号部分以解决调试问题。

但是,我认为有一个更简单的解决方案,至少在给定的.o 文件中有一个TObjectParams 实例的情况下(或者当给定的.o 保证只有连续的@ 987654330@s 和 .o 文件在其中包含的 IDs 中不重叠):

    像现在一样将所有 TObjectParams 放在一个单独的部分中。

    链接二进制文件。这将生成一个包含未排序部分的二进制文件,此链接的唯一原因是获取进入此二进制文件的对象列表。

    如果你已经单独维护了这样一个列表,这一步可以跳过。

    对于第 2 步列表中的每个对象文件 (foo.o),使用 objcopy 获取两个新对象:一个仅包含感兴趣的部分 (foo_w.o),另一个删除了该部分 (@ 987654337@).

    按其中包含的IDfoo,bar,baz_w.o 列表进行排序。

    执行最终链接,以任意顺序使用foo,bar,baz_x.o,并以排序顺序使用foo,bar,baz_w.o。链接器通常不会重新排序该部分的内容,并且该链接应生成所需的最终二进制文件。

【讨论】:

我认为该解决方案部分令人满意。可能有数百个这样的对象,当前每个 TObjectParams 的大小是 12B(闪存,因为对象是 const),每个指向它的指针的大小是 4B(ram)。在资源非常有限的嵌入式固件的情况下,存储指针并在运行时对其进行排序将占用已用内存的 1/3。我想避免这种情况。 Russin 您更新的解决方案看起来不错,但我认为您假设一个源文件只包含一个对象。一个源文件中有多个对象怎么样? @legier 我确实在一个 .o 文件中假设了一个 TObjectParams。对于foo.oTObjectParamsID 1 和3,而bar.oIDs 2 和4 的情况,我没有解决方案。你必须要么重组你的资源或编写用于该案例的ELF 操作工具。

以上是关于如何将来自多个翻译单元的多个 const 对象放置在一个内存块中?的主要内容,如果未能解决你的问题,请参考以下文章

extern 关键字和多个翻译单元的使用

ios swift自定义uitablecell如何放置多个项目(标签,图像)

如何从具有多个数组的字典中获取特定键并存储到放置在表格视图单元格中的字符串中

在命名空间中定义并在多个翻译单元中使用的变量的链接

如何使用实体框架关联来自多个上下文的对象

模板变量是不是允许在多个翻译单元中并有效合并?