arm底层奠定基础 (汇编)
arm底层汇编
目录
- 前言
- arm发展简介
- 32位 ARMv7
- 汇编指令
- 中断向量表
- 解惑
- armv8 64位
前言
在学习和工作中,遇到关于嵌入式程序运行的困惑,通过底层汇编,可以知道arm怎么做到保护现场、linux怎么做到安全运行和系统调用等。
arm发展简介
arm发展历程
指令集的设计是处理器结构中最重要的一个部分,用ARM的术语称之为ISA(Instruction Set Architecture)。根据不同的指令集进行区分,ARM11芯片之后,也就是从ARMv7架构开始,ARM的命名方式有所改变。新的处理器家族,改以Cortex命名,并分为三个系列,分别是Cortex-A,Cortex-R,Cortex-M。(A、R、M),比较熟悉,stm32F4是cortex-M4,imx6ull是cortex-A7。2011 年 10 月,arm公司推出armv8架构,从之前32位到64位,支持64位指令集,在内存、虚拟化和安全有了一定的提升。2021年,arm已经推出armv9。
一、32位 ARMv7
以cortex-M为例
ARM主要有7种基本工作模式,USER、FIQ、IRQ、Supervisor、Abort、Undef和System。如果cortex-A会多出2种,安全监控模式(mon):可在安全模式和非安全模式下转换;HYP虚拟化模式。
CPU的模式可以简单的理解为当前CPU的工作状态
arm寄存器示意图
1、bank寄存器
带有三角,表示bank 寄存器,该模式下独有的寄存器,没有带三角,表示各个模式共用这部分寄存器。
什么是寄存器?
存放数据的地方,cpu内部访问,读取速度最快。
2、特殊寄存器
r15 PC程序计数器(Program Counter),存储下一条要执行的指令的地址。
r14 LR 连接寄存器(Link Register ),保存函数返回地址,当通过BL或BLX指令调用函数时,硬件自动将函数返回地址保存在R14寄存器中。当函数完成时,将LR值传到PC,即可返回到被调用位置。
r13 SP 堆栈指针(Process Stack Pointer),保护现场和恢复现场要用,当发生异常的时候,硬件会把当前状态(使用到寄存器数值)保存在堆栈中,SP保存这个堆栈指针,异常处理完成,通过SP出栈,恢复到异常前的状态。
CPSR程序状态寄存器(current program status register),CPSR和其他寄存器不一样,其他寄存器是用来存放数据的,都是整个寄存器具有一个含义.而CPSR寄存器是按位起作用的,也就是说,它的每一位都有专门的含义。
CPSR位域寄存器
二、汇编指令
思考下C语言编译流程,预处理(preprocessing),对头文件和宏展开→编译(compilation),将展开后的程序转成汇编代码→汇编,将汇编代码转成零一二进制机器码,目标文件;链接,将多个目标文件链接成可执行的文件。
现在我们介绍汇编程序,汇编程序相比C语言,有执行效率高的特点,但是可读性差。汇编程序编译开始流程差不多是C语言汇编步骤的开始,此编译非彼编译。汇编语言,包括两种指令,一种是汇编指令,一种是伪指令。汇编指令有对应的机器码,伪指令并没有对应的机器码,最终不会被CPU执行,而是会被编译器所执行。
1、基础指令
;将立即数3 放在r13 注意:mov指令只能用于通用的寄存器
mov r13,#3
;cpsr是特殊寄存器
mrs r0,cpsr
msr cpsr,r0;相与
and r0,r0,#0xFFFFFFE0
;相或
orr r0,r0,#0x10;逻辑左移
mov r0,r1,LSL#2
mov r0,r1,LSR#2;比较指令
cmp r0,#0
;相等 r1 = 0
moveq r1,#0
;大于 r1 = r1 + 3
addgt r1,r1,#3;跳转到标号为main地代码处 (只能短跳转32M)
b main
;跳转函数func,并保存下一条要执行的指令的位置到 lr寄存器, 当跳转代码结束后,用mov pc,lr指令跳回来
bl func
;相等(指CPSR寄存器中的Z条件码置位时)时,跳转到地址addr处
beq addr
;不等时,跳转到地址addr
bne addr bic r0,r0,#0x0B ;清除r0中的位 0、1、和 3
tst r0,#0x20 ;测试第6位是否为0 ,为0则Z标志置1
cmp r1,r0 ;将寄存器R1的值与寄存器R0的值相减,;并根据结果设置CPSR的标志位
2、armv7汇编demo
.textb main nopnopnopnopnopnopnop
main: ldr r0,=bufldr r1,[r0]mov r2,#5str r2,[r0]str r2,[r0,#8]
main_end:b main_end
对应的机器码:
keil debug
数据不合法
;立即数不合法 因为机器码中只有低12位是存储数据位 借助伪指令,编译器进行编译
mov r3,#0x1101
;使用伪指令
ldr r3,=0x1101
;但是这条指令却是合法的
mov r3,#0x1100000;立即数合法性判断:循环移动到低8位能放下,并且移动次数是偶数次。
;使用伪指令可以避免,立即数合法判断。
机器码:
反汇编示意图
立即数合法性判断:循环移动到低8位能放下,并且移动次数是偶数次,0x1100000是0x11相左移动6次。
3、存储器之间不能直接拷贝,必须通过寄存器中转
ldr r0,[r7] ;4字节载入
ldrb r0,[r7] ;1字节载入
ldrh r0,[r7] ;2字节载入
ldr r0,[r7,#8] ;r7加上8后,载入对应地址的内容
ldr pc,_irq ;将标号中的内容载入,机器码
ldr pc,=irq ;将标号地址载入str r0,[r3]
str r0,[r3,#4]
str r0,[r3],#4
伪指令
.section //定义内存段
.text //将定义符开始的代码编译到代码段
.data //数据段
.if .else .endif //条件编译
.end //文件结束.byte 0x11, ’a’,0 //定义char型数组(.byte 对应1字节)
.word 0x12, 0x445566 //定义int型数组(. word 对应4字节)
.quad 0x3FA0 //分配8字节的空间(.quad 对应8字节)
.string “abcd\0” //定义字符串
.align 4 //2^4 =16 字节对齐ldr r0, =0xE0028008 //载入大常数0xE0028008 到r0中
.equ GPG3CON, 0XE03001C0 //定义宏
.global _start //声明_start 为全局符号
堆栈操作
stmfd sp!,{r0-r12,lr}
//将寄存器r0~r12 lr中的值存入栈中 常用于中断保护现场,! 表示会自动偏移 ldmfd sp!,{r0-r12,pc}^
//将栈中值逐个弹出到寄存器r0~r12 pc中 常用于中恢复断现场,^表示会恢复spsr到cpsr
三、中断向量表
指示中断服务程序的入口位置,中断向量的顺序是固化,不可改变。(是顺序,不是地址,起始地址可以变,偏移地址)
arm异常处理地址
当发生中断或者异常时,arm会自动跳到对应中断入口,由硬件完成。
以stm32启动文件为例,startup_stm32f10x.md.S
...
; Vector Table Mapped to Address 0 at ResetAREA RESET, DATA, READONLY ;只读数据段EXPORT __VectorsEXPORT __Vectors_EndEXPORT __Vectors_Size
;DCD 连续 一段连续空间 外部中断发生时,把要处理的动作放在EXTI0_IRQHandler中断服务函数
__Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault HandlerDCD BusFault_Handler ; Bus Fault HandlerDCD UsageFault_Handler ; Usage Fault HandlerDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCall HandlerDCD DebugMon_Handler ; Debug Monitor HandlerDCD 0 ; ReservedDCD PendSV_Handler ; PendSV HandlerDCD SysTick_Handler ; SysTick Handler; External InterruptsDCD WWDG_IRQHandler ; Window WatchdogDCD PVD_IRQHandler ; PVD through EXTI Line detectDCD TAMPER_IRQHandler ; TamperDCD RTC_IRQHandler ; RTCDCD FLASH_IRQHandler ; FlashDCD RCC_IRQHandler ; RCCDCD EXTI0_IRQHandler ; EXTI Line 0DCD EXTI1_IRQHandler ; EXTI Line 1......
.text;---------vector tableb main ;0x00 resetnop ;0x04 undef ldr pc,_sirq_hander ;0x08 soft irqnop ;0x0c prefecth abortnop ;0x10 data abortnop ;0x14 reservednop ;0x18 irqnop ;0x1C fiq
_sirq_hander:.word sirq_hander;----handler vector table
sirq_hander:stmfd sp!,{r0-r12,lr} ; 入栈 保护现场ldr r0,[lr,#-4] ; 获取到软件中断 号 是1还是2bic r0,#0xff000000cmp r0,#1 addeq r2,#1cmp r0,#2subeq r2,#1sirq_hander_end:ldmfd sp!,{r0-r12,pc} ;出栈 恢复现场;----------app----
main: ldr sp,=stack_buf ;保存栈地址mov r1,#1mov r2,#2swi 0x1 ;软中断 让linux陷入内核 中断号1cmp r2,#2moveq r4,#4movne r4,#6swi 0x2 ;中断号2mov r0,#3
main_end:b main_end.data.space 15*4 ;数据段
stack_buf: ;因为栈是向下增长,递减,这里要注意。.end
四、解惑
1、linux 上层最终怎么进入内核层?
通过系统调用,即swi指令软中断,进入异常,改变arm权限,获取访问硬件的权限。
2、中断怎么做到现场保护和恢复现场?
在进入中断,一般是user模式切换到异常模式(应用层到内核层),因为中断需要用到cpu的寄存器(有共用寄存器),为了不破坏原有的工作环境,会对前一个模式进行现场保护,会对各个寄存器值和代码段地址保存到栈中,记录栈指针到SP。当中断完成后,通过SP堆栈指针,出栈,恢复现场,恢复成之前的环境。
3、当man函数中,调用外部函数时,外部函数执行完,怎么返回到main函数继续执行后面代码?
LR寄存器保存函数返回地址。mov PC,LR。
4、快速中断为什么会比一般中断响应快?
1.FIQ的处理优先级比IRQ更高,甚至可以打断正在执行的IRQ;
2.FIQ模式有自己独有的寄存器,而IRQ需要和其他模式共用寄存器(共用寄存器多),在中断处理的保护/恢复现场会更快;
3.在异常向量表中,FIQ处在最末尾。在异常向量表中IRQ只能保存中断处理程序的首地址,在发生IRQ时需要一次跳转;而FIQ处在最末尾,所以可以直接将FIQ模式下的中断处理程序紧接着存放,这样在处理FIQ时就少一次跳转。
5、对于c语言 i = 1,在用汇编怎么实现的?
把1放在寄存器0中,将i地址载入到寄存器1中,最后将寄存器0数据存入到i地址。C语言一条语句可能对应多个汇编指令。这里可以联想为什么会有系统中会有锁和原子的机制,在上层一条语句其实对应底层多条指令,特别是对多线程而言,竞争访问同一资源的情况。
mov r0,#1
ldr r1,=i
str r0,[r1]
arm官网
armv8 64位
armv8相对armv7增加哪些?以下是arm社区回答
对比图
在内存、虚拟化和安全有了一定的提升,执行状态可在AArch64和AArch32来回切换,兼容之前32位arm指令集。
AArch64中,已经没有User、SVC、ABT等处理器模式的概念,但ARMv8需要向前兼容,在AArch32中,就把这些处理器模式映射到了4个Exception level。
异常类型:SError 系统错误,FIQ 快速中断,IRQ一般中断和Synchronous同步异常。
异常级别有,EL0、EL1和EL2和EL3,
armv8异常示例图
每个异常等级都有自己的异常向量表。异常向量表中的每一项都会保存有异常处理的跳转函数,然后跳转过去处理异常。每个向量表基虚拟地址是由矢量基址寄存器设置的,例如VBAR_EL3,VBAR_EL2和VBAR_EL1。
每个表有16个条目,每个条目的大小为128字节(32条指令)。ARMv8的向量表如下图所示,可以看到每一种异常都有固定的偏移地址。
SVC指令可以用来从EL0的用户应用程序调用到EL1的内核。HVC和SMC系统调用指令以类似的方式将处理器移动到EL2和EL3。当处理器在EL0(应用程序)执行时,它不能直接调用管理程序(EL2)或安全监视器(EL3)。
2、armv8基础汇编指令
b.ne label //不等时跳转
cbz w10, 1f //w10值等于0的适合,跳转导1f
ret //子程序返回指令,返回地址默认保存在LR(X30),代替了mov pc,lrldr x0,=__main //大范围的地址读取:把标号__main(地址)读入x0
adr x0,vector //小范围的地址读取:把标号vector(地址)读入x0,标号距当前指令PC的偏移小于1M
stp x29, x30, [sp, #-16]! //入栈:把x29, x30 存储到sp-16指向的空间后,sp自减16 (因寄存器是8字节,栈是向下生长故是 -16) //类似前索引: *(sp-16) = x29,x30 sp=sp-16 (!使得sp能自更新) 把 x29,x30看成整体 //stp只支持2个寄存器,代替了复杂的stmfd (64位汇编,取消了批量操作指令)
ldp x29, x30, [sp],#16 //出栈: 把sp指向的空间内容载入到x29, x30后,sp加16//类似后索引: x29,x30=*sp sp=sp+16 mrs x0, sctlr_el1 //读sctlr_el1内容到x0 (注:系统寄存器,都通过mrs msr来操作)
msr sctlr_el1, x0 //写x0内容到 sctlr_el1
svc #2 //系统调用指令(触发一个同步异常,cpu则会陷入EL1).global _start //声明_start 为全局符号(让链接脚本能看到)
.quad 0x3FA0 //在存储器中分配8个字节,初值设为0x3FA0
.align 4 //2^4 =16 字节对齐
.macro myAdd, x,y //宏函数, 类似 myAdd(x, y) add \x,\x,\y
.endm
myAdd x0,x2b.ne lable //不等时跳转到标号
cbz w10, 1f //w10值等于0的适合,跳转导1f
ret //子程序返回指令,返回地址默认保存在LR(X30),代替了mov pc,lr .macro myAdd, x,y //宏函数, 类似 myAdd(x, y) add \x,\x,\y
.endm
myAdd x0,x2
3、armv8中断向量表示例
.globl _start
_start:mrs x1,SPSelmrs x2,CurrentELmov x0,#0msr SPSel,x0mov x0, #0x5adr x0,vectorsmsr vbar_el1,x0svc #0x02 //系统调用 ,
reset_end:b reset_enddo_bad_sync:mov x2,#1b reset_enddo_bad_irq:mov x2,#2b reset_end.align 11 //2^11=2048 整个异常向量表 2K对齐 -> 通过对齐,实现向量表空间的预留//16个异常 ,每个异常32条指令 16*32*4=2048 //16个异常,这里使用前8个
vectors://===============sp0===============//---同步异常.align 7 //2^7 1000 0000 =0x80 字节对齐mov x0,#1b do_bad_sync//---irq异常.align 7 //2^7 1000 0000 =0x80mov x0,#1b do_bad_irq//---fiq异常.align 7 mov x0,#1b reset_end//---SError异常.align 7 mov x0,#1b reset_end//===============sp_elx===============//---同步异常.align 7 //2^7 1000 0000 =0x80 字节对齐mov x0,#1b do_bad_sync//---irq异常.align 7 //2^7 1000 0000 =0x80mov x0,#1b do_bad_irq//---fiq异常.align 7 mov x0,#1b reset_end//---SError异常.align 7 mov x0,#1b reset_end
如果有帮助,可以关注 小昭debug,有学习资料等你拿。
arm底层奠定基础 (汇编)相关推荐
- 操作系统内核Hack:(二)底层编程基础
操作系统内核Hack:(二)底层编程基础 在<操作系统内核Hack:(一)实验环境搭建>中,我们看到了一个迷你操作系统引导程序.尽管只有不到二十行,然而要完全看懂还是需要不少底层软硬件知识 ...
- 汇编语言基础--汇编操作指令概述
本文是接续"汇编语言基础--机器级数据存储",主要介绍汇编指令的构造.寻址和指令主要分类. 操作指令 指令的基本要素: 在"计算机处理器(CPU)基础&quo ...
- 15 计算机底层——二进制到汇编学习
计算机底层--二进制到汇编学习 1.概述 语言 机制 进制如何计算 二进制 数据宽度 有符号和无符号数 原码和反码 位运算 位运算计算 汇编 寄存器 内存 汇编指令 内存复制 堆栈的指令 汇编如何写函 ...
- Java(计算机底层——二进制到汇编)(先导课)
Java(计算机底层--二进制到汇编)(先导课) 参考视频:最通俗易懂的计算机底层教学,二进制到汇编学习!(狂神) 1. 概述 语言 进制 进制如何运算 二进制 数据宽度 有符号数和无符号数 原码反码 ...
- ARM 指令集的基础指令
ARM 和汇编语言基础 ARM 基础 位.字节.字是计算机数据存储的单位. 位是最小的存储单位,每一个位存储一个1位的二进制码,一个字节由8位组成. 而字通常为16.32或64个位组成.ARM 处理器 ...
- 清华校友斩获ACM博士论文奖!相关研究为自动驾驶新算法奠定基础
点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 明敏 萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 今 ...
- 【Brain】复旦类脑研究院:破解大脑奥秘,为实现人工智能自我思考奠定基础...
文章来源:新民晚报 图说:复旦类脑研究院 采访对象供图 理解大脑的结构与功能是21世纪最具挑战性的前沿科学问题,谁揭开大脑运作的神秘面纱,谁就在重大脑疾病防治和全球智能产业革命中抢占了先机.利用磁共振 ...
- Zilliqa联合创始人:ZIP-12提案已成功部署,为未来的治理投票奠定基础
Zilliqa联合创始人表示,关于治理的ZIP-12提案在治理投票成功后已成功部署,为未来的治理投票奠定基础. 文章链接:https://www.tuoluocaijing.cn/kuaixun/de ...
- 埃及通信和信息技术部使用 OpenText ECM 内容服务平台为电子政务转型奠定基础
埃及通信和信息技术部为电子政务转型奠定基础 IT 专家通过使用 OpenText™ Extended ECM 平台提供的安全.可扩展和面向未来的数字工作流程替代纸质流程,为服务创新做准备. 名称:埃及 ...
最新文章
- 番外:Spring MVC环境搭建和Mybatis配置避坑篇
- Redis的安装及使用
- composer php 使用方法,Composer的基本使用方法
- 【阿里云课程】卷积神经网络:结构单元、卷积层反向传播求解与典型模型
- POJ 3660 Cow Contest(传递闭包floyed算法)
- LeetCode——Find Minimum in Rotated Sorted Array II
- 学完Java后可从事的十大领域!
- 数据采集无线网服务器软件,WIFI智能数据盒
- oracle+erp+采购管理操作手册 - 图文 - 百度文库
- matlab的persistent,MATLAB局部静态变量类型persistent
- android点击按钮打开蓝牙,Android打开蓝牙的两种方式
- 关于美团、饿了么外卖优惠券公众号的设计思路
- 微信小程序 | canvas为你的天气预报添加雨雪效果
- mSystem:鸟枪法宏基因组测序之外我们还能做什么
- matlab做胶州湾地图,惊了,青岛也有了五环地图,快来看你家在几环?
- 利用用户行为数据——基于Spark平台的协同过滤实时电影推荐系统项目系列博客(二)
- 【Flask】篇一:Error: Failed to find Flask application or factory in module “学习用“. Use “FLASK_APP=学习用:nam
- 测量中的坐标与时间系统1.3(在大地测量学中)
- 2021-2027全球及中国散射光粉尘测量仪行业研究及十四五规划分析报告
- 国庆临近,字节后端开发3+4面,终于拿到秋招第一个offer