将内存中的变量与内存中 2 的幂的偏移量对齐

Align a variable in memory with offset from power of two in memory

我想放置一个 2kB 的内存块,16 字节对齐,然后 1024 字节对齐。

平台:arm、裸机、GNU 工具链。不需要便携性

我可以使用 GCC/attributes 编译指示、ld 自定义链接描述文件或任何其他解决方案吗?

我想避免为此浪费 1kB(基本上放置一个 3kB 的内存块以 1kB 对齐并添加 1024-16 字节的填充)。 强制特定地址放置数据是可能的,但 ld 是否能够在其前后放置变量(或者它只是放置填充的一种方式?)

上下文:根据硬件设计,缓冲区需要位于 1k 边界,但我想在之前/之后添加一点空间,以便能够复制到此缓冲区而无需检查我的源是否最多16B 宽。

编辑:添加示例。

假设我的 RAM 从 0x2000000 开始。我需要在其中放置一个 char buf[2048] ,偏移量为 1024*N-16 - 即 (&buf[16])%1024==0 ,希望不会丢失 1008 个填充字节。

(edit2) 所以我想要 :

您应该在链接描述文件中定义一个特定的部分,并具有所需的对齐方式。

正在查看the man

ALIGN(exp)

Return the result of the current location counter (.) aligned to the next exp boundary. exp must be an expression whose value is a power of two. This is equivalent to

(. + exp - 1) & ~(exp - 1)

ALIGN doesn't change the value of the location counter--it just does arithmetic on it. As an example, to align the output .data section to the next 0x2000 byte boundary after the preceding section and to set a variable within the section to the next 0x8000 boundary after the input sections:

SECTIONS{ ...
  .data ALIGN(0x2000): {
    *(.data)
    variable = ALIGN(0x8000);
  }
... }

The first use of ALIGN in this example specifies the location of a section because it is used as the optional start attribute of a section definition (see section Optional Section Attributes). The second use simply defines the value of a variable. The built-in NEXT is closely related to ALIGN.

例如,您可以定义您的部分

SECTIONS
{
  .myBufBlock ALIGN(16) :
  {
    KEEP(*(.myBufSection))
  } > m_data
}

并在您的代码中可以

unsigned char __attribute__((section (".myBufSection"))) buf[2048];

编辑

SECTIONS
{
  .myBufBlock 0x0x7FBF0 :
  {
    KEEP(*(.myBufSection))
  } > m_data
}

并在您的代码中可以

unsigned char __attribute__((section (".myBufSection"))) buf[16+2048+16];

对于您的 DMA,您可以设置 1k 对齐的地址 &buf[16]

ALIGN(exp) 等价于 (. + exp - 1) & ~(exp - 1)。当然,该表达式仅在 exp 是 2 的幂时有效。所以你不能使用 ALIGN(),但你可以编写自己的表达式来产生你想要的结果。 ((. + 1024 + 16 - 1) & ~(1024 - 1)) - 16 之类的东西应该可以解决问题。为 . 插入各种值,您会看到它按您想要的方式四舍五入。

您将遇到的问题是,链接器会将您指定的每个部分放在您的特殊部分之前,并将指定在它之后的每个部分放在它之后。它不会巧妙地将不同文件的 .data 部分排列在之前或之后,以产生最少的填充量。它也根本不会对目标文件和部分中的各个变量进行重新排序。如果你想尽可能紧凑地打包,我认为你需要做类似的事情:

.data : {
    *(.about1008bytes)
    . = ((. + 1024 + 16 - 1) & ~(1024 - 1)) - 16
    *(.DMAbuf)
    *(.data)
}

使用节属性将缓冲区放在 .DMAbuf 中,并尝试找到接近但不超过 1008 字节的其他数据变量并将它们粘贴在节 .about1008bytes.[=20= 中]

如果你想发疯,使用 gcc -fdata-sections 将每个数据对象放在它自己的部分中,使用 readelf 提取部分大小,将其提供给你编写的程序以对它们进行排序以获得最佳包装,然后吐出一大块链接器脚本,以最佳顺序列出它们。