MacOS 中 ARM64 汇编 ldr =address 伪指令导致运行时崩溃的原因及解决

news2024/11/15 11:46:10

在这里插入图片描述

0. 概览

我们知道在 MacOS 的 as 汇编器中有一条 ldr 伪指令,使用它我们可以非常方便的将立即数加载到寄存器中。

不过,当 ldr 的源操作数是一个标签(Label)时,就会导致在运行时发生崩溃:

在这里插入图片描述

如上图所示,第 21 行指令会导致运行时崩溃。

这是什么原因?又该如何解决呢?

闲言少叙,Let‘s go!!!😉


1. ldr 伪指令探究

在 MacOS 的 as 汇编器(以下简称 as )中, ldr 指令根据不同使用情境有着不同的功能。

在这里插入图片描述

当 ldr 源操作数不以 = 号开头时,它被转换成一条货真价实的 ARM64 机器指令:

ldr x0, [x0]

比如,上面的 ldr 指令表示将 x0 寄存器中的值加载到 x0 寄存器中。

当 ldr 源操作数以 = 号开头时,as 会将它变成一条伪指令。它的意思是将 = 号后面的值加载到目的寄存器中:

ldr x1, =0x12345678aabbccdd

比如,上面的 ldr 伪指令表示将 0x12345678aabbccdd 放入 x1 寄存器。

为什么要有 ldr =伪指令呢?因为在 ARM64 汇编中将立即数放入寄存器是一件“力气活”。

正常情况下,我们可以使用 mov 指令做这件事:

mov x0, #0xfff9			// 未超过 16 位
mov x0, #0xff000000		// 超过 16 位,但可以用左移来表示

不过,当立即数长度超过 16 位且不能用左移(LSL)来表示时,用 mov 指令会出错:

mov x0, #0x12345

以上指令在编译时会妥妥的报错:

arm64.s:22:13: error: expected compatible register or logical immediate
    mov x0, #0x12345
            ^

这是因为:ARM64 机器指令编码本身是定长的 32 位,里面无法再放入一个超长的立即数了(intel x64 汇编就无此问题,因为其指令编码是变长的)。

所以,ldr = 伪指令就应运而生了:编译器会帮助我们处理好赋值超长立即数这件事!

还拿上面的例子来说:

ldr x1,=0x12345678aabbccdd

它从功能上实际等于下面这条 mov 指令:

mov x1, 0x12345678aabbccdd

按常理 0x12345678aabbccdd 对于 mov 指令来说绝对是超长了,不过编译器在这里使用了一个小伎俩:它不是直接把 0x12345678aabbccdd 放到 x1 中,而是在 TEXT 段放入 0x12345678aabbccdd 值,然后将其地址当做 ldr 指令的源操作数:

在这里插入图片描述

这就是 ldr = 伪指令可以间接助攻 mov 指令的根本原因。


需要注意的是,如果 ldr = 伪指令的源操作数可以编码为 mov 指令许可的值,那么 as 汇编器会直接使用 mov 而不是 ldr 指令:

ldr x2,=0x1234

比如,上面的 0x1234 满足 mov 指令的长度限制,所以 as 会生成如下机器指令:

_main:
0000000100003f9c	mov	x2, #0x1234

2. 错误原因

了解了ldr 伪指令,现在我们来看看它的另一种形式:=Label。

	.text
    .globl _main
    .p2align 2
_main:
    func_constructor

    ldr x2,=my_data

    func_destructor
    ret
    .p2align 3
my_data:    .quad 0x12345678aabbccdd

上面的 ldr 伪指令实际做了神马?

它其实是将 my_data 的地址放入 x2 寄存器:

在这里插入图片描述

如上图所示:ldr 指令将 0x100003FB0 地址中的值放入 x2 寄存器,而 0x100003FB0 地址存放的就是 0x12345678aabbccdd 的地址!

以上代码编译链接均无问题,不过在运行时会立即崩溃。

% ./arm64 
zsh: bus error  ./arm64

看来是一种 bus error,我们使用 lldb 调试器看一下详细情况:

