我正在设计我自己的RISC-V CPU,并且能够实现一些指令代码.
我已经安装了GCC编译器的RV32I版本,所以我现在有了汇编程序riscv32-unknown-elf-as
。
我试着用一条指令组装一个程序:
# simple.asm
add x5,x6,x7
我用汇编程序编译它,然后使用以下命令运行objdump:
riscv32-unknown-elf-as simple.asm -o simple
riscv32-unknown-elf-objdump -D simple
这将打印出以下内容:
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
我的问题是:
说清楚一点,我现在把我的处理器当成了裸金属。我在想象我将在处理器中硬编码,指令从内存地址X开始,数据在内存地址Y可用,堆栈在内存地址Z可用。这是正确的吗?还是这是错误的做法?
发布于 2021-11-04 05:21:39
处理器如何知道从哪个地址开始获取指令?
实际的CPU本身将有一些硬连线地址,它从重新设置/电源上获取。通常,系统将设计与ROM或闪存在该电话的地址。(该ROM可能具有ELF程序加载程序的早期启动代码,它将尊重ELF入口点元数据来从ROM中设置ELF内核映像,或者您可以在二进制文件开始时将一个平面二进制文件与正确的代码链接起来。)
这里发生什么事情?我以为我会有一个简单的巫术,但有更多的事情。
您的objdump -D
反汇编所有ELF部分,而不仅仅是.text。如您所见,.text部分中只有一条指令,如果您使用objdump -d,这就是您所看到的。(我通常使用objdump -drwC
,虽然-w
--没有行包装可能与RISC无关,但与x86不同,在x86中,一个insn可以是长的。)
是否可以像我的处理器一样将上面编译的文件传递给我?
不像你想的那样。还请注意,您为输出选择了错误的文件名。生成对象文件(通常是.o),而不是可执行文件。您可以将ld
链接到一个平面二进制文件中,也可以将.text
部分链接到其中。
(理论上,您可以将整个ELF可执行文件,甚至是对象文件放入ROM中,这样,.text
部分恰好从CPU获取的位置开始,但不需要查看元数据字节。因此ELF可执行文件中的ELF入口点地址元数据与此无关。)
.o
和可执行文件之间的区别:.o
只是为链接器提供重新定位元数据来填充实际地址,对于la
伪指令是绝对的,在多个.o
文件(其中一个引用另一个符号)的情况下是相对的。(否则,相对位移可以在组装时计算,而不是留给链接时间计算。)
因此,如果有使用任何标签作为内存地址的代码,则需要链接器在代码中填充这些重新定位条目。然后,您可以从链接的ELF可执行文件中objcopy
一些部分。或者使用链接器脚本来设置平面二进制文件的布局。
对于只有add
、没有la
或其他任何内容的简单情况,不存在重定位条目,因此.o
中的文本部分与链接的可执行文件中的文本部分相同。
使用objcopy
也很难理解静态数据,例如.data
和.bss
部分。如果只将.text
部分复制到平面二进制文件中,那么任何地方都不会有数据。(但是在ROM中,您需要一个启动函数,将静态初始化器从ROM复制到.data
的内存中,并对.bss
空间进行0。如果您想要编写asm源代码,使其具有一个具有非零值的正常外观的.data
部分,那么您的构建脚本就需要计算出要复制的大小,这样您的启动函数就可以使用它,而不必手动完成所有这些操作。)
发布于 2021-11-09 09:14:57
@PeterCordes的回答使我走上了正确的道路。我终于找到了如何生成一个我可以使用的原始内存转储文件。
这些步骤如下:
.text
和.data
部分和_start
标签。我的simple.asm
文件现在看起来如下:
.globl _start .text _start: add x5,x6,x7 .data L1:.word 27.asm
组装成一个.o
文件:
riscv32 32-未知-精灵-为simple.asm -o simple.o.text
和.data
部分。因此,我的链接器脚本(mycpu.ld
)如下所示:
OUTPUT_FORMAT(“elf32 32-littleriscv”、“elf32 32-littleriscv”、“elf32 32-littleriscv”)条目(_start)内存{ DATA (rwx):开头= 0x0,长度= 0x80 INST (rx):开头= 0x80 }节{ .data:{ *(.data) }> DATA .text:{ *(.text) }> INST }riscv32-unknown-elf-gcc
生成ELF文件,该文件自动调用riscv32-unknown-elf-ld
:
riscv32 32-未知-精灵-gcc -nostdlib -T mycpu.ld -o simple.elf simple.o.elf
文件中创建一个原始二进制或十六进制文件,我将使用该文件填充内存的内容。
riscv32 32-未知-elf-objcopy -O二进制simple.elf simple.hex最终的simple.hex
包含以下内容(使用hexyl
):
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 1b 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │•0000000┊00000000│
│00000010│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000040│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000050│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000060│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000070│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000080│ b3 02 73 00 ┊ │וs0 ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
其中b3027300
是add x5,x6,x7
的十六进制值。
就这样!非常感谢@PeterCordes的帮助!:)
https://stackoverflow.com/questions/69834360
复制相似问题