lec 02 arm汇编语言基础

news/2024/11/14 1:24:48/文章来源:https://www.cnblogs.com/mumujun12345/p/18541591

Lecture 02: ARM 汇编基础

Contents

  • 为什么学习ARM/ISA汇编
  • 从C到汇编
  • 理解arm汇编
  • 理解机器执行

1 为什么学习汇编和指令集架构?

1.令人困惑的应用表现

困惑

2.指令集架构ISA(Instruction Set Architecture)

  • CPU向软件(应用程序和操作系统)提供的接口。
  • 理解软件在CPU上的运行(OS设计,程序调试)。
  • 操作系统包含体系结构相关的汇编代码。
  • 操作系统启动代码(栈没有设置)
  • 部分操作C语言无法表达。(e.g.获取系统状态,刷新TLB)
  • 部分场景下汇编更加高效(e.g. memcpy)

2.2 从C语言到汇编

1.为什么硬件不能直接运行C

  • 硬件设计
    (1)高级语言表达能力很强
    (2)硬件理解高级语言复杂度过高难以高效设计。
  • 机器指令
    (1)格式相对固定
    (2)功能相对简单
    (3)二进制编码

2.编译过程
link
hex
二进制文件难以理解->汇编较为适合阅读。

2.3 理解arm汇编

  1. 在完成程序编写后,程序被储存在磁盘中。
  2. OS加载程序,将其放入内存,CPU中的PC指向当前需要执行的第一个汇编指令。每执行一个指令,PC=PC+4.
  3. 数据刚开始存储在磁盘。后来会加载到内存当中。CPU中具有特殊的存储单元:寄存器,用于临时存储数据。可以用load/store指令来搬运数据。
    general reg
    vector reg

2.4 常用汇编

2.4.1 数据搬运

mov

2.4.2 算术指令

arith

2.4.3 移位指令

mov

2.4.4 逻辑运算指令

logic

2.4.5 Modified Register

  1. z=z*48分解成z=z*3与z=z*16,这样可以采用右移,减少运行时间。(浮点乘法消耗时间远大于位移)
    alt
  2. Modified register 优势
    alt
  3. 对操作数进行移位/位扩展。
    alt

2.4.6 访存指令

alt

2.4.7 内存结构

1.CPU视角下的内存:

  • 内存可以被视为一个很大的字节数组。
  • 数组每个元素可以由唯一的地址来索引。

2.内存地址

  • 内存数组的名称计为M。M[addr]为addr开始的内存单元的内容。addr为内存数组的索引。内存单元大小由上下文决定。
  • addr的具体格式由寻址模式决定。

3.寻址模式
(1)基地址模式(索引寻址)

  • \([r_b]\)

(2)基地址+偏移量

  • \([r_b,\text{offset}]\)

(3)前索引寻址(寻址操作前更新基地址)

  • \([r_b,\text{offset}]!\)\(r_b+=\text{offset}\),寻址\(M[r_b]\)

(4)后索引寻址(寻址操作后更新基地址)

  • \([r_b],\text{offset}\) 寻址\(M[r_b]\);\(r_b+=\text{offset}\).

(5)offset可以是

  • 立即数 #imm
  • 64位通用寄存器\(r_i\)
  • 修改过的寄存器。例如:移位运算lsl #3,位扩展sxtw

(4)example:

alt

2.4.8 条件码,分支指令

alt

标签:.L3,.L1
分支指令 .cbz,bne

alt

1.条件码

  • 一组标识位的统称。
  • 由PSTATE寄存器维护。
  • N(Negative),Z(zero),C(carry),V(overflow)
  • 条件码保留之前相关指令的执行状态,其中有
  • 带有s后缀的算术/逻辑指令(subs,adds)
  • 比较指令

2.条件码的设置

  • 第一类:通过s后缀数据处理指令隐式设置。
adds Rd, Rn, Op2

相当于t=a+b

  • C:运算产生进位时设置。
  • Z:当t=0时被设置。
  • N:当t<0时被设置。
  • V:当运算产生有符号溢出时被设置。
