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 协程切换上下文的汇编代码解读 的相关文章

  • 在 ARM 处理器上执行存储在外部 SPI 闪存中的程序

    我有一个 ARM 处理器 能够与外部闪存芯片连接 写入芯片的是为 ARM 架构编译的程序 可供执行 我需要知道如何将这些数据从外部闪存获取到 ARM 处理器上以供执行 我可以提前运行某种复制例程 将数据复制到可执行内存空间吗 我想我可以 但
  • 用于 RHEL 的 gdb-multiarch

    我正在尝试寻找方法来运行gdb 多架构RHEL 中的命令 我已经安装了用于 ARM 处理的 QEMU 模拟器 我想安装GDB进行调试 我能够安装GDB 多体系结构在 Ubuntu 中运行命令成功 sudo apt get GDB multi
  • 哪些变量类型/大小在 STM32 微控制器上是原子的?

    以下是 STM32 微控制器上的数据类型 http www keil com support man docs armcc armcc chr1359125009502 htm http www keil com support man d
  • RAM 存储二进制数和汇编语言的冒泡排序

    我必须使用 ARM v7 执行一个例程 在 RAM 内存中存储 10 个二进制数 然后使用冒泡排序对这些数字从高到低进行排序 我应该如何开始 func bubbleSortAscendingU32 ldr r3 r0 4 mov r1 9
  • DSP 库 - RFFT - 奇怪的结果

    最近我一直在尝试在我的STM32F4 Discovery评估板上进行FFT计算 然后将其发送到PC 我已经调查了我的问题 我认为我对制造商提供的 FFT 函数做错了 我正在使用 CMSIS DSP 库 现在我一直在用代码生成样本 如果工作正
  • 读取和打印手臂组件中的字符串

    我正在使用 ARMSim 刚刚开始学习汇编 所以如果我看起来一无所知 请原谅我 但我正在尝试从输入文件中读取字符串 然后将其打印到输出屏幕 到目前为止我有 equ SWI Open 0x66 open a file equ SWI Clos
  • 源和目标具有不同的 EABI 版本

    我正在尝试使用 ARM 工具链编译 so 文件 但是我不断收到这个错误 错误 源对象的 EABI 版本为 0 但目标对象的 EABI 版本为 5 我无法更改工具链中的任何内容 因为我必须使用给定的工具链 我以前从未见过这个错误 我使用了这个
  • 有没有办法在 Xcode 4 中为 ARM 而不是 Thumb 进行编译?

    如果有很多浮点运算正在进行 Apple 建议针对 ARM 进行编译 而不是针对拇指进行编译 我的整个应用程序几乎是一个大型浮点运算 iOS 应用程序开发工作流程指南中是这样说的 iOS 设备支持两种指令集 ARM 和 Thumb Xcode
  • ARM 调用约定是否允许函数不将 LR 存储到堆栈中?

    正如标题所示 我在理解 ARM 架构的调用约定时遇到问题 特别是 我仍然很难知道当你调用子程序时 LR 寄存器会发生什么 我认为 当您进入子程序时 处理 LR 寄存器的最明显 最安全的方法是将其存储到堆栈中 但该行为没有出现在文档中 因此我
  • 如何使用 Neon SIMD 将无符号字符转换为有符号整数

    如何转换变量的数据类型uint8 t to int32 t使用霓虹灯 我找不到执行此操作的任何内在因素 假设您想要将 16 x 8 位整数的向量转换为 4 个 4 x 32 位整数的向量 您可以通过首先解压缩为 16 位 然后再次解压缩为
  • 产生并处理软件中断

    有人可以告诉我如何在Linux下生成软件中断然后用request irq处理它吗 或者也许这是不可能的 您可以使用软中断来代替 您可以通过编辑 include linux interrupt h 来定义您的 sofirq 然后使用函数 ra
  • 在linux x86平台上学习ARM所需的工具[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我有一个 x86 linux 机器 在阅读一些关于 ARM 的各种信息时 我很好奇 现在我想花一些时间学
  • 在 Intel 机器上构建 Apple Silicon 二进制文件

    如何在 macOS 11 Intel 上编译 C 项目以在 Silicon 上运行 我当前的构建脚本很简单 configure make sudo make install 我尝试过使用 host and target标志与aarch64
  • gdb 不会从外部架构读取核心文件

    我正在尝试在 Linux 桌面上读取 ARM 核心文件 但似乎无法找出我的核心文件 有什么方法可以指示 gdb 我的核心文件是什么类型吗 file daemon daemon ELF 32 bit LSB executable ARM ve
  • 使用 NEON 内在函数除以浮点数

    我当时正在处理四个像素的图像 这是在armv7对于 Android 应用程序 我想分一个float32x4 t向量由另一个向量组成 但其中的数字与大约不同0 7 to 3 85 在我看来 除法的唯一方法是使用右移 但这是针对一个数字2 n
  • 如何获取结构体中任意成员的位位置

    如何获取结构体中任意成员的位位置 在示例中 gt typedef struct BitExamStruct unsigned int v1 3 unsigned int v2 4 unsigned int v3 5 unsigned int
  • Android NDK 代码中的 SIGILL

    我在市场上有一个 NDK 应用程序 并获得了有关以下内容的本机崩溃报告 SIGILL信号 我使用 Google Breakpad 生成本机崩溃报告 以下是详细信息 我的应用程序是为armeabi v7a with霓虹灯支持 它在 NVIDI
  • 让 TensorFlow 在 ARM Mac 上使用 GPU

    我已经安装了TensorFlow在 M1 上 ARM Mac 根据这些说明 https github com apple tensorflow macos issues 153 一切正常 然而 模型训练正在进行CPU 如何将培训切换到GPU
  • aarch64 Linux 硬浮点或软浮点

    linux系统有arm64 有arm架构armv8 a 如何知道 Debian 运行的是硬浮动还是软浮动 符合 AAPCS64 GNU GCC for armv8仅提供硬浮动aarch64工具链 这与 armv7 a 的 GCC 不同 后者
  • 在LPC2148 ARM处理器上创建中断向量的汇编代码

    我最近刚刚开始使用 LPC2148 ARM 处理器 我试图理解一些有关创建中断向量的汇编代码 这是代码 Runtime Interrupt Vectors Vectors b start reset start ldr pc undf un

随机推荐

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

    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 和一组特殊