【ARM64 常见汇编指令学习 12 -- ARM 汇编函数 的学习】


上篇文章:ARM64 常见汇编指令学习 11 – ARM 汇编宏 .macro 的学习
下篇文章:ARM64 常见汇编指令学习 13 – ARM 汇编 ORG 伪指令学习

1.1 ARM 汇编函数简介

ARM汇编中的函数定义并不像高级语言那样有特定的语法,但通常可以通过 标签(label)子程序调用指令 (如BL,BLX) 来实现类似于函数的功能。

例如,下面的代码定义了一个名为 my_function 的 “函数”,它接受一个参数(通过寄存器r0传递),将其值增加 1后返回:

my_function: 
  add r0, r0, #1 
  bx lr

在这里,my_function 是一个标签,表示这个函数的入口点。add r0, r0, #1是函数的主体,它将寄存器 r0 的值增加 1。最后,bx lr是函数的退出语句,它将执行权返回给调用者。

这个函数可以通过 BL 指令来调用,例如:

mov r0, #5 
bl my_function

在这里,mov r0, #5 将值5加载到寄存器r0中,然后bl my_function跳转到my_function标签处执行代码,同时将返回地址(即下一条指令的地址)保存到链接寄存器lr中。

需要注意的是,这只是最基本的情况。在实际的ARM汇编代码中,函数可能会更复杂,包括更多的寄存器操作、保存和恢复现场、处理函数调用堆栈等。

1.1.1 ARM 汇编标签 Label

在ARM汇编中,标签(Label)是用来标记代码中某一位置的一种机制。标签后面通常跟着一个冒号(:),然后在其后可以是一条或多条汇编指令。我们可以使用标签来作为跳转指令(如b,beq等)的目标,或者作为数据的引用。

例如下面的代码:

start: 
  mov r0, #10 
  add r1, r0, #5 

loop: 
  subs r0, r0, #1 
  bne loop

在这个代码中,"start"和"loop"就是两个标签。我们可以看到,在"loop"标签后面,有一个循环,它会一直执行,直到r0的值为0为止。

注意,不能在两个不同的地方定义相同名字的标签。而且,标签名是大小写敏感的,也就是说,"Loop"和"loop"是两个不同的标签。

1.1.2 ARM 汇编函数属性指令

在 ARM 汇编中,“.global”,“.section”,".type"都是汇编器指令,它们用于指示汇编器如何处理随后的汇编代码。

  • .global”:它用于声明一个全局标签,也就是说这个标签可以在其他的汇编文件中引用。例如,".global my_func"声明了一个名为my_func的全局标签。

  • .section”:它用于指定接下来的代码或数据应该放在哪个段(section)中。例如,“.section .text"指定接下来的代码应该放在名为".text"的段中。在链接过程中,链接器会将同名的段合并在一起。在”.section"指令后面通常会跟着一个段名,以及一些可选的段属性,如 ,“ax” 段属性,表示这个段是可以执行的(‘x’)和可以读写的(‘a’)。

  • .type”:它用于指定一个符号(通常是一个标签)的类型,这对链接器解析符号的方式有所影响。例如,“.type my_func, %function"指定 my_func 是一个函数类型的符号。”%function"是 GNU汇编器的一个预定义符号类型,表示这个符号是一个函数。这对链接器以及某些调试工具是有用的,它们可以通过这个类型信息来正确地处理这个符号

以下是一个简单的函数定义的例子:

.global    my_func 
.type      my_func, %function 
.section   .text 

my_func: 
  mov r0, #1 
  bx lr

在这个例子中,我们定义了一个名为 my_func 的全局函数,这个函数在.text段中,函数的功能是将1放入r0寄存器,然后返回。

1.1.3 UEFI 中的函数宏:ASM_FUNC

见:edk2/ArmPkg/Include/AsmMacroIoLibV8.h

#define _ASM_FUNC(Name, Section)    \
  .global   Name                  ; \
  .section  #Section, "ax"        ; \
  .type     Name, %function       ; \
  Name:

#define ASM_FUNC(Name)  _ASM_FUNC(ASM_PFX(Name), .text. ## Name)

#Section 中的 # 的作用是讲将 Section 字符串化,就是将 _ASM_FUNC 中的 .text 转换成字符串;

1.1.3.1 UEFI ASM_FUNC 实现汇编函数
//x0 postcode value
ASM_FUNC (PostCode_S)
  mov x24, x1
  mov x1, #0x87000000
  str x0,[x1]
  mov x1, x24
  ret

1.1.4 UEFI 预编译前缀宏 ASM_PFX

关于宏 ASM_PFX 的定义如下(edk2/MdePkg/Include/Base.h):

//
// For symbol name in assembly code, an extra "_" is sometimes necessary
//

///
/// Private worker functions for ASM_PFX()
///
#define _CONCATENATE(a, b)   __CONCATENATE(a, b)
#define __CONCATENATE(a, b)  a ## b

///
/// The __USER_LABEL_PREFIX__ macro predefined by GNUC represents the prefix
/// on symbols in assembly language.
///
#define ASM_PFX(name)  _CONCATENATE (__USER_LABEL_PREFIX__, name)

从上面的定义可以看出最后是给 ASM_PFX(name) 中的 name 加了一个 __USER_LABEL_PREFIX__ 前缀。

GNU C中,“__USER_LABEL_PREFIX__” 是一个预定义宏,它的值代表了在该编译环境中用户定义的标签前缀。

在某些平台和编译器中,用户定义的函数和变量在汇编层面需要添加一个前缀。例如,在许多 UNIX 系统中,用户定义的标签需要添加一个下划线"_"作为前缀。

例如,如果你在 C 代码中定义了一个函数"my_function",那么在生成的汇编代码中,这个函数的名称会变成"_my_function"。

在这种情况下,“__USER_LABEL_PREFIX__“的值就会被定义为”_”。然而,在不需要添加前缀的环境中,"__USER_LABEL_PREFIX__"的值就会被定义为空。

这个宏在处理跨平台代码时很有用,特别是需要直接编写汇编代码时,可以通过这个宏来正确地引用C函数或变量,避免平台差异带来的问题。

例如:

void my_function(void); 
asm(".global " __user_label_prefix__ "my_function");

这段代码在需要添加前缀的环境中会被扩展为".global _my_function",而在不需要添加前缀的环境中会被扩展为".global my_function"。

在 UEFI 的代码edk2/MdePkg/Include/AArch64/ProcessorBind.h中可以看到 __USER_LABEL_PREFIX__ 定义为空:

#ifndef __USER_LABEL_PREFIX__
#define __USER_LABEL_PREFIX__
#endif

上篇文章:ARM64 常见汇编指令学习 11 – ARM 汇编宏 .macro 的学习
下篇文章:ARM64 常见汇编指令学习 13 – ARM 汇编 ORG 伪指令学习

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

主公CodingCos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值