例如,全尺寸手臂(皮质-A 等)地址 0x00000000 是自行执行的重置指令,这有点奇怪,您经常看到地址列表,但它们就是这样做的。对于 cortex-m,它们不仅使用地址列表,而且硬件设计足以符合 EABI 以允许您将 C 函数名称放入表中,而不必进行少量汇编(向量表除外)本身)。
例如使用 gnu 汇编器。
;@-----------------------
.cpu cortex-m0
.thumb
;@-----------------------
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
在深入研究之前,您需要访问 infocenter.arm.com 上的当前内容,然后在架构下,然后 armv6-m 获取 armv6-m 架构参考手册(ARM ARM for the v6m)。这些链接已经存在了很长时间,但它们自然可以更改,它们确实调用它并区分架构参考手册和技术参考手册。架构参考手册通常涵盖该架构中的系列。 TRM 通常涵盖特定核心或特定核心的修订号。可能值得在信息中心页面的其他地方获得它。
我正在查看我认为是 armv6m ARM ARM 的 C 版:ARM DDI
0419C
在文档中搜索“向量表”,找出可能在或不在同一部分中的内容:
表 B1-4 向量表格式
此表显示地址空间中偏移量 0 处是 SP_main。这是主堆栈指针的重置值。
然后表中的字偏移量是异常编号,在 arm 世界中字是 4 个字节,因此异常编号 1 在地址空间中的偏移量 4 处,异常 2 在 8 处,依此类推。
这总是需要我一段时间才能找到。也在armv6m arm上。
B1.5.2 异常号定义
其中异常编号 1 被重置 2 是 nmi,依此类推。我们关心重置。
所以这意味着在 ARMS ADDRESS SPACE 中的地址 0x00000000 处,如果我们选择预加载堆栈地址,我们可以如果我们想要在引导代码中也设置堆栈,但我们不必这样做,一个地方必须做,但不要两者兼而有之。
然后在 ARMS ADDRESS SPACE 的地址 0x00000004 处,我们放置了重置处理程序的地址。
所以在我的示例中组装、编译和链接代码后,我得到了
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000041 stmdaeq r0, {r0, r6}
8000008: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800000c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000010: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000014: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000018: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800001c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000020: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000024: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000028: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800002c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000030: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000034: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000038: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800003c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
08000040 <reset>:
8000040: f000 f80a bl 8000058 <notmain>
8000044: e7ff b.n 8000046 <hang>
08000046 <hang>:
8000046: e7fe b.n 8000046 <hang>
您可以看到,在 gnu 汇编器将 .thumb_func 放在标签之前的情况下,使该标签成为人们可能调用的函数或地址,因此 bx 或 blx 指令因此需要设置 bx 或 blx 需要的 lsbit正确分支(bl不在乎)。链接器会自动修复向量表中的地址。例如,在偏移量 0x40 处重置会得到 0x41。
现在为什么不为地址 0x00000000 构建这段代码?!那是因为您必须超越 arm 文档查看芯片供应商文档,arm 不制造芯片,它们制造处理器内核和一些支持它们的逻辑,您可以去 st 或 nxp 或 ti 或其他任何人找到其余的故事特别是地址空间中的引导闪存在哪里。毫无疑问,在这种情况下,正常启动的 arm 地址空间中地址 0x08000000 处的闪存映射到 0x00000000,某些芯片将具有多个启动闪存,并且取决于带(输入引脚连接高或低或以各种组合)一个闪存或另一个将永远或一段时间内映射到地址零。
皮质 m0(和 m1)基于 armv6m,皮质 m3 和 m4 基于 armv7m。后者支持 thumb2 扩展指令集的巨大差异(以前未定义的指令成为两个半字指令 32 位指令的前半部分,不要与 32 位臂模式指令混淆),并且有大约 150 个新的 thumb2 指令添加到皮质 m3,然后皮质 m4 具有浮点单元的一小部分(只有一个浮点大小,可能是单个)以及随之而来的所有指令(基本上重新定义了协处理器指令)。这使得 cortex-m0 的生活更轻松,只有 16 位指令(是的,bl 实际上是并且在文档中定义为两个单独的指令,如果你愿意,你可以彼此分开编码)。
目前armv6m ARM ARM还包含指令集定义
第 A5 章 Thumb 指令集编码
看着
A6.7.17 CMP (immediate)
(同样,我的章节编号将来可能不会保持不变或匹配,他们的文档通常不会发生太大变化或从一个到下一个,但你永远不知道)。
首先要注意的是编码
Encoding T1 All versions of the Thumb instruction set.
这意味着所有支持拇指的人都支持这个指令(从armv4回到现在)
然后是语法
CMP <Rn>,#<imm8>
统一的语法可能与本文档的语法不同,也了解 ARM 有自己的工具链,因此如果定义的语法是特定于他们的汇编程序的。汇编语言不是一个标准,它是由汇编程序(解析它的程序)专门定义的。 Gnu 汇编器是一个独立的东西,不必遵守这个文档,它主要是这样做的,但是 arm 也开始了这个统一语法的东西,以允许一定比例的汇编语言组装到 thumb、thumb2 扩展和 arm 指令集而无需重新编写,尽管如果您不指定这三个中的哪一个,您仍然很快就会被束缚。
您可以在此指令中看到高位必须是 00101 位 15:11,这就是处理器如何知道这是一个比较立即数。 Rn 是寄存器 r0 到 r7,无论您使用的是哪一个(要访问 r8 到 r15,您必须使用其他 mov 指令以允许 16 位指令,他们必须将大多数指令保留到低 7 个寄存器以保存指令编码中的位) .然后低 8 位是从 0 到 255 的直接常量值(其他 arm/thumb 立即编码不是那么直接,并且 thumb vs arm 使用不同的编码,所以你必须阅读手册)。
我强烈建议如果您想查看编码然后用汇编语言编写汇编然后反汇编并让希望调试的工具链为您完成工作然后尝试对您看到的内容进行逆向工程并将其与手册相匹配。挠头是奇怪的地址,但这些都是记录在案的(尽管不一定如您所愿),然后是与 pc 相关的任何东西,每当您对它进行操作时,pc 都是两条指令,它实际上不是用管道,而是用于反向兼容性并设定一个标准,即 arm/thumb 标准是前面两个指令。因此,在计算或逆向工程计算 pc 相对地址时,这就是为什么你的数学总是少 4 个字节。
与大多数处理器一样,您的程序员或至少您信任借用他们的代码的程序员是设置堆栈指针的人。你可以把它放在你想要的任何地方,arm 内核本身,或者 arm 自己都不知道芯片供应商将如何实现,编译器也不知道或想知道所有可能的芯片,所以你程序员必须告诉工具链,然后工具链告诉手臂处理器你希望堆栈在哪里。传统上,您希望从高地址开始使用降序堆栈。您首先查看 pop 和 push 指令和伪代码,以查看 arm 首先递减寄存器数量乘以 4(对于 push),然后写入这些地址,然后在退出时调整 sp。因此,如果您的 ram 以 0x2001FFFF 结束,您可以安全地将堆栈指针放在 0x20020000 处,而推送的第一件事将是 0x2001FFFC。 (好吧,不是第一件事,而是堆栈的最底层)其他非ARM处理器的工作方式不同并且有不同的规则,有些你根本无法获取堆栈指针,有些你可以但重置值是正确的,还有一些就像你需要担心的武器一样。全尺寸的手臂,你有多个堆栈指针要管理,你也可以让堆栈上升或下降,尽管我不会因为你可以而违背原则。