(a>0 && b>0 && t<0) || (a<0 && b<0 && t>=0)
  • 第二类:通过比较指令cmp显式设置。
cmp src1, src2

计算src1-src2,不存储结果,只改变条件码。

  • C:运算不产生借位时设置。
  • Z:当操作数相等时被设置。
  • N:当src1<src2时被设置。
  • V:当运算产生有符号溢出时被设置。

3.跳转条件
alt

4.跳转指令

  • 直接分支指令
  • 标签对应地址作为跳转目标
  • 无条件分支指令:b <label>
  • 有条件分支指令:bcond <label>, bcond=beq,bne,ble,...
  • 间接分支指令
  • 寄存器中地址作为跳转目标。br reg

alt

2.5 函数调用

2.5.1 函数调用=无条件跳转

alt

2.5.2 基本概念

  • 术语:
  1. Caller 调用者
  2. Callee 被调用者

alt

2.5.3 函数调用与返回指令

函数调用bl <label>,返回ret

alt

函数调用

  • 指令:
  1. bl <label> 直接调用函数
  2. blr Rn 间接调用,调用函数指针。
  • 功能:
  1. 将返回地址存储在链接寄存器x30
  2. 跳转到被调用者的入口地址。

返回指令

  • 指令
  1. ret 不区分直接调用和间接调用。
  • 功能
  1. 跳转到返回地址X30

alt

2.5.4 多级函数调用

alt

  • [ ]一级:cube调用square
  • cube中的bl指令将返回地址保存在LR(X30)中
  • square中的ret指令返回到LR(X30)记录的地址
  • [ ]二级:cube调用square,square调用foo
  • LR首先存储了square返回cube的地址
  • 嵌套调用时发生覆盖:LR(X30)存储foo返回square的地址

2.5.5 函数栈帧

  • 栈桢:函数在运行期间使用的一段内存

  • 生命周期:从被调用到返回前

  • 作用:存放其局部状态,包括:
    (1) 存放返回地址
    (2) 存放上一个栈桢的位置
    (3) 存放局部变量

  • 多级函数调用

  • 例如,A调用B、B调用C

  • 程序执行中存在多个未返回的函数

  • 函数栈桢按照调用顺序排列
    (1) 先被调用者后返回,后被调用者先返回
    (2) 栈:先进后出,后进先出

  • CPU中的另一个特殊寄存器SP
    SP: Stack Pointer
    指向栈顶(低地址)

alt

2.5.6 函数调用返回过程中栈的变化

alt

alt

2.5.7 帧指针FP:X29寄存器

  • 栈桢回溯
  • 栈桢大小不一
  • 如何找到上一个栈桢(如调试)
    (1) 保存x29(上一个栈桢的SP)
    (2) 将当前SP写入x29(让callee能保存)
    alt

2.5.8 函数的调用,返回与栈

alt

2.6 函数参数与返回值

2.6.1 寄存器传递数据

  1. x0-x7寄存器传递前8个参数
  2. x0作为返回值

alt

2.6.2 传递数据

  • 调用者压到栈上的数据
  • 第8个之后的参数
  • 按声明顺序从右到左
    Why? 因为参数的数量无法确定。而编译器读取参数时只是读取sp指针以上的内存。因此可以得到每个参数的地址。反之则会导致每个参数的内存地址无法确定。
  • 所有数据对齐到8字节
  • 被调用者通过SP+

alt

举例说明:

void proc(long a1, long *a1p,int a2, int *a2p,short a3, short *a3p,char a4, char *a4p,char a5, char *a5p)
{*a1p += a1;*a2p += a2;*a3p += a3;*a4p += a4;*a5p += a5;
}void caller(long *n)
{proc (1,0x2000,3,0x4000,5,0x6000,7,0x8000,9,0xA000);
}

查看caller的汇编语言:

