4

我正在尝试学习使用 cortex m0 处理器。我有一个 stm32f0 开发板,可以让我查看每个地址的每一位并轻松上传新的二进制文件。我一直在阅读大量有关许多规则和功能的手册,但仍然不知道程序计数器在重置时从哪里开始,它期望什么类型的参数,我什至不知道如何编写诸如 add 之类的东西或二进制形式的 str/ldr。我读过的手册中是否遗漏了这些基本知识?

它说 m0 有一个完整的降序堆栈,但似乎表明起点在另一端(0x00000000)。如果向量表也可以用外行的术语来解释,那就太好了。

4

2 回答 2

6

例如,全尺寸手臂(皮质-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 臂臂中。

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.

这意味着所有支持 thumb 的人都支持这个指令(从 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处理器的工作方式不同并且有不同的规则,有些你根本无法获取堆栈指针,有些你可以但重置值是正确的,还有一些就像你需要担心的武器一样。全尺寸的手臂你有多个堆栈指针要管理,你也可以让堆栈上升或下降,尽管我不会因为你可以而违背原则。有些你可以,但重置值是正确的,有些像你需要担心的武器。全尺寸的手臂你有多个堆栈指针要管理,你也可以让堆栈上升或下降,尽管我不会因为你可以而违背原则。有些你可以,但重置值是正确的,有些像你需要担心的武器。全尺寸的手臂你有多个堆栈指针要管理,你也可以让堆栈上升或下降,尽管我不会因为你可以而违背原则。

于 2015-04-27T20:39:05.713 回答
4

对于所有 Cortex-M,内存映射中的前两个字(分别位于地址 0 和 4)应该是您的初始堆栈指针和您要开始执行的第一条指令的地址。

通常,您会将堆栈放在可用的最高 RAM 地址处,并使用链接描述文件在地址 4 处插入程序入口点的地址。

于 2015-04-27T05:11:14.430 回答