ARM 64 协程切换上下文的汇编代码解读

2023-11-07

ARM 64协程切换上下文的汇编代码解读

贺志国
2023.8.11

在ARM 64位架构中,有一组通用寄存器(General Purpose Registers)、一组浮点寄存器(Floating-point Registers)和一组特殊寄存器(Special Registers)。

通用寄存器(x0-x30)是用于存储数据和计算结果的寄存器。这些寄存器可以用于存储整数、指针和地址等数据。其中,寄存器x0-x7有特殊用途,例如x0通常用于存储函数的返回值,x1-x7用于传递函数参数。

与通用寄存器类似,浮点寄存器也有一定的编号规律。在ARM 64位架构中,浮点寄存器从d0到d31,共有32个。这些寄存器可以用于存储和进行浮点数计算。ARM 64位架构支持SIMD(Single Instruction Multiple Data)指令集,用于高效地进行向量化计算。在汇编语言中,可以使用特定的寄存器名称来引用浮点寄存器,例如d0表示浮点寄存器0,d8表示浮点寄存器8等。在编写ARM 64位汇编代码时,可以使用这些浮点寄存器进行浮点数的加载、存储和运算操作。

特殊寄存器包括:
程序计数器(Program Counter,PC):存储下一条将要执行的指令的地址。
栈指针(Stack Pointer,SP):指向当前栈的顶部。
链接寄存器(Link Register,LR):存储函数调用前的返回地址。
状态寄存器(Condition Flags):存储比较和算术操作的结果。
程序状态寄存器(Program Status Register,PSR):存储处理器的运行状态和控制位。

上述寄存器在汇编语言中通过特定的寄存器名称来引用,例如x0代表通用寄存器0,d8表示浮点寄存器8,sp代表栈指针寄存器,pc代表程序计数器等。

以下是一段ARM 64协程切换上下文的汇编代码:

.text;                             ; 以下是代码段
.align 4                           ; 按2^4=16字节的倍数对齐地址,空隙默认用0来填充
.globl ctx_swap                    ; 全局入口函数是ctx_swap

