什么时候需要 GAS ELF 指令 .type、.thumb、.size 和 .section?
Posted
技术标签:
【中文标题】什么时候需要 GAS ELF 指令 .type、.thumb、.size 和 .section?【英文标题】:When are GAS ELF the directives .type, .thumb, .size and .section needed? 【发布时间】:2011-05-24 07:39:44 【问题描述】:我正在使用 GNU 作为基于 ARM Cortex-M3 的微控制器(Thumb 2 指令集)的汇编程序。
在一些示例代码中,我发现像 .size
、.section
和 .type
这样的指令是 ELF 指令。举个例子:
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
bl main
b Infinite_Loop
.size Reset_Handler, .-Reset_Handler
.type
指令用于设置符号的类型 - 通常为 %object(表示数据?)或 %function。我不知道它有什么不同。它并不总是包括在内,所以我不确定何时需要使用它。
与此相关的还有.thumb_func
指令。根据我的阅读,它似乎可能相当于:
.thumb
.type Symbol_Name, %function
或者是完全不同的东西?
.size
应该设置与符号关联的大小。什么时候需要,我不知道。这是默认计算的,但可以用这个指令覆盖吗?如果是这样 - 你什么时候想要覆盖?
.section
更容易在上面找到文档,而且我想我对它的作用有一个大致的了解,但我仍然有点不确定它的用法。根据我的理解,它在不同的 ELF 部分之间切换(text
用于代码,data
用于可写数据,bss
用于未初始化数据,rodata
用于常量等),并在需要时定义新的部分。我猜你会根据你是否定义代码、数据、未初始化的数据等在这些之间切换。但是为什么要为函数创建一个小节,就像上面的例子一样?
对此的任何帮助表示赞赏。如果您能找到更详细地解释这一点的教程或文档的链接——最好是新手可以理解的——我将不胜感激。
到目前为止,Using as 手册已经提供了一些帮助 - 也许您可以比我从中获得更多知识,了解更多。
【问题讨论】:
我已经为这个问题添加了一个赏金,希望得到更详细的答案,特别是关于 .type 和 .size 指令。 这应该根据指令分成一个问题。建议:了解 ELF 格式,然后制作带有和不带有每个指令的最小示例,编译并在它们上readelf -a
。
【参考方案1】:
程序的各个部分与大多数系统(Linux、BSD 等)存储其对象和可执行文件的 ELF 格式密切相关。 This article 应该可以让您深入了解 ELF 的工作原理,这将帮助您理解部分的原因。
简单地说,section 可以让你将程序组织到不同的内存区域中,这些内存区域具有不同的属性,包括地址、执行和写入权限等。在最后的链接阶段,链接器使用特定的linker script,通常将所有将同名的部分放在一起(例如,来自所有编译单元的所有代码放在一起,...)并为它们分配内存中的最终地址。
对于嵌入式系统,它们的使用特别明显:首先,引导代码(通常包含在.text
部分中)必须加载到固定地址才能执行。然后,可以将只读数据分组到一个专用的只读部分,该部分将映射到设备的 ROM 区域。最后一个例子:操作系统的初始化函数只被调用一次,之后就不再使用,浪费了宝贵的内存空间。如果将所有这些初始化函数组合成一个专用部分,例如.initcode
,并且如果将此部分设置为程序的最后一部分,那么操作系统可以在初始化完成后通过降低自己内存的上限。例如,众所周知 Linux 会使用该技巧,而 GCC 允许您通过使用 __attribute__ ((section ("MYSECTION")))
后缀将变量或方法放置到特定部分中
.type
和 .size
实际上我也不清楚。我将它们视为链接器的助手,并且从未在汇编程序生成的代码之外看到它们。
.thumb_func
似乎只需要用于旧的 OABI 接口,以允许与 Arm 代码交互。除非您使用的是旧工具链,否则您可能不必担心。
【讨论】:
我想这至少清除了部分。定义自己的部分是否有特殊原因? .thumb_func 如果你获取函数的地址是必要的。对于 Thumb 函数,其地址必须设置位 0(与数据项地址不同)。该指令使汇编器将符号标记为这样,因此链接器知道何时设置位 0。【参考方案2】:多年来,我一直在编写 arm/thumb 的许多汇编程序,并且需要的指令很少。
.thumb_func 正如另一位响应者指出的那样非常重要。
例如
.globl _start _开始: b 重置 重置: 。手臂 .globl 一 一: 添加 r0,r0,#1 bx lr 。拇指 .globl 二 二: 添加 r0,r0,#2 bx lr .thumb_func .globl 三 三: 添加 r0,r0,#3 bx lr .字二 .word 三.arm 或曾经是 .code32 或 .code 32 之类的东西告诉它这是 arm 代码而不是拇指代码,对于您的 cortex-m3,您不需要使用它。
.thumb 同样,以前是 .code 16 或者可能仍然有效,同样的处理使以下代码拇指不支持。
如果您使用的标签不是需要从其他文件或间接分支到的全局标签,则不需要 .thumb_func。但是为了正确计算这些全局标签之一的分支地址(lsbit 是拇指的 1 和手臂的 0),您希望将其标记为拇指或手臂标签,thumb_func 会这样做,否则您必须在分支添加更多代码之前设置该位,并且标签不可从 C 调用。
00000000 <_>: 0: eaffffff b 4 00000004 : 4: e2800001 添加 r0, r0, #1 8:e12fff1e bx lr 0000000c : c: 3002 添加 r0, #2 e: 4770 bx lr 00000010 : 10: 3003 添加 r0, #3 12:4770 bx lr 14: 0000000c andeq r0, r0, ip 18: 00000011 andeq r0, r0, r1, lsl r0直到 .thumb 为止,汇编器都是根据需要的 arm 代码。
两个和三个标签/功能都是所需的拇指代码,但两个标签具有偶数地址,三个标签具有正确的奇数地址。
使用最新的代码源工具来组装、链接和转储上述示例。
现在对于一切都是 thumb(/thumb2) 的 cortex-m3,thumb_func 可能不那么重要了,它可能只适用于命令行开关(很容易做一个实验来找出)。不过,如果您从仅使用拇指的处理器转移到普通的手臂/拇指内核,这是一个好习惯。
汇编程序通常喜欢添加所有这些指令和其他使事物看起来/感觉更像高级语言的方法。我只是说你不必使用它们,我为 arm 切换了汇编器,并为许多不同的处理器使用了许多不同的汇编器,并且更喜欢少即是多的方法,这意味着专注于组装本身并尽可能少地使用特定于工具的项目.我通常是例外而不是规则,因此您可以通过查看编译器输出生成的指令(并通过文档验证)来找出更常用的指令。
无符号整数一(无符号整数 x) 返回(x+1); .arch armv5te .fpu 软vfp .eabi_attribute 20, 1 .eabi_attribute 21, 1 .eabi_attribute 23, 3 .eabi_attribute 24, 1 .eabi_attribute 25, 1 .eabi_attribute 26, 2 .eabi_attribute 30, 2 .eabi_attribute 18, 4 .file“鲍勃.c” 。文本 .对齐 2 .global 一 .type 一,%function 一: .fnstart .LFB0: @args = 0,假装 = 0,帧 = 0 @frame_needed = 0,使用_anonymous_args = 0 @链接寄存器保存消除。 添加 r0, r0, #1 bx lr .fnend .size 一个,.-one .ident“GCC:(Sourcery G++ Lite 2010.09-50)4.5.1” .section .note.GNU-stack,"",%progbits我确实在将 arm 和 thumb 汇编器或数据与汇编器混合时使用 .align,您会希望此类平台的汇编器知道一些显而易见的东西,例如 thumb 指令在半字边界上,而 arm 指令在字边界上对齐.这些工具并不总是那么聪明。洒上 .aligns 不会有什么坏处。
.text 是默认值,因此有点多余,但不会造成伤害。 .text 和 .data 是标准属性(不是特定于 arm),如果您在目标上编译 rom 和 ram 的组合,您可能会关心(取决于您对链接器脚本的操作),否则 .text 将适用于所有内容.
.size 显然是该指令开始的函数的大小。汇编器不能自己解决这个问题,所以如果这个函数的大小对你的代码、链接器脚本、调试器、加载器很重要,那么这需要是正确的,否则你不必费心。无论如何,函数是一个高级概念,汇编程序实际上并没有函数,更不用说声明它们的大小了。 C 编译器当然不在乎,它只是在寻找要分支到的标签,在 arm 系列的情况下,它是要分支到的 thumb 代码还是 arm 代码。
如果您在很长一段代码上对立即数 (ldr rx,=0x12345678) 很懒惰,您可能会发现 .pool 指令(有一个较新的等效指令)很有用。同样,这些工具并不总是足够聪明,无法将这些数据放在无条件分支之后,您有时会告诉他们。我说的懒惰一半是认真的,一直做 label: .word 的事情很痛苦,而且我相信 arm 和 gcc 工具都允许使用该快捷方式,所以我和其他人一样使用它。
另请注意,llvm 会向 binutils 输出一个或两个额外的 .eabi_attribute,这些 .eabi_attribute 受 code sourcery 的 version/mods 支持,但 gnu 发布的 binutils 不支持(可能还没有)。两种可行的解决方案,修改 llvm 的 asm 打印函数以不编写 eabi_attributes 或至少用注释 (@) 编写它们,或者从代码源中获取 binutils 源/模块并以这种方式构建 binutils。代码源倾向于引导 gnu(例如,thumb2 支持)或者可能向后移植新功能,所以我假设这些 llvm 属性很快就会出现在主线 binutils 中。通过修剪 llvm 编译代码中的 eabi_attributes,我没有受到任何不良影响。
这是上面相同函数的 llvm 输出,显然这是我修改以注释掉 eabi_attributes 的 llc。
.syntax 统一 @ .eabi_attribute 20, 1 @ .eabi_attribute 21, 1 @ .eabi_attribute 23, 3 @ .eabi_attribute 24, 1 @ .eabi_attribute 25, 1 @ .eabi_attribute 44, 1 .file “bob.bc” 。文本 .globl 一 .对齐 2 .type一,%函数 一一 @BB#0: @%entry 添加 r0, r0, #1 bx lr .ltmp0: .size 一个,.Ltmp0-oneelf 文件格式有据可查,如果您想真正了解 elf 特定指令(如果有的话)在做什么,那么它很容易解析。其中许多指令对链接器的帮助比什么都大。例如 .thumb_func、.text、.data。
【讨论】:
谢谢,这肯定有帮助。所以除非我们在讨论特殊情况,否则 .size 和 .type 都不是必需的? 我从未使用过 .size 或 .type ,而且我的大部分工作都是在手臂上完成的,并且总是涉及到汇编程序。我假设如果您的链接器脚本或加载器/精灵解析器想要查找这些东西,那么您需要它们,我使用非常简单的链接器脚本,它真的只关心 .text 与 .data,基本上是 rom 与 ram。 关于 .size 指令:在仿生的 android git 树中,提交 fb723c87490b76d1d2fe521886f7cb6c96ed40b7 说:update the ARM syscalls with the BEGIN(x) and END(x) macros to give size information for the code of the syscall. Useful for valgrind.
【参考方案3】:
我在试图弄清楚为什么 ARM 和 Thumb 互通与最近的 binutils(已通过 2.21.53 (MacPorts) 和 2.22 (Yagarto 4.7.1) 验证)中断的原因时遇到了这个问题。
根据我的经验,.thumb_func
与早期的 binutils 配合得很好,可以生成正确的互通胶合板。然而,随着最近的发布,
需要.type *name*, %function
指令来确保正确生成单板。
binutils mailing list post
我懒得挖掘旧版本的 binutils 来检查 .type
指令是否足以代替早期 binutils 的 .thumb_func
。我想在你的代码中包含这两个指令没有害处。
已编辑:更新了关于在代码中使用 .thumb_func
的注释,显然它适用于 ARM->Thumb 互通来标记 Thumb 例程以生成单板,但 Thumb->ARM 互通失败,除非使用 .type
指令标记 ARM 函数。
【讨论】:
以上是关于什么时候需要 GAS ELF 指令 .type、.thumb、.size 和 .section?的主要内容,如果未能解决你的问题,请参考以下文章
Gnu Assembler (GAS) 中的 CFI 指令有啥用途?
为啥包装在函数中的 GAS 内联汇编为调用者生成的指令与纯汇编函数不同