什么时候需要 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-one

elf 文件格式有据可查,如果您想真正了解 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:内存引用过多

什么是APP-假如我要写一门app的语言

为啥包装在函数中的 GAS 内联汇编为调用者生成的指令与纯汇编函数不同

1分钟搞清Gas/ Gas Price/ Gas Limit

使用 GAS AT&T 指令计算引导扇区的填充长度?