【问题标题】:Align a variable in memory with offset from power of two in memory将内存中的变量与内存中 2 的幂的偏移量对齐
【发布时间】:2016-12-13 13:54:15
【问题描述】:

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

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

我可以使用 GCC/属性编译指示、ld 自定义链接器脚本或任何其他解决方案吗?

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

上下文:通过硬件设计,缓冲区需要在 1k 边界,但我想在之前/之后添加一点空间,以便能够复制到这个缓冲区,并且没有边界检查我的源是否最多 16B 宽.

编辑:添加示例。

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

(编辑2) 所以我想要:

  • 0x2000000 - 一些变量
  • 0x2000010 - 其他一些变量 ...
  • 0x2000100 - 其他一些变量 ...
  • 0x20003F0 - char buf[2048] : 这里 (int)&buf[16]%1024=0x2000400%1024==0
  • 0x2000BF0 - 其他一些变量 ...

【问题讨论】:

  • 您可以制作 2 个大小为 1024 的缓冲区的结构。您将设置一个与 16 对齐。
  • 您应该在链接器脚本中定义一个特定的部分,并使用所需的对齐方式
  • @koper :我希望它们在 1024 之前对齐 16 个字节。您的解决方案 IIUC,将有 2 个大小为 1024 的缓冲区对齐到 16(所以要么对齐到 16,32,48,64 ...)
  • @LP:你能详细说明一下吗?我想创建这样一个脚本,但我无法获得正确的参数/指令,这些参数/指令可以让我在两个边界的幂之前声明数据对齐(并且不会丢失 1k 的内存)。
  • 为什么投反对票?这个问题是不是表述不好?

标签: c gcc linker embedded ld


【解决方案1】:

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)
}

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

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

【讨论】:

    【解决方案2】:

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

    看着the man

    对齐(exp)

    返回与下一个 exp 边界对齐的当前位置计数器 (.) 的结果。 exp 必须是一个表达式,其值为 2 的幂。这相当于

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

    ALIGN 不会改变位置计数器的值——它只是对其进行算术运算。例如,将输出 .data 节与前一节之后的下一个 0x2000 字节边界对齐,并将节内的变量设置为输入节之后的下一个 0x8000 边界:

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

    本例中第一次使用 ALIGN 指定了节的位置,因为它被用作节定义的可选开始属性(参见可选节属性一节)。第二种用法简单地定义了一个变量的值。内置的 NEXT 与 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 设置地址 &buf[16],该地址将是 1k 对齐的。

    【讨论】:

    • 当然可以,但是 exp 必须是 2 的幂,所以我应该在这里使用 exp=1024 吗?那么 -16 字节偏移量呢?
    • 我不确定。例如,我希望缓冲区 buf 在地址 N*1024-16 处对齐,例如:地址 1024-16=1008,或 2k-16=2032,甚至 511k-16==0x7FBF0,您的示例会将 mybufblock 对齐到 N *16
    • 我认为有些误解。对齐是指节内数据的对齐。段的起始地址另当别论,可以定义,例如:.myBufBlock 0x123456 :
    • 假设我的 RAM 位于 0x2000000。我需要将 char[2048] 放置在其中的 1024*N-16 偏移处,希望不会丢失 1008 个填充字节。
    • 那么:您能否发布您的链接描述文件,其中定义了 1024 对齐?如果是这样,我可以看看它并理解你的意思。因为在一个数据段中有一个 1024 字节对齐是很奇怪的。
    猜你喜欢
    • 2016-10-18
    • 2021-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-16
    相关资源
    最近更新 更多