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

2023-11-22 12:30

本文主要是介绍【散文诗】C语言的本质(基于ARM深入分析C程序),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1. ARM架构
    • ARM通用寄存器及其别名
    • 基本汇编指令
      • LDR
      • STR
      • ADD
      • SUB
      • BL
      • PUSH
      • POP
      • MOV
  • 2. 局部变量的分配与初始化
    • 局部变量初始化
  • 3. 全局变量、静态变量初始化
  • 4. 栈和堆
      • 堆的malloc函数简单实现
  • 5. 函数是什么
    • 调用函数
    • 往内存地址上拷贝函数
    • 函数怎么传递参数

1. ARM架构

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

在这里插入图片描述

CPU运行时,先去取得指令,再执行指令:① 把内存a的值读入CPU寄存器R0② 把内存b的值读入CPU寄存器R1③ 把R0、R1累加,存入R0(对于数据的运算是在cpu内部执行)④ 把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]

r3为一个地址,去r3这个地址上读数据放入r0中

STR

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

STR r0,[r3]

	r3为一个地址,把r0上的数据存入r3的地址上

ADD

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

ADD r0,r1,r2

	r0等于r1加r2

SUB

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

ADD r0,r1,r2

	r0等于r1减r2

BL

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

BL my_main

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

PUSH

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

PUSH {r3,lr}

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

在这里插入图片描述


POP

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

PUSH {r3,pc}

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


MOV

传送指令

MOV r0,#0x1C8

	将0x1C8存入r0寄存器中

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

变量

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

局部变量初始化

C语言

void my_main(void)
{int a = 456;
}

汇编

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

在这里插入图片描述


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

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


4. 栈和堆


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


堆的malloc函数简单实现

volatile char my_buf[20*1024];
volatile int index = 0;void *malloc(int size)
{char *ret = &my_buf[index];index += size;return ret;
}

5. 函数是什么

函数就是一些列的机器吗

调用函数

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

往内存地址上拷贝函数

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

int add_val(int v){int a = v;a++;return a;
}void copy_add_val_to_ram(void){unsigned char *src = (lunsigned char *)add_val;unsigned char *dest = (unsigned char *)0x20008000;for(int i = 0; i < 16; i++){dest[i] = src[i];}/*等价于 ⬆⬆⬆⬆⬆⬆unsigned char *src;unsigned int val = (unsigned int)add_val;unsigned char *dest = (unsigned char *)0x20008000; src = (unsigned char *)val;for(int i = 0; i < 16; i++){dest[i] = src[i];}*/
}int main(void)
{int (*p)(int );int a = 0;p = (int(*)(int))0x20008000;a = p(1);printf("a = %d\n",a);		//a = 2;
}

函数怎么传递参数

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

这篇关于【散文诗】C语言的本质(基于ARM深入分析C程序)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/K3169/article/details/127327458
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/410168

相关文章

基于Go语言实现Base62编码的三种方式以及对比分析

《基于Go语言实现Base62编码的三种方式以及对比分析》Base62编码是一种在字符编码中使用62个字符的编码方式,在计算机科学中,,Go语言是一种静态类型、编译型语言,它由Google开发并开源,... 目录一、标准库现状与解决方案1. 标准库对比表2. 解决方案完整实现代码(含边界处理)二、关键实现细

如何合理管控Java语言的异常

《如何合理管控Java语言的异常》:本文主要介绍如何合理管控Java语言的异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍2、Thorwable类3、Error4、Exception类4.1、检查异常4.2、运行时异常5、处理方式5.1. 捕获异常

C语言中的常见进制转换详解(从二进制到十六进制)

《C语言中的常见进制转换详解(从二进制到十六进制)》进制转换是计算机编程中的一个常见任务,特别是在处理低级别的数据操作时,C语言作为一门底层编程语言,在进制转换方面提供了灵活的操作方式,今天,我们将深... 目录1、进制基础2、C语言中的进制转换2.1 从十进制转换为其他进制十进制转二进制十进制转八进制十进

$在R语言中的作用示例小结

《$在R语言中的作用示例小结》在R语言中,$是一个非常重要的操作符,主要用于访问对象的成员或组件,它的用途非常广泛,不仅限于数据框(dataframe),还可以用于列表(list)、环境(enviro... 目录1. 访问数据框(data frame)中的列2. 访问列表(list)中的元素3. 访问jav

Java使用WebView实现桌面程序的技术指南

《Java使用WebView实现桌面程序的技术指南》在现代软件开发中,许多应用需要在桌面程序中嵌入Web页面,例如,你可能需要在Java桌面应用中嵌入一部分Web前端,或者加载一个HTML5界面以增强... 目录1、简述2、WebView 特点3、搭建 WebView 示例3.1 添加 JavaFX 依赖3

防止SpringBoot程序崩溃的几种方式汇总

《防止SpringBoot程序崩溃的几种方式汇总》本文总结了8种防止SpringBoot程序崩溃的方法,包括全局异常处理、try-catch、断路器、资源限制、监控、优雅停机、健康检查和数据库连接池配... 目录1. 全局异常处理2. 使用 try-catch 捕获异常3. 使用断路器4. 设置最大内存和线

使用Python创建一个功能完整的Windows风格计算器程序

《使用Python创建一个功能完整的Windows风格计算器程序》:本文主要介绍如何使用Python和Tkinter创建一个功能完整的Windows风格计算器程序,包括基本运算、高级科学计算(如三... 目录python实现Windows系统计算器程序(含高级功能)1. 使用Tkinter实现基础计算器2.

C语言中位操作的实际应用举例

《C语言中位操作的实际应用举例》:本文主要介绍C语言中位操作的实际应用,总结了位操作的使用场景,并指出了需要注意的问题,如可读性、平台依赖性和溢出风险,文中通过代码介绍的非常详细,需要的朋友可以参... 目录1. 嵌入式系统与硬件寄存器操作2. 网络协议解析3. 图像处理与颜色编码4. 高效处理布尔标志集合

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

C 语言中enum枚举的定义和使用小结

《C语言中enum枚举的定义和使用小结》在C语言里,enum(枚举)是一种用户自定义的数据类型,它能够让你创建一组具名的整数常量,下面我会从定义、使用、特性等方面详细介绍enum,感兴趣的朋友一起看... 目录1、引言2、基本定义3、定义枚举变量4、自定义枚举常量的值5、枚举与switch语句结合使用6、枚