Pla*_*ove 3 assembly cpu-architecture elf riscv riscv32
我正在设计自己的 RISC-V CPU,并且已经能够实现一些指令代码。
我已经安装了 RV32I 版本的 GCC 编译器,因此现在可以使用汇编器riscv32-unknown-elf-as
了。
我正在尝试仅用一条指令来汇编一个程序:
# simple.asm
add x5,x6,x7
Run Code Online (Sandbox Code Playgroud)
我使用汇编器编译它,然后使用以下命令运行 objdump:
# simple.asm
add x5,x6,x7
Run Code Online (Sandbox Code Playgroud)
这会打印出以下内容:
new: file format elf32-littleriscv
Disassembly of section .text:
00000000 <.text>:
0: 007302b3 add t0,t1,t2
Disassembly of section .riscv.attributes:
00000000 <.riscv.attributes>:
0: 2d41 jal 0x690
2: 0000 unimp
4: 7200 flw fs0,32(a2)
6: 7369 lui t1,0xffffa
8: 01007663 bgeu zero,a6,0x14
c: 00000023 sb zero,0(zero) # 0x0
10: 7205 lui tp,0xfffe1
12: 3376 fld ft6,376(sp)
14: 6932 flw fs2,12(sp)
16: 7032 flw ft0,44(sp)
18: 5f30 lw a2,120(a4)
1a: 326d jal 0xfffff9c4
1c: 3070 fld fa2,224(s0)
1e: 615f 7032 5f30 0x5f307032615f
24: 3266 fld ft4,120(sp)
26: 3070 fld fa2,224(s0)
28: 645f 7032 0030 0x307032645f
Run Code Online (Sandbox Code Playgroud)
我的问题是:
需要明确的是,我目前将我的处理器视为裸机。我想象我将在处理器中进行硬编码,指令从内存地址 X 开始,数据在内存地址 Y 可用,堆栈在内存地址 Z 可用。这是正确的吗?或者这是错误的方法?
@PeterCordes 的回答让我走上了正确的道路。我终于弄清楚如何生成可以使用的原始内存转储文件。
\n步骤如下:
\n修改了程序集文件,使其具有.text
and.data
部分和_start
标签。我的simple.asm
文件现在如下所示:
.globl _start\n\n.text\n_start:\n add x5,x6,x7\n\n.data\nL1: .word 27\n
Run Code Online (Sandbox Code Playgroud)\n使用以下命令将其组装.asm
到文件中:.o
riscv32-unknown-elf-as simple.asm -o simple.o\n
Run Code Online (Sandbox Code Playgroud)\n为特定处理器创建链接描述文件。我观看了这个精彩的视频,该视频演示了从头开始创建链接器脚本的过程。现在,我只需要.text
和.data
部分。所以我的链接器脚本(mycpu.ld
)如下所示:
OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv")\nENTRY(_start)\n\nMEMORY\n{\n DATA (rwx) : ORIGIN = 0x0, LENGTH = 0x80\n INST (rx) : ORIGIN = 0x80, LENGTH = 0x80\n}\n\nSECTIONS\n{\n .data :\n {\n *(.data)\n }> DATA\n\n .text :\n {\n *(.text)\n }> INST\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n生成 ELF 文件,使用riscv32-unknown-elf-gcc
该文件自动调用riscv32-unknown-elf-ld
:
riscv32-unknown-elf-gcc -nostdlib -T mycpu.ld -o simple.elf simple.o\n
Run Code Online (Sandbox Code Playgroud)\n从该文件创建一个原始二进制或十六进制文件.elf
,我将用它来填充内存的内容。
riscv32-unknown-elf-objcopy -O binary simple.elf simple.hex\n
Run Code Online (Sandbox Code Playgroud)\nFinalsimple.hex
包含以下内容(使用hexyl
):
\xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90\n\xe2\x94\x8200000000\xe2\x94\x82 1b 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x82\xe2\x80\xa20000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000010\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000020\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000030\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000040\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000050\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000060\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000070\xe2\x94\x82 00 00 00 00 00 00 00 00 \xe2\x94\x8a 00 00 00 00 00 00 00 00 \xe2\x94\x8200000000\xe2\x94\x8a00000000\xe2\x94\x82\n\xe2\x94\x8200000080\xe2\x94\x82 b3 02 73 00 \xe2\x94\x8a \xe2\x94\x82\xc3\x97\xe2\x80\xa2s0 \xe2\x94\x8a \xe2\x94\x82\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xb4\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98\n
Run Code Online (Sandbox Code Playgroud)\n其中b3027300
是 的十六进制值add x5,x6,x7
。
就是这样!非常感谢@PeterCordes 的帮助!:)
\n处理器如何知道从哪个地址开始获取指令?
实际的 CPU 本身会有一些硬连线地址,可以在复位/上电时获取。通常,系统将在该物理地址处设计有 ROM 或闪存。(该 ROM 可能具有 ELF 程序加载器的早期启动代码,它将尊重 ELF 入口点元数据以从 ROM 设置 ELF 内核映像,或者您可以在启动时将一个平面二进制文件与正确的代码链接起来。二进制。)
这里发生了什么?我以为我会有一个简单的单行十六进制,但还有很多事情要做。
您objdump -D
反汇编了所有 ELF 部分,而不仅仅是 .text。正如您所看到的,.text 部分中只有一条指令,如果您使用 objdump -d 这就是您所看到的。(我通常使用objdump -drwC
,尽管-w
没有换行可能与 RISC-V 无关,这与 x86 不同,其中单个 insn 可能很长。)
是否可以将我上面编译的文件按原样传递给我的处理器?
可能不是你想的那样。另请注意,您为输出选择了错误的文件名。as 生成一个目标文件(通常为 .o),而不是可执行文件。您可以链接ld
到平面二进制文件,或链接到其中的部分objcopy
。.text
(理论上,您可以将整个 ELF 可执行文件甚至目标文件放入 ROM 中,这样该.text
部分恰好从 CPU 读取的位置开始,但不会查看元数据字节。因此,ELF 中的 ELF 入口点地址元数据可执行文件是无关紧要的。)
.o
a和可执行文件之间的区别:a.o
只是具有重定位元数据,供链接器填充实际地址,绝对地址用于la
伪指令,或者相对于多个文件(其中一个文件引用另一个文件的符号)的auipc
情况。.o
(否则,可以在汇编时计算相对位移,而不是留给链接时计算。)
因此,如果您的代码使用任何内存地址标签,则需要链接器在代码中填充这些重定位条目。然后您可以objcopy
从链接的 ELF 可执行文件中取出一些部分。或者使用链接描述文件来设置平面二进制文件的布局。
对于只有add
、 nola
或任何内容的简单情况,没有重定位条目,因此 中的文本部分与.o
链接的可执行文件中的相同。
objcopy
静态数据也很难正确处理,例如.data
和.bss
部分。如果您仅将该部分复制.text
到平面二进制文件中,则任何地方都不会包含数据。(但是在 ROM 中,您需要一个启动函数,将静态初始化器从 ROM 复制到 RAM .data
,并将空间归零.bss
。如果您想编写 asm 源代码以获得具有.data
非零值的正常部分,您可以希望您的构建脚本能够计算出要复制的大小,以便您的启动函数可以使用它,而不必手动执行所有这些操作。)