[C.C++] 【散文诗】C语言的本质(基于ARM深入分析C程序)

501 0
Honkers 2025-3-6 19:51:36 来自手机 | 显示全部楼层 |阅读模式

1. ARM架构

  1. 程序编译后生成 .bin、.hex文件,(汇编代码)烧入Flash中。
  2. 启动设备,程序在Flash中一条一条执行。
  3. 程序告诉CPU执行操作,如分配内存、分配栈、计算。
  4. CPU操作,如从内存中某个地址读写数据、开辟空间,GPIO的读写等。

  1. CPU运行时,先去取得指令,再执行指令:
  2. ① 把内存a的值读入CPU寄存器R0
  3. ② 把内存b的值读入CPU寄存器R1
  4. ③ 把R0、R1累加,存入R0(对于数据的运算是在cpu内部执行)
  5. ④ 把R0的值写入内存a
复制代码

ARM通用寄存器及其别名

R#APCS别名意义
R0a1参数/结果/scratch 寄存器1
R1a2参数/结果/scratch 寄存器2
R2a3参数/结果/scratch 寄存器3
R3a4参数/结果/scratch 寄存器4
R4v1arm 状态局部变量寄存器1
R5v2arm 状态局部变量寄存器2
R6v3arm 状态局部变量寄存器3
R7v4 / wrarm 状态局部变量寄存器4 / thumb状态工作寄存器
R8v5arm 状态局部变量寄存器5
R9v6 / sbarm 状态局部变量寄存器6 / 在支持RWPI的ATPCS中作为静态基址寄存器
R10v7 / slarm 状态局部变量寄存器7 / 在支持数据栈检查的ATPCS中作为数据栈限制指针
R11v8 / fparm 状态局部变量寄存器8 / 帧指针
R12ip内部过程调用 scratch寄存器
R13sp栈指针
R14lr链接寄存器
R15pc程序计数器


基本汇编指令

LDR

读内存指令,即Load之意,加载寄存器,表示读4个字节,可以加后缀,B:LDRB表示读1个字节, H:LDRH表示读2个字节。

LDR r0,[r3]

  1. r3为一个地址,去r3这个地址上读数据放入r0中
复制代码

STR

写内存指令,即Store之意,存储的意思,可以加后缀,B:STRB表示写1个字节,STRH:2个字节

STR r0,[r3]

  1. r3为一个地址,把r0上的数据存入r3的地址上
复制代码

ADD

加指令,不涉及内存操作即不用访问地址,只在cpu内部来实现

ADD r0,r1,r2

  1. r0等于r1加r2
复制代码

SUB

减指令,不涉及内存操作即不用访问地址,只在cpu内部来实现

ADD r0,r1,r2

  1. r0等于r1减r2
复制代码

BL

跳转指令,即Branch And Link,跳转到标号地址,并将返回地址保存在 LR 中,R14(LR)来存放当前子程序的返回地址,此指令有两个作用,第一记录返回地址(下一条指令的地址),保存在R14(LR),第二执行函数。

BL my_main

  1. 先记录my_main下一条指令的地址保存到R14中(LR),再执行my_main
复制代码

PUSH

入栈指令(栈指针由高地址往低地址指,如内存为0x2000000~0x201000,设栈指针为0x201000)

PUSH {r3,lr}

  1. 本条语句指将寄存器r3和lr中的值(用来存放当前子程序的返回地址)写入内存栈中,将lr写入sp-4地址,r3写入sp-8地址。
  2. 注:本质是调用写内存指令STR,将r3和lr寄存器中的值写入内存中去,高标号寄存器写入高地址的栈里,低标号寄存器写入低地址的栈里。
  3. 先将sp=sp-4,再将lr寄存器的值放进去(即将lr中的值放入sp所指的内存地址,如sp一开始的地址为0x201000,则0x2000FFC地址上的值为lr寄存器中的值),再sp=sp-4,将r3寄存器中的值写入sp里(r3存至0x2000FF8)。
复制代码


POP

出栈指令(栈指针由高地址往低地址指,如内存为0x2000000~0x201000,设栈指针为0x201000)

PUSH {r3,pc}

  1. 本条语句是将取出内存栈中地址sp中的值放入r3寄存器中,sp+4中的值放入pc寄存器中
  2. 注:本质是调用读内存指令LDR,高标号寄存器的内容来自高地址的栈,低标号寄存器的内容来自低地址的栈,先读出内存栈地址为sp的内存中的值存入r3寄存器,再sp=sp-4,读出sp中的值存入pc寄存器中。
复制代码


MOV

传送指令

MOV r0,#0x1C8

  1. 将0x1C8存入r0寄存器中
复制代码

2. 局部变量的分配与初始化

变量

  1. 全局变量
  2. 局部变量
  3. 局部静态变量

局部变量初始化

C语言

  1. void my_main(void)
  2. {
  3. int a = 456;
  4. }
复制代码

汇编

  1. // 执行my_main()
  2. PUSH {r3,lr} //进入函数,寄存器r3、lr的值,都存入内存的栈中(lr保存程序返回地址)
  3. // 执行 int a = 456
  4. MOV r0,#0x1C8 //0x1c8 = 456
  5. STR r0,[sp,#0x00]
复制代码


3. 全局变量、静态变量初始化

在调用main函数之前,使用copy、SetZero函数对全局变量、静态变量初始化


4. 栈和堆


一块空闲内存,可以使用malloc/free函数来管理


堆的malloc函数简单实现

  1. volatile char my_buf[20*1024];
  2. volatile int index = 0;
  3. void *malloc(int size)
  4. {
  5. char *ret = &my_buf[index];
  6. index += size;
  7. return ret;
  8. }
复制代码

5. 函数是什么

函数就是一些列的机器吗

调用函数

调用函数就是让CPU的PC寄存器等于“一系列机器码”的首地址,就是函数地址

往内存地址上拷贝函数

如函数为16条指令码,往0x20008000的地址上拷贝函数

  1. int add_val(int v){
  2. int a = v;
  3. a++;
  4. return a;
  5. }
  6. void copy_add_val_to_ram(void){
  7. unsigned char *src = (lunsigned char *)add_val;
  8. unsigned char *dest = (unsigned char *)0x20008000;
  9. for(int i = 0; i < 16; i++){
  10. dest[i] = src[i];
  11. }
  12. /*
  13. 等价于 ⬆⬆⬆⬆⬆⬆
  14. unsigned char *src;
  15. unsigned int val = (unsigned int)add_val;
  16. unsigned char *dest = (unsigned char *)0x20008000;
  17. src = (unsigned char *)val;
  18. for(int i = 0; i < 16; i++){
  19. dest[i] = src[i];
  20. }
  21. */
  22. }
  23. int main(void)
  24. {
  25. int (*p)(int );
  26. int a = 0;
  27. p = (int(*)(int))0x20008000;
  28. a = p(1);
  29. printf("a = %d\n",a); //a = 2;
  30. }
复制代码

函数怎么传递参数

在进入函数前,会把函数的参数值拷贝到r0寄存器中,后续在函数内使用参数都是直接从r0寄存器中操作

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4008
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行