% lldb arm64
(lldb) target create "arm64"
Current executable set to '/Users/hopy/src/asm/arm64' (arm64).
(lldb) r
Process 3194 launched: '/Users/hopy/src/asm/arm64' (arm64)
Process 3194 stopped
* thread #1, stop reason = EXC_BAD_ACCESS (code=2, address=0x100003fb0)
    frame #0: 0x0000000191597ed8 dyld`dyld4::fixupPage64(void*, mwl_info_hdr const*, dyld_chained_starts_in_segment const*, unsigned int, bool) + 132
dyld`dyld4::fixupPage64:
->  0x191597ed8 <+132>: str    x12, [x9]
    0x191597edc <+136>: add    x9, x9, x11, lsl #2
    0x191597ee0 <+140>: cbnz   x11, 0x191597e98          ; <+68>
    0x191597ee4 <+144>: b      0x191597f04               ; <+176>
Target 0: (arm64) stopped.

可以看到,实际崩溃发生在一个名为 fixupPage64() 的“陌生”函数里。

这个函数是做什么的呢?它实际是在程序运行前来修正程序中绝对链接地址的。

再回过头看一下前面一张图:
在这里插入图片描述

ldr 指令中 0x100003FB0 指向的内容在链接时为 0x3FA8,这不是一个运行时有效的地址,所以 Mach-O 可执行文件加载器会在运行时根据实际基地址将其修正为有效的内存地址。

我们再来看看 fixupPage64() 函数中发生崩溃的指令 str x12, [x9],用 lldb 命令查看一下此时 x12 和 x9 寄存器中的内容到底是啥:

Process 3227 launched: '/Users/hopy/src/asm/arm64' (arm64)
Process 3227 stopped
* thread #1, stop reason = EXC_BAD_ACCESS (code=2, address=0x100003fb0)
    frame #0: 0x0000000191597ed8 dyld`dyld4::fixupPage64(void*, mwl_info_hdr const*, dyld_chained_starts_in_segment const*, unsigned int, bool) + 132
