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

How to place multiple const objects from multiple translation units sorted in one memory block?

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

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

   TAccessControl AccessControl; //object access control
};

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

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

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

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

Is there any way to force linker to sort these objects?

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

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

I know that I can easily dump objects_section, sort it (by own app) and update through objcopy but it will totally mess debugging experience and direct call of any object because updating section content won't update symbols addresses (or maybe I am wrong).

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

更新:

that solution is partially satisfying.

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

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

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

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

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

  2. Link 二进制文件。这将生成一个包含未排序部分的二进制文件,这样做 link 的唯一原因是获取进入该二进制文件的对象列表。

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

  3. 对于第 2 步列表中的每个对象文件 (foo.o),使用 objcopy 获取两个新对象:一个只包含感兴趣的部分 (foo_w.o),以及删除该部分的一个 (foo_x.o)。

  4. ID{foo,bar,baz}_w.o 的列表进行排序。

  5. 执行最后的link,使用{foo,bar,baz}_x.o任意顺序,{foo,bar,baz}_w.o排序顺序。 linker 通常不会重新排序该部分的内容,link 应该会生成所需的最终二进制文件。