arm gcc 链接器和 .word

arm gcc Linker and .word

我正在尝试了解我的启动文件和链接器文件。由于我并不真正了解汇编及其怪癖(老实说了解一些基础知识),所以我被困在一个简单的 .word 指令中。

我的流水线:

.word _sdata

然后在链接器中我有

//inside of .data section
. = ALIGN(4);
_sdata = .;

我大概明白了我的链接器做了什么。仍然有点想知道对齐部分以及为什么如果我不调用它_sdata 是在正确的位置创建的但与我的文本部分相关。

无论如何,问题是这个 .word 到底有什么作用。我知道它指的是 _sdata,因为稍后在我的启动文件中它被用作 ldr r1, =_sdata。基本上我想详细了解 .word _sdata 的作用

您将汇编语言与特定于工具链的 linker 脚本混淆了。

.word 只是表示在程序中的这个位置放置一个值。

它不是一条指令,而是一条指令,而是 assembler 的汇编语言的一部分。汇编语言由 assembler 定义,具体工具不是目标处理器体系结构,也不是某些规范。有许多 x86 汇编语言,AT&T 与 Intel 的比较并不是它们数量的一个因素。 ARM、MIPS 等也有许多不同的、通常不兼容的汇编语言。大多数如果它在指令语法和标签和注释以及其他类似的项目中。有时指令。

.globl _start
_start:
    ldr r0,next_add
    bx r0
next:
    bx lr

.word 1,2,3
next_add:
    .word next
.word 0x12345678

Assemble and link and disassemble:

Disassembly of section .text:

08000000 <_start>:
 8000000:   e59f0010    ldr r0, [pc, #16]   ; 8000018 <next_add>
 8000004:   e12fff10    bx  r0

08000008 <next>:
 8000008:   e12fff1e    bx  lr
 800000c:   00000001    andeq   r0, r0, r1
 8000010:   00000002    andeq   r0, r0, r2
 8000014:   00000003    andeq   r0, r0, r3

08000018 <next_add>:
 8000018:   08000008    stmdaeq r0, {r3}
 800001c:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

我使用了 disassembler 来查看发生了什么,所以忽略从 800000c 行开始的反汇编,因为它是我们想要的数据,32 位数字,这些是我们要求的项目使用 .word 指令放置在程序中。

还有一个例子,说明为什么你可能想要做这样的事情你可能想要 linker 稍后填写的一些标签的地址,你不必手工计算指令或字节数要自己解决这个问题,让工具来完成这项工作。

我怀疑真正的问题是基于 linker 脚本,这看起来也是 gnu 工具

so.s

.text
.globl _start
_start:
    bx lr

.data
.word _tdata
.word _pdata
.word _sdata

so.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .data :
    {
        _tdata = .;
        *(.data*)
        _pdata = .;
        . = ALIGN(8);
        _sdata = .;
    } > ram
}

assemble, link, disassemble

Disassembly of section .text:

08000000 <_start>:
 8000000:   e12fff1e    bx  lr

Disassembly of section .data:

20000000 <_tdata>:
20000000:   20000000    andcs   r0, r0, r0
20000004:   2000000c    andcs   r0, r0, r12
20000008:   20000010    andcs   r0, r0, r0, lsl r0

这次我在 .data 部分使用了 .word 而不是 .text。它可以去任何地方它是将信息位放置在程序中您想要这些位的少数几种方法之一。

同样,.data 部分中的所有反汇编都将被忽略,它的数据不是指令,disassembler 只是试图完成它的工作,因为它不知道指令中的数据。

什么行像_sdata = .; linker 脚本中的意思是 linker 正在创建一个变量并且 .表示当前位置,因此我正在创建一个变量,linker 将使用内存映射定义中该位置的程序中的地址值填充该变量,称为 linker 脚本。

你可以看到我在那里放了一些。

.data :
{
    _tdata = .;
    *(.data*)
    _pdata = .;
    . = ALIGN(8);
    _sdata = .;

_tdata 应该设置为 .data 的开始地址,我在这里定义为 0x20000000(脚本中的第一项使用 ram 地址 space)。但是就像汇编语言中的标签一样,这只是一个值,它不会为此项分配 space,在工具内部它有一个带有名称和值的 table,就像一个标签,该值是我们可以在代码中要求的东西。

从 0x20000000 开始,我们希望放置 .data 项,因此我请求的三个 .words 将在 0x20000000、0x20000004 和 0x20000008 到达那里。

我要求的第一个数据项是 label/address _tdata,我们知道它将成为 .data 或 0x20000000 的开头。 _pdata 是放置 .data 项之后的地址,因此 0x2000000c 就是该地址。我们看到 linker 为我们生成了它。因为这些是 .words 并且已经对齐,并且该工具通常为此目标在单词边界上对齐,所以我将其更改为 ALIGN(8)。

现在。在左边,那是说我希望你使当前地址等于右边的东西。 =对齐(8);意味着使用当前地址找到具有该对齐方式(128 位,8 字节)的下一个地址,即使它是当前地址。并将地址指针更改为该值。之后的下一行是分配这个 label/variable 地址指针的值。

因此,在 0x2000000c 之后,ALIGN(8) 导致地址更改为 0x20000010,然后 _sdata = 。导致 variable/label 的 _sdata 等于 0x20000010,linker 看到有人要求全局 label/variable 并将其放置以清理 linking 作业。

linker 脚本具有这些类型的东西是很常见的,您经常会看到 variable/label 放在一个部分之前和之后,这样一些代码可以知道该部分从哪里开始,有多大,例如,C 程序员希望 .bss 数据被清零,所以一种非常常见的方法是 bootstrap 将该内存清零,但要知道代码在哪里,请 linker 通过创建这些变量然后在程序中使用它们有时你会看到 bss_size = bss_end - bss_start;在 linker 脚本中,另外两个在 .bss 之前和之后。您会看到使用了 .ALIGNs,以便将内存清零的代码可以对对齐进行假设并进行 simpler/faster 填充例程(不,您不使用没有意义的 memset() 直到您不能使用 C bootstrap 它并且你不能 bootstrap 它使用一个 C 函数,直到你 bootstrap C).

正如我在这里演示的那样,使用工具(尤其是 gnu)相对容易,可以查看发生了什么。在你更好地掌握语言(任何一种)并且你不需要 运行 任何代码之前,不要像这样弄乱 linker 脚本的细微差别可能会更容易甚至需要一个正常运行的程序。只需使用工具并检查输出即可。

您最初不需要 linker 脚本

arm-none-gnueabi-as so.s -o so.o
arm-none-gnueabi-ld -Ttext=0x1000 -Tdata=0x2000 so.o -o so.elf
arm-none-gnueabi-objdump -D so.elf

然后使用 linker 脚本使事情复杂化,从简单开始,然后根据需要逐步提高。您在野外发现的大多数 linker 脚本都过于复杂且不必要。您自己可能会弄乱 linker 脚本的地方不需要那么乱。