dyld`dyld4::fixupPage64:
->  0x191597ed8 <+132>: str    x12, [x9]
    0x191597edc <+136>: add    x9, x9, x11, lsl #2
    0x191597ee0 <+140>: cbnz   x11, 0x191597e98          ; <+68>
    0x191597ee4 <+144>: b      0x191597f04               ; <+176>
Target 0: (arm64) stopped.
(lldb) register read x12 x9
     x12 = 0x0000000100003fa8  arm64`my_data
      x9 = 0x0000000100003fb0  arm64`my_data + 8
(lldb) memory read $x12
0x100003fa8: dd cc bb aa 78 56 34 12 a8 3f 00 00 00 00 00 00  ....xV4..?......
0x100003fb8: 01 00 00 00 1c 00 00 00 00 00 00 00 1c 00 00 00  ................
(lldb) memory read $x12+8
0x100003fb0: a8 3f 00 00 00 00 00 00 01 00 00 00 1c 00 00 00  .?..............
0x100003fc0: 00 00 00 00 1c 00 00 00 00 00 00 00 1c 00 00 00  ................

看到了吗?fixupPage64() 函数实际上想要把 0x3fa8 修正为 100003fa8。

但这又为什么会出错呢?

原来,0x3fa8 是放在代码段(TEXT)中的,而代码段本身只有可读 + 可执行权限,是没有可写权限的,这就是程序崩溃的根本原因!

3. 解决之道

由此看来,as 中 ldr 的本意是让我们操作立即数,而不是标签(Label)。

解决方法有几种,首先我们可以为 TEXT 段添加可写属性,不过这样费事又不安全。

其实,我们的本意是获得 my_data 的地址,这可以通过 adr 指令来完成:

_main:
    func_constructor

    //ldr x2,=my_data
    adr x2, my_data

    func_destructor
    ret
    .p2align 3
my_data:    .quad 0x12345678aabbccdd

生成的机器指令如下所示:
在这里插入图片描述

综上所述,为了获得标签(my_data)的地址,我们完全可以跳过 ldr = 伪指令,而直接使用 adr 指令;只在需要立即数(尤其是超长立即数)赋值时借助 ldr = 的“神秘力量”。

4. 总结

在本篇博文中,我们详细讨论了 MacOS as 汇编器(ARM64)中 ldr = 伪指令的功能,并介绍了为什么不能用它来获得标签(Label)的地址,并提供解决方法。

感谢观赏,再会!😎

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

179_自动生成 千万级 Power BI 示例数据

179_自动生成 千万级 Power BI 示例数据 在早一些是时候&#xff0c;我曾写过一个示例数据《赠送300家门店260亿销售额的零售企业Power BI实战示例数据》&#xff0c;本次我们对该示例数据做了一些调整。 一、更新内容 针对有一些朋友不会使用 vba 模块&#xff0c;我们增加了…

RHEL CentOS Debian Ubuntu 如何刷新 DNS 缓存

RHEL CentOS Debian Ubuntu 如何刷新 DNS 缓存 全文&#xff1a;如何刷新 DNS 缓存 (macOS, Linux, Windows) Unix Linux Windows 如何刷新 DNS 缓存 (macOS, FreeBSD, RHEL, CentOS, Debian, Ubuntu, Windows) 请访问原文链接&#xff1a;https://sysin.org/blog/how-to-fl…

【YOLO】目标识别模型的导出和opencv部署

文章目录 0 前期教程1 什么是模型部署2 怎么部署 0 前期教程 【YOLO】朴实无华的yolov5环境配置 【YOLO】yolov5训练自己的数据集 1 什么是模型部署 前期教程当中&#xff0c;介绍了yolov5环境的搭建以及如何利用yolov5进行模型训练和测试&#xff0c;虽然能够实现图片或视频…

语法篇·JSP基础

一、初识JSP 1.1简介 JSP(Java Server Pages),其根本是一个简化的Servlet设计&#xff0c;它实现了在Java中使用HTML标签。JSP是一种动态网页技术标准&#xff0c;也是JavaEE的标准。JSP和Servlet一样&#xff0c;是在服务器端执行的。JSP是在Servlet技术发展之后为了让开发者…

苹果公司开发者账号申请流程

目录 一、注册 Apple ID 账号二、Apple Developer 登录三、申请公司邓白氏编码四、下载 Apple Developer app五、审核六、缴费七、发票 一、注册 Apple ID 账号 注册网址&#xff1a;https://appleid.apple.com/account 二、Apple Developer 登录 登录网址&#xff1a;http…

汽车EBSE测试流程分析(二):关于优势和挑战的案例分析

EBSE专题连载共分为“五个”篇章。此文为该连载系列的“第二”篇章&#xff0c;在之前的“篇章&#xff08;一&#xff09;”中已经阐述了汽车软件工程的特点&#xff0c;以及使用混合方法设计的分阶段EBSE测试过程&#xff0c;并提出问题。接下来&#xff0c;我们将具体分析EB…

抖音矩阵系统源码开发指南

抖音矩阵系统是一个大规模的分布式系统&#xff0c;它可以处理数百万级别的并发请求。要开发和部署抖音矩阵系统源代码 您需要遵循以下步骤&#xff1a; 下载和安装必要的软件依赖项&#xff1a;抖音矩阵系统源代码需要使用Java和Scala编程语言&#xff0c;因此您需要下载和安…

2023年程序员工资中位数增长10%?开发者最常用的语言竟然是……

在调研了全球超过 90000 名开发者之后&#xff0c;程序员社区 Stack Overflow 重磅发布了《2023 Developer Survey》调查报告。在本次报告中&#xff0c;Stack Overflow 从工具、编码、工作、社区等维度展开&#xff0c;同时深入研究了 AI/ML 技术&#xff0c;并解析开发者如何…

python spider 爬虫 之 urllib系列

python 中 集成了 urllib urllib import urllib.request # urlopen 方法 url"url" response urllib.request.urlopen(url)print(type(response )) print(response.read()) # 解码 字节--->字符串 decode 字符串--》字节 encode print(response.read()…

618电商物流内卷,拼速度不是唯一底牌,还有……

每年的618大促&#xff0c;对于消费者来说都是一场购物盛宴&#xff0c;也是各个快递企业的“大练兵”。各大电商平台也纷纷铆足劲&#xff0c;希望能抓住此次机会增加营收。 面对电商平台和消费者需求&#xff0c;今年的快递电商企业在保证速度&#xff0c;提升服务质量的前提…

用CMake下的find_package()函数链接库

文章目录 find_package()原理案例1&#xff1a;为项目添加库 find_package()原理 关于find_package()函数的相关内容可参考&#xff1a; https://www.cnblogs.com/lidabo/p/16635249.html Cmake 会在以下的路径中寻找Config.cmake或Find.cmake文件。找到后即可执行该文件并生…

pycharm 2023 IDE 个人一些常用配置记录|输入法|中文|光标|

1. 中文 文件->设置->插件,找到这个&#xff1a; 2. 光标前进和后退 移除前面的配置&#xff0c;修改一下&#xff0c;改称Ctrl左键头\右箭头 然后点击确定 3.中文输入法 3.1 解决输入法有无问题 在pycharm安装目录bin下的pycharm.sh中&#xff0c;添加如下几行&am…

白酒回收APP开发需具备哪些功能?

开发白酒回收APP软件需要具备哪些功能呢&#xff1f; 1、酒品展示。白酒回收APP首页展示各种人们回收的白酒产品&#xff0c;还可以对白酒进行品牌分类&#xff0c;这样用户在回收售卖的时候可以快速找到自己需要的品牌&#xff0c;点击进入查看回收指南&#xff0c;一步…

中国葡萄酒 当惊世界殊 宁夏贺兰山东麓葡萄酒亮相首都地铁

近日&#xff0c;宁夏贺兰山东麓葡萄酒以“中国葡萄酒 当惊世界殊、中国酒庄酒 天赋贺兰山”为宣传主题的宁夏贺兰山东麓葡萄酒宣传专列&#xff08;列车车号&#xff1a;01055&#xff09;亮相北京地铁1号线。该专列将从4月7日至7月6日持续开行3个月&#xff0c;平均每天往返穿…

Snipaste下载

官网&#xff1a; Snipaste 下载 解压 在桌面右下角状态栏中可以找到软件图标&#xff0c; 如图所示&#xff0c;已经运行成功 鼠标右键点击该图标&#xff0c;选择首选项可以设置相关功能选项。 设置了开机自启 最后将该文件夹放到合适的路径下&#xff0c;重新双击运行Snipa…

计算物理专题:薛定谔方程的有限元解法

计算物理专题&#xff1a;薛定谔方程的有限元解法 简单边值问题的有限元解法 其中&#xff1a;都是上已知的连续函数&#xff0c;且也连续&#xff0c; 等价性定理 如果y(x)是边值问题(1)的解&#xff0c;则y(x)是\mu 中使得泛函I(z)取极小值的函数&#xff1b;反正&#xff0c…

【面试题系列】关于K8s—Job的2个问题

序言 人生是一场消耗&#xff0c;要把美好的时光放在喜欢的人与事上。 写在前面 前面学习了job&#xff0c;现在思考一下两个问题 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标…

only up 游戏分析

文章目录 介绍游戏玩法游戏面向的群体游戏所须配置游戏美术风格游戏优点游戏缺点和改进方案游戏爆火原因分析同类型游戏对比和游戏继承性地铁跑酷掘地求升跳跳乐 总结 介绍 游戏玩法 这是一个玩法很简单的跑酷游戏&#xff0c;你必须找到一条可行的道路&#xff0c;一直往上走…

【C++】哈希的应用

文章目录 一、位图1. 位图的引入2. 位图的实现3. 位图的应用4. 哈希切割 二、布隆过滤器1. 布隆过滤器的引入2. 布隆过滤器的实现3. 布隆过滤器的应用4. 布隆过滤器的总结 一、位图 1. 位图的引入 我们先来看一道面试题&#xff1a; 给40亿个不重复的无符号整数&#xff0c;没…

vue项目用iframe嵌入另外一个vue项目(cesium)

vue项目用iframe 项目嵌入另外一个vue项目&#xff0c;主要分2种情况&#xff0c;一种情况是嵌入本地项目&#xff0c;另外一种是嵌入用web服务器启动的vue项目。 1&#xff09;嵌入本地项目 vue create hello-world 创建项目后&#xff0c; 用npm run build打包&#xff0c;把…