; 以下单个寄存器和内存变量的空间均为64位(8字节)
ctx_swap:                          ; 定义全局函数ctx_swap
	stp    x0,   x30, [sp,#-16]!   ; 将x0, x30的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    d8,   d9, [sp,#-16]!    ; 将d8, d9的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    d10,  d11, [sp,#-16]!   ; 将d10, d11的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    d12,  d13, [sp,#-16]!   ; 将d12, d13的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    d14,  d15, [sp,#-16]!   ; 将d14, d15的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x1,   x19, [sp,#-16]!   ; 将x1, x19的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x20,  x21, [sp,#-16]!   ; 将x20, x21的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x22,  x23, [sp,#-16]!   ; 将x22, x23的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x24,  x25, [sp,#-16]!   ; 将x24, x25的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x26,  x27, [sp,#-16]!   ; 将x26, x27的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x28,  x29, [sp,#-16]!   ; 将x28, x29的值存储到sp - 16的地址上,并且执行sp -= 16

	mov    x3,   sp                ; 将sp的值传送到x3
	str    x3,   [x0]              ; 将x3的值存储到x0的地址上

	ldr    x3,   [x1]              ; 从x1的地址里取一个64位数存储到x3中
	mov    sp,   x3                ; 将x3的值传送到sp

	ldp    x28,  x29,  [sp]        ; 从sp的地址里取出2个64位的数(共16字节),分别存入x28, x29
	ldp    x26,  x27,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x26, x27,并且执行sp += 16
	ldp    x24,  x25,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x24, x25,并且执行sp += 16
	ldp    x22,  x23,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x22, x23,并且执行sp += 16
	ldp    x20,  x21,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x20, x21,并且执行sp += 16
	ldp    x1,   x19,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x1, x9,并且执行sp += 16
	ldp    d14,  d15,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入d14, d15,并且执行sp += 16
	ldp    d12,  d13,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入d12, d13,并且执行sp += 16
	ldp    d10,  d11,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入d10, d11,并且执行sp += 16
	ldp    d8,   d9,   [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入d8, d9,并且执行sp += 16
	ldp    x0,   x30,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x0, x30,并且执行sp += 16

	add    sp,   sp,   #16         ; sp += 16

	ret                            ; 从当前函数返回,寻找x30上的地址并执行其中的指令

这是一段协程上下文切换函数,它将当前CPU寄存器值保存到内存栈的一个区域中,然后从内存栈的另一个区域中恢复之前保存的CPU寄存器值并返回。

context

以下这段代码是将当前的寄存器x0, x30, d8, d9, d10, d11, d12, d13, d14, d15, x1, 19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29的值保存到内存栈中。

	stp    x0,   x30, [sp,#-16]!   ; 将x0, x30的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    d8,   d9, [sp,#-16]!    ; 将d8, d9的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    d10,  d11, [sp,#-16]!   ; 将d10, d11的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    d12,  d13, [sp,#-16]!   ; 将d12, d13的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    d14,  d15, [sp,#-16]!   ; 将d14, d15的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x1,   x19, [sp,#-16]!   ; 将x1, x19的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x20,  x21, [sp,#-16]!   ; 将x20, x21的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x22,  x23, [sp,#-16]!   ; 将x22, x23的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x24,  x25, [sp,#-16]!   ; 将x24, x25的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x26,  x27, [sp,#-16]!   ; 将x26, x27的值存储到sp - 16的地址上,并且执行sp -= 16
	stp    x28,  x29, [sp,#-16]!   ; 将x28, x29的值存储到sp - 16的地址上,并且执行sp -= 16

以下这段代码是将保存了当前寄存器值快照的内存栈顶地址的寄存器sp保存到当前上下文中(寄存器x0指向的内存地址

	mov    x3,   sp                ; 将sp的值传送到x3
	str    x3,   [x0]              ; 将x3的值存储到x0的地址上

以下这段代码是从新的上下文中(寄存器x1指向的内存地址)加载内存栈顶地址,并将其保存到寄存器sp。

	ldr    x3,   [x1]              ; 从x1的地址里取一个64位数存储到x3中
	mov    sp,   x3                ; 将x3的值传送到sp

以下这段代码是从之前存储在内存栈中的寄存器值快照恢复到寄存器x0, x30, d8, d9, d10, d11, d12, d13, d14, d15, x1, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29中(反方向恢复)。

	ldp    x28,  x29,  [sp]        ; 从sp的地址里取出2个64位的数(共16字节),分别存入x28, x29
	ldp    x26,  x27,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x26, x27,并且执行sp += 16
	ldp    x24,  x25,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x24, x25,并且执行sp += 16
	ldp    x22,  x23,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x22, x23,并且执行sp += 16
	ldp    x20,  x21,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x20, x21,并且执行sp += 16
	ldp    x1,   x19,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x1, x9,并且执行sp += 16
	ldp    d14,  d15,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入d14, d15,并且执行sp += 16
	ldp    d12,  d13,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入d12, d13,并且执行sp += 16
	ldp    d10,  d11,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入d10, d11,并且执行sp += 16
	ldp    d8,   d9,   [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入d8, d9,并且执行sp += 16
	ldp    x0,   x30,  [sp,#16]!   ; 从sp + 16的地址里取出2个64位的数(共16字节),分别存入x0, x30,并且执行sp += 16

可能有人会问,寄存器有很多,为什么要保留以下这些寄存器:x0, x30, d8, d9, d10, d11, d12, d13, d14, d15, x1,x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29 ?根据博客:ARMv8-aarch64 寄存器和指令集一文的介绍,x0用于保存函数返回值,x1用于保存函数的调用参数,x30保存当前函数调用结束后下一条需要执行的指令地址。x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29是CPU上下文切换时,被调用者必须保存的寄存器。d8, d9, d10, d11, d12, d13, d14, d15是128位浮点寄存器v8, v9, v10, v11, v12, v13, v14, v15的低64位(例如,128位浮点寄存器v8分高64位和低64位,其中低64位使用d8表示),被调用者在协程切换时必须保留上述浮点寄存器。也就是说,根据ARMv8-aarch64指导手册,在切换协程时,要将调用参数寄存器x0, x1(实际上,x0保存的是当前CPU寄存器快照值在内存栈的栈顶地址,x1保存的是原来CPU寄存器快照值在内存栈的栈顶地址),返回后的下一条指令地址寄存器x30, 被调用者用到的临时通用寄存器x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29,以及被调用者用到的临时浮点寄存器d8, d9, d10, d11, d12, d13, d14, d15当前的快照值全部保存到内存栈,同时从内存栈取出之前保存的上述寄存器快照值,从而顺利完成协程上下文的切换

register_0
register_1

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ARM 64 协程切换上下文的汇编代码解读 的相关文章

  • armv8 NEON if 条件

    我想了解armv8 NEON内联汇编代码中的if条件 在armv7中 这可以通过检查溢出位来实现 如下所示 VMRS r4 FPSCR BIC r4 r4 1 lt lt 27 VMSR FPSCR r4 vtst 16 d30 d30 d
  • 为什么无符号类型在arm cpu中效率更高?

    我正在阅读手臂手册并提出这个建议 但没有提到原因 为什么无符号类型更快 在 ARMv4 之前 ARM 没有对加载半字和有符号字节的本机支持 要加载有符号的字节 你必须LDRB然后对值进行符号扩展 LSL那就起来吧ASR它回落 这很痛苦所以c
  • ARM NEON:如何实现 256 字节查找表

    我正在使用内联汇编将我编写的一些代码移植到 NEON 我需要的一件事是将范围 0 128 的字节值转换为表中采用完整范围 0 255 的其他字节值 该表很短 但其背后的数学并不容易 因此我认为不值得每次 即时 计算它 所以我想尝试查找表 我
  • 线程安全的向量和字符串容器?

    我之前发过一个问题 在嵌入式 Linux 平台上使用 std string 时出现段错误 https stackoverflow com questions 2412667 seg fault when using stdstring on
  • ARM 的启动过程是怎样的?

    我们知道 对于X86架构 按下电源按钮后 机器开始执行0xFFFFFFF0处的代码 然后开始执行BIOS中的代码以进行硬件初始化 BIOS 执行后 它使用引导加载程序将操作系统映像加载到内存中 最后 操作系统代码开始运行 对于ARM架构 使
  • 如何修改内核DTB文件

    Summary 我目前正在为定制板编译 Linux 内核 内核 模块和 DTB 以及一些定制驱动程序 有时 我会编译内核并意识到 DTB 文件中的兼容性字符串不是自定义驱动程序正在寻找的内容 现在 我可以解决此问题的唯一方法是修改 DTS
  • 适用于arm(cortex-m3)的位置独立可执行文件(-pie)

    我正在使用codesourcery g lite 基于gcc4 7 2版本 为stm32 Cortex m3 编程 我希望动态加载可执行文件 我知道我有两个选择 1 可重定位的elf 需要一个elf解析器 2 具有全局偏移寄存器的位置无关代
  • 用于 RHEL 的 gdb-multiarch

    我正在尝试寻找方法来运行gdb 多架构RHEL 中的命令 我已经安装了用于 ARM 处理的 QEMU 模拟器 我想安装GDB进行调试 我能够安装GDB 多体系结构在 Ubuntu 中运行命令成功 sudo apt get GDB multi
  • 如何使用 gcc 编译代码和 ARM Cortex A8 目标进行调用图分析?

    我对这个已经咬牙切齿了 我需要在 ARM 板上进行分析并需要查看调用图 我尝试使用 OProfile Kernel perf 和 Google 性能工具 一切正常 但不输出任何调用图信息 这使我得出结论 我没有正确编译代码 我在编译 C 代
  • RAM 存储二进制数和汇编语言的冒泡排序

    我必须使用 ARM v7 执行一个例程 在 RAM 内存中存储 10 个二进制数 然后使用冒泡排序对这些数字从高到低进行排序 我应该如何开始 func bubbleSortAscendingU32 ldr r3 r0 4 mov r1 9
  • 在嵌入式设备上使用new或malloc引起的段错误[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我正在尝试
  • M1 MacBook Pro 上的 Android Studio 无法使用 ABI armeabi-v7a 模拟系统映像

    我的 M1 Macbook Pro 上的 Android Studio 可以很好地模拟 ABI arm64 v8a 的所有系统映像 API 24 29 30 31 但是 它无法使用 ABI armeabi v7a 运行所有映像 例如 API
  • arm-thumb指令集的blx指令如何支持4MB范围

    读自https www keil com support man docs armasm armasm dom1361289866046 htm https www keil com support man docs armasm arma
  • 交叉编译 Qt 4.7 时出现“非法指令”

    我已经在这个问题上苦苦挣扎了一个多星期了 但仍然找不到解决方案 我正在尝试为 ARM 设备交叉编译 Qt 4 7 嵌入式开源版本 构建过程本身可以顺利完成 但生成的二进制文件似乎包含处理器无法理解的指令 构建主机是 i386 上的 Debi
  • 如何使用 Neon SIMD 将无符号字符转换为有符号整数

    如何转换变量的数据类型uint8 t to int32 t使用霓虹灯 我找不到执行此操作的任何内在因素 假设您想要将 16 x 8 位整数的向量转换为 4 个 4 x 32 位整数的向量 您可以通过首先解压缩为 16 位 然后再次解压缩为
  • 使用 Android NDK 使用 -fsigned-char 进行构建安全吗?

    为了与其他平台保持一致 我需要使用signed char在我正在处理的一些本机代码中 但默认情况下在Android NDK上char类型是unsigned 我尝试明确使用signed char类型 但它生成太多警告differ in sig
  • arm-linux-gnueabi 编译器选项

    我在用 ARM Linux gnueabi gcc在 Linux 中为 ARM 处理器编译 C 程序 但是 我不确定它编译的默认 ARM 模式是什么 例如 对于 C 代码 test c unsigned int main return 0x
  • ARM Chromebook 上的 Android 开发环境?

    我尝试了多次安装和使用安卓工作室 https developer android com studio index html on an ARM Chromebook C100P https archlinuxarm org platfor
  • GCC C++ (ARM) 和指向结构体字段的 const 指针

    假设有一个简单的测试代码 typedef struct int first int second int third type t define ADDRESS 0x12345678 define REGISTER type t ADDRE
  • 交叉编译armv5,但它创建v7二进制文件

    我设法为arm926ej s创建了一个目标文件我在 qemu 上使用 Debian Arm arm linux gnueabi gcc 4 4 static O c mcpu arm926ej s hello c o hello root

随机推荐

  • ​​​​​​​自动化批量漏洞扫描脚本定制

    github上找到一款并发框架 POC T https github com Xyntax POC T 可以优美的进行并发操作 上面所述的内容大多可以用插件联合POC T进行 因为POC T不能一次使用多个插件 于是笔者对POC T框架进行
  • Xshell5登录报“找不到匹配的host key 算法“的错误

    Xshell5登录报 找不到匹配的host key 算法 的错误 现象 解决方法一 解决方法二 现象 xshell5登录欧拉22 03时报错 找不到匹配的host key 算法 解决方法一 1 编辑 etc ssh sshd config
  • win10电脑任务栏右侧小图标消失解决方法

    WIN10系统任务栏 左边是窗口键和快捷图标 右边是时钟 系统喇叭 网线连接图标 任务栏左边没问题 窗口键和快捷图标都良好 右侧的系统图标无显示 只显示任务栏的底色 尝试操作隐藏任务栏再开启任务栏后 图标恢复正常了 再点击右侧任务栏任意图标
  • 网络环路导致公司网络瘫痪问题排查

    问题 公司网络突然很不稳定 跟踪发现大量丢包 问题排查 1 怀疑电信网络 设备有问题 联系电信经理 安排工程人员过来排查 排查发现入户网络正常 更换电信入户光猫后网络还是不稳定 还是大量丢包 2 机房排查 2 1 关闭所有交换机 然后再一台
  • Anaconda换国内源(清华源、中科大源)

    命令行执行 Windows下 Anaconda 清华源 conda config add channels https mirrors tuna tsinghua edu cn anaconda pkgs free conda config
  • PPP协议实现透明传输的2种方法以及工作状态

    文章目录 1 PPP协议帧格式 2 字节填充 2 1 零比特填充方法 不使用序号和确认机制 PPP协议的工作状态 1 PPP协议帧格式 7E 十六进制数0x7E 在PPP协议里代表帧头和帧尾 二进制表示为0111 1110 占一个子节 FF
  • 机器学习SVM函数

    目录 1 SVM的损失函数 2 SVM的核方法 2 1 什么是核函数 2 1 1 核函数概念 2 1 2 核函数举例 2 1 2 1 核方法举例1 2 1 2 2 核方法举例2 2 2 常见核函数 2 3 小结 3 SVM回归 1 SVM的
  • springboot的负载均衡

    springboot的负载均衡 eueka作为注册中心 负载均衡使用的是Ribbon Ribbon负载均衡的策略有轮询 重试 权重 默认轮询 这是它独特的算法去调用具体的服务 在消费者启动动类中加上 Bean LoadBalanced pu
  • C++ 机房预约系统(七):老师模块——老师登录和注销、查看所有预约功能、审核预约功能的具体实现

    9 教师模块 在这个模块中 登录和注销和管理员与学生的实现一样 查看所有预约也和学生的查看所有预约实现一样 审核预约基本上和学生的取消预约一样 不同的是 学生模块 是通过学号和预约状态找到可以取消的预约记录 在老师模块 是通过预约状态找到可
  • Ik分词器(自定义分词-mysql)

    引言 ik分词器的分词范围不够广泛 某些特定行业的专业用语分词能力就不够了 此时就需要自定义分词 与停顿词 1 下载ik分词器源码 git地址 https github com medcl elasticsearch analysis ik
  • Lombok 的正确使用姿势

    文章目录 1 Lombok 是什么 2 安装 Lombok 3 Spring Boot 集成 Lombok 4 使用 Lombok 4 1 注解一览表 4 2 部分使用介绍 Getter lazy true Value Builder Su
  • R语言基本统计分析——抽样

    R语言基本统计分析 抽样 简单随机抽样 简单随机抽样是指从数据总体中任意抽取指定数量的数据作为样本 其中每个可能被抽取中的样本概率相等 可以用R语言中的sample 函数进行随机抽样 抽取方法分为 重置抽样 不重置抽样 R语言命令为 sam
  • Ubuntu 18.04 配置ibus中文拼音输入法(超简单)

    Ubuntu 18 04系统想安装中文输入法 利用ibus输入法配置 只要三步 注意 你的Ubuntu需要可以上网 因为要下载一系列安装包 第一步 首先需要给Ubuntu18 04安装Chinese语言包支持 先打开Settings窗口 g
  • playwright连接已有浏览器操作

    文章目录 playwright连接已有浏览器操作 前置准备 打开本地已有缓存的Chrome 理解 指定端口打开浏览器 连接指定端口已启动浏览器 推荐 playwright连接已有浏览器操作 前置准备 pip install playwrig
  • Linux和windows下setsockopt用法

    Linux和windows下setsockopt用法 linux struct timeval timeout 3 0 3s int ret setsockopt sock fd SOL SOCKET SO SNDTIMEO timeout
  • xml 文档树

    xml 文档树 XML documents form a tree structure that starts at the root and branches to the leaves XML 文档树起始于 根元素 并以此为基础扩展文档
  • 优质网址收集

    1 免费PPT模板下载网址都是免费且免登录 网址为 http www ypppt com http 51pptmoban com 2 在线工具网站 包括格式转换 文字识别 图片压缩 视频压缩等 网址为 http www nicetool n
  • python课后作业总结

    课后作业1 一个列表中有多种字符型的元素 要求一将非字符型的全部改成字符型 要求二将所有字符型中的大写字母改成小写 需要用到的知识有 1 lower 函数 功能 将大写字母改成小写 2 列表生成式 s lower for s in L 其中
  • layui生成菜单

    layui生成菜单 thymeleaf渲染 1 ul class layui nav layui nav tree li class layui nav item a href a li ul
  • ARM 64 协程切换上下文的汇编代码解读

    ARM 64协程切换上下文的汇编代码解读 贺志国 2023 8 11 在ARM 64位架构中 有一组通用寄存器 General Purpose Registers 一组浮点寄存器 Floating point Registers 和一组特殊