【问题标题】:Linker adds extra padding in section?链接器在部分中添加了额外的填充?
【发布时间】:2016-09-27 15:38:59
【问题描述】:

我正在使用 GCC 并试图让链接器从自定义部分中的不同文件创建数据对象数组。我已将此部分添加到链接器脚本中:

.mydata :
{
  __mydata_start = .;
  KEEP (*(.mydata))
  __mydata_end = .;
}

这是我的数据结构:

struct my_type_t {
    unsigned char a;
    size_t b;
    void *c;
    const void *d;
    const void *e;
    const char *f;
};

我在 64 位平台上,sizeof(struct my_type_t) 给出 48。但是,链接器似乎想将这些结构对齐到 64 字节。

我的声明:

struct my_type_t a1 __attribute__((section(".mydata"))) = {
    1, 2, (void *)3, (void *)4, (void *)5, (void *)6,
};
struct my_type_t a2 __attribute__((section(".mydata"))) = {
    7, 8, (void *)9, (void *)10, (void *)11, (void *)12,
};

链接器输出:

$ gcc -o ldtest ldtest.c -Wl,-Tlink.ld
$ objcopy -Obinary -j .mydata ldtest mydata
$ hd mydata
00000000  01 00 00 00 00 00 00 00  02 00 00 00 00 00 00 00  |................|
00000010  03 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00  |................|
00000020  05 00 00 00 00 00 00 00  06 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  07 00 00 00 00 00 00 00  08 00 00 00 00 00 00 00  |................|
00000050  09 00 00 00 00 00 00 00  0a 00 00 00 00 00 00 00  |................|
00000060  0b 00 00 00 00 00 00 00  0c 00 00 00 00 00 00 00  |................|

如果我改为将其声明为数组:

struct my_type_t a1[2] __attribute__((section(".mydata"))) = {
    {
    1, 2, (void *)3, (void *)4, (void *)5, (void *)6,
    },
    {
    7, 8, (void *)9, (void *)10, (void *)11, (void *)12,
    },
};

没有填充,正如预期的那样:

00000000  01 00 00 00 00 00 00 00  02 00 00 00 00 00 00 00  |................|
00000010  03 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00  |................|
00000020  05 00 00 00 00 00 00 00  06 00 00 00 00 00 00 00  |................|
00000030  07 00 00 00 00 00 00 00  08 00 00 00 00 00 00 00  |................|
00000040  09 00 00 00 00 00 00 00  0a 00 00 00 00 00 00 00  |................|
00000050  0b 00 00 00 00 00 00 00  0c 00 00 00 00 00 00 00  |................|

为什么链接器会添加超出编译器认为必要的额外填充,我该如何摆脱它?

【问题讨论】:

  • "为什么链接器要添加额外的填充..." - 为什么不呢?标准中没有关于不同对象的特定放置的要求。它与兼容的 C 代码完全无关。关于“如何摆脱它”,您已经有了答案。我认为没有问题。
  • 正如我所指定的,这些对象位于不同的文件中,因此我无法从中创建数组。但是,我想将它们视为 C 代码中的数组。 C认为大小是48,所以如果我增加一个指向第一个的指针,它将指向填充,而不是下一个的开始。
  • “但是,我想将它们视为 C 代码中的数组” - 这是未定义的行为。听起来像一个 XY 问题。必须有不同的方法,例如指针表。
  • 这是 Linux 为允许不同的驱动程序和子系统注册 init 函数所做的。在循环它们进行初始化时,它将它们视为一个数组,这正是我想要做的。如果链接器只是随意插入填充字节,他们将无法依赖这种机制。
  • 试试SUBALIGN(0)

标签: c gcc linker ld


【解决方案1】:

我不知道它为什么要在 64 字节上对齐,但你可以这样做:

struct my_type_t {
    unsigned char a;
    size_t b;
    void *c;
    const void *d;
    const void *e;
    const char *f;
} __attribute__ ((aligned (64)));

为了使 gcc 自动将每个结构填充到最近的 64 字节高地址,并使 sizeof(struct my_type_t) 也包含填充。这使得这样的操作可以正常工作:

struct my_type_t *s = &__mydata_start;
s++; // Jump to next entry
s[0]; // First entry
s[1]; // Second entry

【讨论】: