4 内嵌汇编规则、ATPCS规则、内存操作指令

day4.txt

file a.out //查看a.out的基本信息(运行平台)


readelf -h a.out //查看elf格式文件的64字节头信息


objcopy -O binary a.out a.bin //a.out的elf文件格式转换位 a.bin


objdump -d a.out //将a.out的elf反汇编


strip a.out //删除a.out的标号信息

 

====================================================================

GNU内嵌汇编规则:在c语言中需要改变寄存器数值时使用内嵌汇编

__asm__ __volatile__(
  “mov r0, #12\n” //指令域,所有的指令必须以“”包裹,必须以'\n'结束,可以是多条语句
  : “=&r”(val) //输出变量声明域:(实现从汇编到c语言变量的数据传递) “=”代表变量是只写“r”编译工具需要给变量分配寄存器 “&”给变量分配的寄存器是之前没有使用过的 c的变量必须以()包裹
  ://输入变量声明域
  :“r0” //改变声明域,本次汇编修改的寄存器
          );

 

内嵌汇编如果改变了寄存器的数据,就必须声明改变,否则会出现未知错误

mov.c

复制代码
 1 #include <stdio.h>
 2 
 3 int main()
 4 {
 5     int val;
 6     
 7     __asm__( //内嵌汇编的规则
 8             "mov r0, #1\n\t" //r0 = 1
 9             "mov r0, #11\n\t"//r0 = 11
10             "mov %0, r0\n\t" //val = r0 %0表示c语言的val变量在汇编程序中的引用
11             :"=r&"(val)//输出变量声明域
12             ://输入变量声明域
13             :"r0"    //改变声明域
14     );
15 
16     printf("val = %d\n", val);
17 
18     return 0;
19 }
复制代码

 

===========================================================================================================================

 

APCS规则:汇编调用C程序的规则

1.寄存器命名规则:r0-r3 (a1-a4);

         r4-r11(v1-v8);  r11:fp;  r12 :ip;

           r13:sp;  r14:lr;   r15:pc 。
2.行参传递:行参少于4个使用r0-r3,多于4个时前4个使用r0-r3,其余使用栈。
3.函数返回值传递:使用r0。
4.局部变量分配:r4-r11。

--------------------------------------------------------------------------------------------------------------------------------

【转】ATPCS规则 

ATPCS即ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)的简称。

  PCS规定了应用程序的函数可以如何分开地写,分开地编译,最后将它们连接在一起,所以它实际上定义了一套有关过程(函数)调用者与被调用者之间的协议。

  为了使C语言程序和汇编程序之间能够互相调用,必须为子进程间的调用制定规则,在ARM处理器中,该规则被称为ATCPS:ARM程序和Thumb程序中子进程调用的规则。

  基本ATPCS规定了在子程序调用时的一些基本规则,包括下面3方面的内容:
 
  1.寄存器的命名/使用规则。
  2.数据栈的使用规则。
  3.参数传递的规则。
 
1、ATPCS下ARM寄存器的命名: 
。R0 a1 工作寄存器
R1 a2 工作寄存器
R2 a3 工作寄存器
R3 a4 工作寄存器
。R4 v1 必须保护;局部变量寄存器
R5 v2 必须保护;局部变量寄存器
R6 v3 必须保护;局部变量寄存器
R7 v4 必须保护;局部变量寄存器
R8 v5 必须保护;局部变量寄存器
R9 v6 必须保护;局部变量寄存器
R10 sl  v7 栈限制
R11 fp  v8 帧指针
。R12 ip 指令指针 
R13 sp 栈指针
R14 lr 连接寄存器
     
 
寄存器的使用必须满足下面的规则:
 
   1).子程序间通过寄存器R0一R3来传递参数,这时,寄存器R0~R3可以记作A1-A4。被调用的子程序在返回前无需恢复寄存器R0~R3的内容。
 
   2).在子程序中,使用寄存器R4~R11来保存局部变量.这时,寄存器 R4 ~ R11可以记作V1 ~ V8。如果在子程序中使用到了寄存器V1~V8中的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器则不必进行这些操作。在Thumb程序中,通常只能使用寄存器R4~R7来保存局部变量。
 
  3).寄存器R12用作过程调用时的临时寄存器(用于保存SP,在函数返回时使用该寄存器出栈), 记作ip。在子程序间的连接代码段中常有这种使用规则。
 
  4).寄存器R13用作数据栈指针,记作sp。在子程序中寄存器R13不能用作其他用途。寄存器sp在进入子程序时的值和退出子程序时的值必须相等。
 
  5).寄存器R14称为连接寄存器,记作lr。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14则可以用作其他用途。
 
  6).寄存器R15是程序计数器,记作pc。它不能用作其他用途。
 
 
 2、堆栈使用规则:ATPCS规定堆栈为FD类型,即满递减堆栈。并且堆栈的操作是8字节对齐。而对于汇编程序来说,如果目标文件中包含了外部调用,则必须满足以下条件: (1)、外部接口的数据栈一定是8位对齐的,也就是要保证在进入该汇编代码后,直到该汇编程序调用外部代码之间,数据栈的栈指针变化为偶数个字; (2)、在汇编程序中使用PRESERVE8伪操作告诉连接器,本汇编程序是8字节对齐的. 3、参数的传递规则:

     根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可变的子程序.这两种子程序的参数传递规则是不同的.

     1.参数个数可变的子程序参数传递规则

     对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0~R3来进行参数传递,当参数超过4个时,还可以使用数据栈来传递参数.      在参数传递时,将所有参数看做是存放在连续的内存单元中的字数据。然后,依次将各名字数据传送到寄存器R0,R1,R2,R3; 如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈.       按照上面的规则,一个浮点数参数可以通过寄存器传递,也可以通过数据栈传递,也可能一半通过寄存器传递,另一半通过数据栈传递.
     2.参数个数固定的子程序参数传递规则

     对于参数个数固定的子程序,参数传递与参数个数可变的子程序参数传递规则不同,如果系统包含浮点运算的硬件部件。      浮点参数将按照下面的规则传递:       (1)各个浮点参数按顺序处理;      (2)为每个浮点参数分配FP寄存器;        分配的方法是,满足该浮点参数需要的且编号最小的一组连续的FP寄存器.第一个整数参数通过寄存器R0~R3来传递,其他参数通过数据栈传递.

     3、子程序结果返回规则

     1.结果为一个32位的整数时,可以通过寄存器R0返回.

     2.结果为一个64位整数时,可以通过R0和R1返回,依此类推.

     3.结果为一个浮点数时,可以通过浮点运算部件的寄存器f0,d0或者s0来返回.

     4.结果为一个复合的浮点数时,可以通过寄存器f0-fN或者d0~dN来返回.

     5.对于位数更多的结果,需要通过调用内存来传递.