00000000000000a0 <_caller>:a0: ff c3 00 d1  	sub	sp, sp, #48a4: fd 7b 02 a9  	stp	x29, x30, [sp, #32]a8: fd 83 00 91  	add	x29, sp, #32ac: a0 83 1f f8  	stur	x0, [x29, #-8]b0: e9 03 00 91  	mov	x9, spb4: 28 01 80 52  	mov	w8, #9b8: 28 01 00 39  	strb	w8, [x9]bc: 08 00 94 d2  	mov	x8, #40960c0: 28 05 00 f9  	str	x8, [x9, #8]c4: 20 00 80 d2  	mov	x0, #1c8: 01 00 84 d2  	mov	x1, #8192cc: 62 00 80 52  	mov	w2, #3d0: 03 00 88 d2  	mov	x3, #16384d4: a4 00 80 52  	mov	w4, #5d8: 05 00 8c d2  	mov	x5, #24576dc: e6 00 80 52  	mov	w6, #7e0: 07 00 90 d2  	mov	x7, #32768e4: 00 00 00 94  	bl	0xe4 <_caller+0x44>e8: fd 7b 42 a9  	ldp	x29, x30, [sp, #32]ec: ff c3 00 91  	add	sp, sp, #48f0: c0 03 5f d6  	ret

2.7 寄存器保存

2.7.1 通用寄存器保存

  • 不同函数共享同一批通用寄存器
  • 因此能够通过寄存器传递参数和返回值
  • 然而,不同的函数对通用寄存器的使用会存在冲突 — 覆盖
  • 避免冲突的思路
  • 函数在使用某个寄存器之前保存该寄存器的值,返回前恢复
  • 保存在哪:函数栈桢中
  • 效率问题:有时候可能无需保存
    如:一个函数内不调用其他函数
    编译器会尽可能减少冗余保存的代码

2.7.2 寄存器使用约定

alt

  • 调用者保存的寄存器包括 X9~X15

  • 调用者在调用前按需(仅考虑自己是否需要)进行保存
    调用者在被调用者返回后恢复这些寄存器的值

  • 被调用者可以随意使用
    这些寄存器调用后的值可能发生改变

  • 被调用者保存的寄存器包括 X19~X28

  • 被调用者在使用前进行保存

  • 被调用者在返回前进行恢复

  • 调用者视角:这些寄存器的值在函数调用前后不会改变

alt

2.7.3 举例理解

