19

我正在使用 GNU 作为基于 ARM Cortex-M3 的微控制器(Thumb 2 指令集)的汇编程序。

在一些示例代码中,我找到了类似的指令,.size我理解的是 ELF 指令。举个例子:.section.type

    .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更容易找到文档,我想我对它的作用有一个很好的了解,但我仍然有点不确定它的用法text我理解它的方式,它在不同的 ELF 部分(用于代码、data可写数据、bss未初始化数据、常量等)之间切换rodata,并在需要时定义新部分。我猜你会根据你是否定义代码、数据、未初始化的数据等在这些之间切换。但是为什么要为函数创建一个小节,就像上面的例子一样?


对此的任何帮助表示赞赏。如果您能找到更详细地解释这一点的教程或文档的链接——最好是新手可以理解的——我将不胜感激。

到目前为止,作为手册的使用已经提供了一些帮助——也许你可以比我得到更多的知识,更多的知识。

4

3 回答 3

15

多年来,我一直在为 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 代码。

两个和三个标签/功能都是所需的拇指代码,但两个标签具有偶数地址,三个标签具有正确的奇数地址。

最新的 codesourcery 工具用于组装、链接和转储上述示例。

现在对于一切都是 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 about 不会受到伤害。

.text 是默认值,所以有点多余,但不会受到伤害。.text 和 .data 是标准属性(不是特定于 arm),如果您在目标上编译 rom 和 ram 的组合,您可能会关心(取决于您对链接器脚本的操作),否则 .text 将适用于所有内容.

.size 显然是该指令开始的函数的大小。汇编器不能自己解决这个问题,所以如果这个函数的大小对你的代码、链接器脚本、调试器、加载器很重要,那么这需要是正确的,否则你不必费心。无论如何,函数是一个高级概念,汇编程序实际上并没有函数,更不用说声明它们的大小了。C 编译器当然不在乎,它只是在寻找要分支到的标签,在 arm 系列的情况下,它是要分支到的拇指代码还是 arm 代码。

如果您在很长一段代码上对立即数 (ldr rx,=0x12345678) 很懒惰,您可能会发现 .pool 指令(有一个较新的等效指令)很有用。同样,这些工具并不总是足够聪明,无法将这些数据放在无条件分支之后,您有时会告诉他们。我说懒惰一半是认真的,一直做标签是痛苦的: .word 事情,我相信 arm 和 gcc 工具都允许使用该快捷方式,所以我和其他人一样使用它。

还要注意 llvm 输出一个或两个额外的 .eabi_attribute ,它由 code sourcery 的 version/mods 支持到 binutils 但不被 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。

于 2010-12-14T23:44:28.367 回答
7

您的程序部分与大多数系统(Linux、BSD 等)存储其对象和可执行文件的 ELF 格式密切相关。本文应该让您深入了解 ELF 的工作原理,这将帮助您理解部分的原因。

简而言之,节让您将程序组织到具有不同属性的不同内存区域,包括地址、执行和写入权限等。在最后的链接阶段,链接器使用特定的链接描述文件,该脚本通常将相同的所有部分分组一起命名(例如,来自所有编译单元的所有代码一起,...)并为它们分配内存中的最终地址。

对于嵌入式系统,它们的使用特别明显:首先,引导代码(通常包含在.text节中)必须加载到固定地址才能执行。然后,可以将只读数据分组到一个专用的只读部分,该部分将映射到设备的 ROM 区域。最后一个例子:操作系统的初始化函数只被调用一次,之后就不再使用,浪费了宝贵的内存空间。如果所有这些初始化函数都被组合成一个专用部分,例如,.initcode,并且如果将此段设置为程序的最后一段,那么一旦初始化完成,操作系统就可以通过降低自身内存的上限来轻松回收这块内存。例如,众所周知 Linux 会使用该技巧,而 GCC 允许您通过将变量或方法添加到特定部分中__attribute__ ((section ("MYSECTION")))

.type实际上我也.size不清楚。我将它们视为链接器的助手,并且从未在汇编程序生成的代码之外看到它们。

.thumb_func似乎只需要旧的 OABI 接口才能允许与 Arm 代码互通。除非您使用的是旧工具链,否则您可能不必担心它。

于 2010-12-14T07:06:10.303 回答
6

当我试图弄清楚为什么 ARM 和 Thumb 互通与最近的 binutils (通过 2.21.53 (MacPorts) 和 2.22 (Yagarto 4.7.1) 验证)中断时,我遇到了这个问题。

根据我的经验,.thumb_func使用早期的 binutils 可以很好地生成正确的互通单板。然而,在最近的版本中,需要该 .type *name*, %function指令来确保正确生成单板。

binutils 邮件列表帖子

我懒得挖掘旧版本的 binutils 来检查该.type指令是否足以代替.thumb_func早期的 binutils。我想在代码中包含这两个指令没有害处。

已编辑:更新了代码中使用的注释.thumb_func,显然它适用于 ARM->Thumb 互通以标记 Thumb 例程以生成胶合板,但 Thumb->ARM 互通失败,除非该.type指令用于标记 ARM 函数。

于 2012-09-05T03:52:16.817 回答