=================================================================================================================================

内存操作指令:
单寄存器操作:
ldr/str

从内存加载数据到寄存器 ldr r0, [r1] ==== r0 = *r1
保存寄存器的数值到内存地址 str r0, [r1] ==== *r1 = r0

标号寻址:ldr r0, =fun //将fun标号的地址存储到r0
ldr r0, fun //取出fun标号地址处的内容
****************************************
fun:
.word 0x1111
在标号fun处申请一个字的空间并且初始化为0x1111
****************************************
立即数寻址:
前索引:ldr r0, [r1, #4] === r0 = *(r1 + 4) r1 = r1 ,地址偏移后再进行内存操作,基地值不改变
str r0, [r1, #4] === *(r1+4) = r0 r1的数值不变
后索引: ldr r0, [r1], #4 === r0=*r1 r1=r1+4
str r0, [r1], #4 === *r1=r0 r1=r1+4
变址索引:ldr r0, [r1, #4]! === r0=*(r1+4) r1=r1+4

寄存器寻址:
前索引:ldr r0, [r1, r2] === r0 = *(r1+r2) r1不变
str r0, [r1, r2] === *(r1+r2) = r0 r1不变
后索引:ldr r0, [r1], r2 === r0=*r1 r1=r1+r2
str r0, [r1], r2 === *r1=r0 r1=r1+r2
变址索引:ldr r0, [r1, r2]! === r0=*(r1+r2) r1=r1+r2

ldrb/strb ldrh/strh ldrd/strd

多寄存器操作:
ldm/stm
ldm r0, {r1,r2,r3} === r1=*r0 r2=*(r0+4) r3=*(r0+8) r0不变
stm同ldm

ldm r0!, {r1-r3} ===r1=*r0 r2=*(r0+4) r3=*(r0+8) r0=r0+8

ldmia/stmia 先进行内存操作后地址+4 /*一般都是用*/
ldmda/stmda 先进行内存操作后地址-4
ldmib/stmib 先地址+4,后进行内存操作
ldmdb/stmdb 先地址-4,后进行内存操作

多寄存器操作规定:
小地址对小编号寄存器,大地址对大编号寄存器

特殊地址sp

满栈:sp指向的空间是不能直接使用的
空栈:sp指向的空间是可以直接使用的
减栈:栈的生长方向地址减小
增栈:栈的生长方向地址增大

满减栈也可以使用:
stmdb sp!,{r0-r12,lr}
ldmia sp!,{r0-r1,pc}
******************************************************
栈操作指令:
ldmfd/stmfd 满减栈操作
ldmed/stmed 空减栈操作
ldmfa/stmfa 满增栈操作
ldmea/stmea 空增栈操作

stmfd sp!, {r0-r12, lr} //入栈
ldmfd sp!, {r0-r12, pc}^ //出栈 "^"代表需要进行模式切换

push {r0-r12} //入栈
pop {r0-r12} //出栈
栈操作一般使用来进行保存现场
*********************
指令中的“!”代表
更新基地址
*********************
******************************************************

 

posted @   bkycrmn  阅读(1362)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示