0000000000000000 <square>:0:	1b007c00 	mul	w0, w0, w04:	d65f03c0 	ret0000000000000008 <cube>:8:	a9be7bfd 	stp	x29, x30, [sp, #-32]!	// 开辟栈帧,保留调用者的栈帧x29,保存返回地址x30c:	910003fd 	mov	x29, sp		// 当前帧的栈顶地址写入x2910:	f9000bf3 	str	x19, [sp, #16]	//被调用者使用前保存调用者或之前保留的数据14:	2a0003f3 	mov	w19, w0		// 使用数据参数18:	94000000 	bl	0 <square>1c:	1b137c00 	mul	w0, w0, w1920:	f9400bf3 	ldr	x19, [sp, #16]	// 从内存中恢复数据24:	a8c27bfd 	ldp	x29, x30, [sp], #32	// 返回弹栈28:	d65f03c0 	ret

2.8 局部变量

2.8.1 函数局部变量存放在函数栈桢中

  • 为什么不直接把局部变量存储在寄存器?
  • 寄存器数量有限
  • 数组和结构体等复杂数据结构
  • 局部变量可能需要寻址 (如&a)

2.8.2 局部变量

  • 局部变量的分配
  • 在分配栈帧时被一起分配
  • 局部变量的释放
  • 在返回前释放栈帧时释放
  • 局部变量通过SP相对地址引用
  • (例如ldr x1, [sp, #8])

2.8.3 小结

alt
alt
alt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/832227.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

看雪看雪看雪

看雪看雪看雪[攻防世界]看雪看雪看雪 分析 得到一个rar文件,里面有一张jpg 南方孩子羡慕按照图片隐写思路:属性 010editor(隐藏文件分离,宽高) stegslove 等解题 按照刚才思路,没有什么发现 回头看看题目“看雪看雪看雪”,好像有个东西叫雪隐写 后面看wp,其实属性里面有…

cf round 898 (div.4) E

建造水族馆 题目描述 你喜欢鱼,所以你决定建造一个水族馆。你有一块由 n 根柱子组成的珊瑚,其中 i 根柱子高 ai 个单位。之后,你将在珊瑚周围建造一个水族箱,具体如下:选择一个整数 h --水箱的高度。在水箱两侧建造高度为 h 的墙壁。 然后,在水箱中注满水,使每一列的高度…

【日记】居然把今天的应酬逃掉了(668 字)

正文今天副行长回来了。本来以为今晚又要应酬,结果跑掉了。嘿嘿。有一个企业的董事长听说他回来了,所以嚷嚷着要请客。而客户请吃饭的对象又只有客户经理,所以我和柜面主管两个人就溜了。办公室的人也没去。不过明天是全行内部性质的,估计溜不了了。能逃一次是一次吧,嘿嘿…

字节豆包发布新模型,AI 一句话 P 图;Google 正式推出 Vids,简单提示即可生成视频演示丨 RTE 开发者日报

开发者朋友们大家好:这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文章 」、「有看点的会议」,但内容仅代表编辑的…

解决DDD最大难题-如何划分领域

https://www.cnblogs.com/Can-daydayup/p/18528659前言 在.NET开发中,为了准确统计对应方法的执行时间,我们最常用的方式是手动使用 Stopwatch 来显式编写计时逻辑,但是假如你需要大量的使用 Stopwatch 来进行耗时统计的话不利于保持代码的整洁和增加代码的维护成本。项目介…

【VMware by Broadcom】新闻:VMware Workstation 和 Fusion 产品商业使用免费。

VMware Workstation Pro 和 VMware Fusion Pro 是 VMware Desktop Hypervisor 产品,允许用户在 Windows、Linux 或 Mac 计算机上运行虚拟机、容器和 Kubernetes 集群。我相信绝大部分人使用 VMware 的产品都是从这里开始的,我也不例外。前段时间,VMware 宣布将这两款产品永久…

Qt - 信号与槽的第五个参数

connent函数第五个参数的作用 connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) 第五个参数代表槽函数在哪个线程中执行 :自动连接(Qt::AutoConnection),默认的连接方式,如…

团队作业4——项目冲刺

团队作业4——项目冲刺这个作业属于哪个课程 <计科22级34班>这个作业要求在哪里 <作业要求>这个作业的目标 修改完善需求规格说明书、系统设计、Alpha任务分配计划、测试计划GitHub 链接 https://github.com/tangliweiwww/ChatGpt🍟一、团队 1.团队名称:Eleganc…

Maven打包项目的精准指定——流程管理

作用Maven不仅可以进行依赖管理的自动化,还可以自动化实现编译,打包,发布,等,也被成为构建流程 生命周期(lifecycle) 构成Maven生命周期本身可以看做一个集合,在这个集合中包含了一系列阶段(phase)。也就是说Maven的生命周期由一系列阶段(phase)构成但是话又说回来…

如何正确导入mapstruct,同时避免编译时mapstruct与lombok冲突

本文介绍编译时产生的冲突,导包时期产生的冲突请劳驾查找其他解决方法一般情况下只需要按照官网的导入即可,但如果同时使用了lombok,则需要小心。详细信息可以查看官网文档:MapStruct 1.5.5.Final 集成lombok注意:在编译测试的时候,一定先清理再编译。这样可以解决80%的报…

施耐德UNITY下使用ST编程计算最近一小时的均值

昨晚学习练习了ST语言做最近60秒的分钟均值,今天继续做最近一小时的均值,1秒采集一次数据。在昨晚程序上增加功能,新建一个导出的功能块类型Average_Hour,定义下面的变量:旗下新建一个程序段Average_Hour,使用ST编程 Minute1:=BCD_TO_INT(%SW51);Minute:=mod_int(Minute1…