首页 > 编程语言 >lec 02 arm汇编语言基础

lec 02 arm汇编语言基础

时间:2024-11-12 12:40:45浏览次数:1  
标签:02 00 lec 调用者 sp mov 指令 寄存器 arm

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, #48
      a4: fd 7b 02 a9  	stp	x29, x30, [sp, #32]
      a8: fd 83 00 91  	add	x29, sp, #32
      ac: a0 83 1f f8  	stur	x0, [x29, #-8]
      b0: e9 03 00 91  	mov	x9, sp
      b4: 28 01 80 52  	mov	w8, #9
      b8: 28 01 00 39  	strb	w8, [x9]
      bc: 08 00 94 d2  	mov	x8, #40960
      c0: 28 05 00 f9  	str	x8, [x9, #8]
      c4: 20 00 80 d2  	mov	x0, #1
      c8: 01 00 84 d2  	mov	x1, #8192
      cc: 62 00 80 52  	mov	w2, #3
      d0: 03 00 88 d2  	mov	x3, #16384
      d4: a4 00 80 52  	mov	w4, #5
      d8: 05 00 8c d2  	mov	x5, #24576
      dc: e6 00 80 52  	mov	w6, #7
      e0: 07 00 90 d2  	mov	x7, #32768
      e4: 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, #48
      f0: 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, w0
   4:	d65f03c0 	ret

0000000000000008 <cube>:
   8:	a9be7bfd 	stp	x29, x30, [sp, #-32]!	// 开辟栈帧,保留调用者的栈帧x29,保存返回地址x30
   c:	910003fd 	mov	x29, sp		// 当前帧的栈顶地址写入x29
  10:	f9000bf3 	str	x19, [sp, #16]	//被调用者使用前保存调用者或之前保留的数据
  14:	2a0003f3 	mov	w19, w0		// 使用数据参数
  18:	94000000 	bl	0 <square>
  1c:	1b137c00 	mul	w0, w0, w19
  20:	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

标签:02,00,lec,调用者,sp,mov,指令,寄存器,arm
From: https://www.cnblogs.com/mumujun12345/p/18541591

相关文章

  • 【2024-11-11】团建加班
    20:00人的存在之谜不在于他现在是什么,而在于他能够成为什么。                                                 ——A·J·赫舍尔周末何太公司组织部门团建,我带着二宝......
  • 代码静态测试工具Klocwork 2024.3新版发布:Validate平台改进编码标准CC++
    Klocwork2024.3为C/C++分析引擎和构建上传流程引入了新功能和性能改进。此版本还附带了增强的安全性和用户体验改进,包括用于SAML/OIDC身份验证的IDE插件中更好的用户身份验证工作流程。其他增强功能包括更广泛的编码标准覆盖范围以及改进的与Bazel构建系统的集成。Vali......
  • 【025A】基于51单片机多功能电子时钟【Proteus仿真+Keil程序+报告+原理图】
    ☆、设计硬件组成:51单片机最小系统+DS1302时钟芯片+LCD1602液晶显示+按键设置+蜂鸣器+LED灯。1、本设计采用STC89C52、AT89C52、AT89S52单片机作为主控芯片,并采用LCD1602进行实时显示信息;2、可以显示年月日,时分秒,星期,以及上/下午;3、可以设置闹钟,并且闹钟数据保存在AT24C......
  • 2024年CRM系统全球排名:国内外十大品牌深度比较
    当前,国内外CRM系统市场呈现出百花齐放的景象。种类繁多的CRM系统满足了不同企业的多样化需求。在国外,CRM系统发展较早且较为成熟。其发展历程经历了数据库营销时代、联系管理软件时代、自动化销售力支持系统时代、全方位CRM系统时代以及云计算和移动技术时代。国外市场上的......
  • 代码随想录算法训练营第四天(LeetCode24.两两交换链表中的节点;LeetCode10.删除链表的倒
    LeetCode24.两两交换链表中的节点题目链接:两两交换链表中的节点题目链接思路这道题其实就是一个模拟题,要求每次交换链表中两个相邻的节点(1、2节点互换;3、4节点互换;2、3节点不互换,意思就是交换过的节点不参与后续的交换了),同时只能进行节点交换,不能进行值交换。主要考......
  • 2024年10大主流设备管理系统盘点!核心功能、适用场景解读!
    设备管理系统在现代企业中扮演着至关重要的角色。随着企业规模的不断扩大和设备数量的增加,传统的设备管理方式已经无法满足企业的需求。设备管理系统可以帮助企业实现设备的全生命周期管理,从设备的采购、安装、调试到运行、维护、报废,都可以进行有效的管理和监控。同时,设备管理系......
  • 2024华为OD机试真题---中文分词模拟器
    华为OD机试中的中文分词模拟器题目,通常要求考生对给定的不包含空格的字符串进行精确分词。这个字符串仅包含英文小写字母及英文标点符号(如逗号、分号、句号等),同时会提供一个词库作为分词依据。以下是对这类题目的详细解析一、题目描述给定一个连续不包含空格的字符串Q,该字......
  • 基于HarmonyOS Next的医疗数据防泄漏与身份认证方案:Device Certificate Kit的深度应用
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。在医疗信息化快速发展的今天,医疗......
  • HarmonyOS Next企业级设备认证解决方案:基于Device Certificate Kit的多层级身份验证
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。在企业数字化转型的浪潮中,大量设......
  • HarmonyOS Next在智能家居领域的安全架构:设备身份认证与数据防泄漏方案
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。在智能家居蓬勃发展的今天,各种智......