链接脚本中的ALIGN

23

在链接脚本中,ALIGN关键字的作用是什么?我读了很多关于链接脚本的教程,但是我不明白ALIGN到底是干什么的。有人能简单解释一下吗?谢谢!

5个回答

37

典型用法是:

. = ALIGN(8);

这意味着:插入填充字节,直到当前位置对齐在8字节边界上。也就是说:

while ((current_location & 7) != 0)
  *current_location++ = padding_value;

1
谢谢您以如此简单的方式解释。我真的找不到一个能够这么简单地解释它的地方。 - TopchetoEU

7
ALIGN()指令告诉链接器,节(bss、text)应该对齐到这个位置。
一个典型的例子可以在这里查看(4.6.3“输出节描述”)。
例如:
    //.data is aligned by word size on the 32-bit architecture and direct it to the data section
For a 32-bit machine, it typically needs to be word aligned 

        .data : ALIGN(4) 
        {
           *(.data*)
        } > data_sdram

2

. = ALIGN(8)

对应以下(使用运算符的工作链接脚本示例):

data = .;

. = ((data + 0x8 - 1) & ~(0x8 - 1)) - data;

1
ALIGN有两种典型的用法。
  • 将一个部分的起始位置与所需的边界对齐

  • 将一个部分填充到所需的大小。

      /* . = ALIGN(BEGIN) ;  如果VMA==LMA,则等同于下面的代码 */
      .section : ALIGN(BEGIN) 
      {
        /* . = ALIGN(BEGIN) ; !!与上面的代码不等同!! */
        ...
        . = ALIGN(END);
      }
    
第一个ALIGN确保起始值是2的幂次方的倍数,以BEGIN为基准。第二个ALIGN将确保大小是END的倍数。通常情况下,您希望它们相等且为2的幂次方,这表示需要多少地址位,并且可以帮助缓存。
例如,如果链接器脚本禁止大小具有固定大小的倍数,则可以通过大传输大小来优化复制循环。
在'.section'内部使用ALIGN(BEGIN)会导致一些垃圾出现在节的开头。请参阅:FILL()=fill以控制写入的内容。请参见下面的原因,不要使用它;它实际上什么也不做。
一些需要对齐的结构示例包括:
- 向量表 - MMU表 - 需要缓存填充的时间关键例程 - 用于初始化效率的.bss.initdata Gnu ld语法允许您在任何地方使用ALIGN。 如果您要使用BYTELONG自定义代码结构,可能需要在一个部分中对表格/结构进行对齐。这是链接器脚本的一种相当晦涩的用法,但是可以实现,并且是对前两种用法的例外情况。然而,对于大多数初学者来说,了解ld,这两种用法几乎总是期望的用法。
在一个部分内的操作就好像所有的地址都是相对的。所以部分的起始位置是零(并且由于这个原因,代码中注释的不等效是无法工作的)。由于最后一件事是. = ALIGN(...);,所以它设置了部分的大小,因为它是基于零的。即使部分的起始地址没有对齐,这也能正常工作。

对于使用LMA==VMA的非裸机用户,很多问题都不会有影响。然而,ALIGN的放置将决定正在对齐什么。VMA是执行地址。LMA是二进制文件中的加载地址或位置;启动代码将把一个复制到另一个。对于非裸机,加载器设置所有内容并可能处理ELF文件以将部分放置在适当的地址处。 - artless noise
被接受的答案没有区分LMA、VMA或者在一个段内还是在段外。这通常与'.'的行为有关,而不是直接与ALIGN()有关。然而,90%以上的ALIGN()使用都是用来设置当前位置的。它可以直接与表达式一起使用(祝你好运)。 - artless noise

1

经过一些恶劣的行为和意外的结果,这是我的结论:

只有当exp是2的幂次方时,ALING(exp)才能按预期工作!!!

一些文档对此解释不清楚,而另一些则根本没有提到!
https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_14.html

ALIGN将等同于

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

如您所见,存在一个带有掩码 (exp-1) 的 & (AND) 仅当 exp 为 2^ 时才有效

让我们来看这个例子:

  .fini_array : {

    . = ALIGN(4);

    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);

    . = ALIGN(1024);

  } >FLASH  : FLASH_PHDR =0xDD

这里,最后的ALIGN(1024)将确保(.)光标(或下一个将使用的地址)与1024字节对齐。为了说明这一点,我使用了0xDD来填充链接器将引入的填充区域,以满足1024可整除的要求。

以下是结果:

enter image description here

如你所见,链接器只是在有用数据后面加了316个字节(0xDD)作为填充,以便使下一个光标对齐到1024。
下一个光标(地址)是31744,可以被1024整除。
让我们看一个错误的例子(出乎意料的结果):
  .fini_array : {

    . = ALIGN(4);

    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);

    . = ALIGN(13);

  } >FLASH  : FLASH_PHDR =0xDD

下一个光标将不会像人们期望的那样对齐到13个字节!!!

enter image description here

下一个地址是31439,显然是错误的,因为在上面的等效公式中使用了不完整的掩码。希望这能稍微澄清一些事情!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接