汇编程序段指令有什么用?
What are assembler section directives used for?
我正在努力学习 ARM 汇编。
写完这个 Hello World 小程序后:
.global _start
.text
_start: ldr R1,=msgtxt
mov R2,#13
mov R0,#1
mov R7,#4
svc 0
mov R7,#1
svc 0
.data
msgtxt: .ascii "Hello World!\n"
.end
我注意到我可以删除 .text 和 .data 指令,程序也能正常工作。
因此我很好奇:我读到的所有内容都强调了一个事实,即 .text 部分将用于代码,.data 部分用于数据。可在这里,在我的眼前,他们似乎什么都不做!
因此,如果这些不是用来分别保存代码和数据,那么它们的真正用途是什么?
这些类型的指令取决于您正在构建程序的体系结构,它们会选择将哪个内存部分分配给后面的任何代码或数据。最后,一切都只是一串字节。汇编程序后,symbols/labels 将根据它们所在的部分分配不同的内存地址。
.text
一般分配在只读内存段,最适合不希望改变的代码。
.data
通常是内存的可写部分。我相信如果预计不会更改(或者体系结构可能具有类似的只读段),将字符串放在代码数据旁边的 .text
中是很常见的。我会说 .data
部分甚至 避免 大多数时候。为什么?因为 .data
部分需要初始化——在程序启动时从程序二进制文件复制到内存中。您的程序引用的大多数数据都可以是只读的,并且它们操作所需的任何内存通常只分配给 .bss
段,该段分配一段未初始化的内存。
在同一节中混合代码和数据有一些优点,例如可以轻松访问与 PC 寄存器(正在执行的代码的地址)有相对偏移的数据地址。当然也有缺点,如果你试图修改只读内存,你最终至少会忽略你的操作,并且程序可能会触发异常并崩溃。所有这些都是特定于体系结构的,最安全的做法是将代码保存在用于代码的段中,data/allocations 保存在用于数据的段中。
这一切都非常具体地针对您的计划目标。例如,Game Boy Advance 有一个 256KB "slow" 内存区域,一个 32KB "fast" 内存区域,然后是只读 "ROM" 区域(游戏卡带数据),它可以是几个兆字节,汇编程序使用了这些内存部分:
.data or .iwram -> Internal RAM (32KB)
.bss -> Internal RAM uninitialized
.ewram -> External RAM (256KB)
.sbss -> External RAM uninitialized
.text or .rodata -> Read only ROM (cartridge size)
再举个例子,SPC-700(SNES 声音芯片)有 64KB 的可读写内存,用于所有内容,但它的前 256 个字节访问速度更快("zero page") .在这种理论上的情况下,.data
和 .text
将被分配到相同的内存区域——也就是说,它们不会分配在零页中,并且它们都共享相同的内存。零页将有一个自定义段,.text
和 .data
之间的差异很小 - 只是一种区分汇编程序中哪些符号指向 "data" 的方法哪些符号指向程序代码。
GAS(与大多数 assemblers 一样)默认为 .text
部分,您的只读数据在 .text
中仍然有效
一切都是字节
您可以将 echo 'mov r1, #2' > foo.s
和 assemble+link 转换为 ARM 二进制文件(
gcc -nostdlib -static foo.s
例如)。您可以在 GDB 中单步执行该指令。
(如果没有 sys_exit
系统调用,你的程序会在那之后崩溃,但当然你也可以在没有任何指令的情况下这样做。)
linker 会警告它没有找到 _start
符号(因为你遗漏了标签本身,更不用说告诉 .globl
指令 assembler 使其在目标文件的符号 table.
中可见
但是 GNU binutils ld
的默认设置是使用 .text
部分的开头作为 ELF 入口点。
除 .text
以外的大多数部分默认情况下不会 link 进入 executable 内存,因此 _start:
在 .data
中通常是一个问题。
只读数据通常应放在 .rodata
部分中,该部分无论如何都会作为 TEXT 段的一部分 link 编辑。因此,就运行时行为而言,将它放在 .text
部分 的末尾(通过省略 .data
)几乎完全等同于你应该做的。
What's the difference of section and segment in ELF file format
将其放入 .data
会导致 linker 将其放入不同的段,告诉 OS 的 ELF 程序加载器将其映射为读+写(而不是执行)。
将 .rodata
部分与 .text
分开的目的是将代码和数据组合在一起。 许多 CPU 已拆分 L1d 和 L1i 缓存,and/or 为数据/指令分离 TLB,因此在拆分缓存中将只读数据与代码浪费进行细粒度混合 space。
在您的情况下,您没有 link 任何其他也有一些代码和一些数据的文件,所以没有区别。
我正在努力学习 ARM 汇编。
写完这个 Hello World 小程序后:
.global _start
.text
_start: ldr R1,=msgtxt
mov R2,#13
mov R0,#1
mov R7,#4
svc 0
mov R7,#1
svc 0
.data
msgtxt: .ascii "Hello World!\n"
.end
我注意到我可以删除 .text 和 .data 指令,程序也能正常工作。
因此我很好奇:我读到的所有内容都强调了一个事实,即 .text 部分将用于代码,.data 部分用于数据。可在这里,在我的眼前,他们似乎什么都不做!
因此,如果这些不是用来分别保存代码和数据,那么它们的真正用途是什么?
这些类型的指令取决于您正在构建程序的体系结构,它们会选择将哪个内存部分分配给后面的任何代码或数据。最后,一切都只是一串字节。汇编程序后,symbols/labels 将根据它们所在的部分分配不同的内存地址。
.text
一般分配在只读内存段,最适合不希望改变的代码。
.data
通常是内存的可写部分。我相信如果预计不会更改(或者体系结构可能具有类似的只读段),将字符串放在代码数据旁边的 .text
中是很常见的。我会说 .data
部分甚至 避免 大多数时候。为什么?因为 .data
部分需要初始化——在程序启动时从程序二进制文件复制到内存中。您的程序引用的大多数数据都可以是只读的,并且它们操作所需的任何内存通常只分配给 .bss
段,该段分配一段未初始化的内存。
在同一节中混合代码和数据有一些优点,例如可以轻松访问与 PC 寄存器(正在执行的代码的地址)有相对偏移的数据地址。当然也有缺点,如果你试图修改只读内存,你最终至少会忽略你的操作,并且程序可能会触发异常并崩溃。所有这些都是特定于体系结构的,最安全的做法是将代码保存在用于代码的段中,data/allocations 保存在用于数据的段中。
这一切都非常具体地针对您的计划目标。例如,Game Boy Advance 有一个 256KB "slow" 内存区域,一个 32KB "fast" 内存区域,然后是只读 "ROM" 区域(游戏卡带数据),它可以是几个兆字节,汇编程序使用了这些内存部分:
.data or .iwram -> Internal RAM (32KB)
.bss -> Internal RAM uninitialized
.ewram -> External RAM (256KB)
.sbss -> External RAM uninitialized
.text or .rodata -> Read only ROM (cartridge size)
再举个例子,SPC-700(SNES 声音芯片)有 64KB 的可读写内存,用于所有内容,但它的前 256 个字节访问速度更快("zero page") .在这种理论上的情况下,.data
和 .text
将被分配到相同的内存区域——也就是说,它们不会分配在零页中,并且它们都共享相同的内存。零页将有一个自定义段,.text
和 .data
之间的差异很小 - 只是一种区分汇编程序中哪些符号指向 "data" 的方法哪些符号指向程序代码。
GAS(与大多数 assemblers 一样)默认为 .text
部分,您的只读数据在 .text
中仍然有效
一切都是字节
您可以将 echo 'mov r1, #2' > foo.s
和 assemble+link 转换为 ARM 二进制文件(
gcc -nostdlib -static foo.s
例如)。您可以在 GDB 中单步执行该指令。
(如果没有 sys_exit
系统调用,你的程序会在那之后崩溃,但当然你也可以在没有任何指令的情况下这样做。)
linker 会警告它没有找到 _start
符号(因为你遗漏了标签本身,更不用说告诉 .globl
指令 assembler 使其在目标文件的符号 table.
但是 GNU binutils ld
的默认设置是使用 .text
部分的开头作为 ELF 入口点。
除 .text
以外的大多数部分默认情况下不会 link 进入 executable 内存,因此 _start:
在 .data
中通常是一个问题。
只读数据通常应放在 .rodata
部分中,该部分无论如何都会作为 TEXT 段的一部分 link 编辑。因此,就运行时行为而言,将它放在 .text
部分 的末尾(通过省略 .data
)几乎完全等同于你应该做的。
What's the difference of section and segment in ELF file format
将其放入 .data
会导致 linker 将其放入不同的段,告诉 OS 的 ELF 程序加载器将其映射为读+写(而不是执行)。
将 .rodata
部分与 .text
分开的目的是将代码和数据组合在一起。 许多 CPU 已拆分 L1d 和 L1i 缓存,and/or 为数据/指令分离 TLB,因此在拆分缓存中将只读数据与代码浪费进行细粒度混合 space。
在您的情况下,您没有 link 任何其他也有一些代码和一些数据的文件,所以没有区别。