Spike/PK 在 RISC-V 汇编中调用 fgets
Calling fgets in RISC-V assembly on Spike/PK
更新:写出来让我发现我哪里出错了,但不知道为什么。我显然以错误的方式调用 fgets,因为在五次调用之后我到达了地址 0x221000,这是映射内存所在的位置——我在更高的地址写入——但我不知道为什么会这样。有人可以解释一下吗?
这有点复杂,我不知道为什么会出现这种行为:我不知道是我的基础知识有误,还是 Spike/PK 的一个特性。
注意:这里的libc由newlib提供,代码编译为riscv64-unknown-elf。
精简版 我输入了用 RISC-V 汇编编写的代码,之前 运行 很顺利,但是自从我引入了对 mmap 的系统调用后,它第五次崩溃了它被执行了。问题是因为我的调用顺序错误还是 Spike 模拟器和 PK 代理内核可能有问题?
详细解释
我正在编写一种类似于 Forth 的线程解释语言。它目前针对 Spike 模拟器上的 PK 代理内核,但希望很快在 'real' 硬件上 运行。代码位于 https://github.com/mcmenaminadrian/riscyforth
TIL 实现了一个无限循环来获取输入,依次调用获取标准输入文件指针的例程,然后是获取输入的例程。
获取标准输入文件指针(存储在堆栈中):
mv a0, zero
la a1, stdinopen
call fdopen
addi sp, sp, -8
sw a0 0(sp)
(stdinopen 在 .data 部分中作为 .asciz“r”)
然后获取输入 - 将其弹出堆栈...
lw a2, 0(sp)
addi sp, sp, 8
la a0, INPUT_BUFFER
li a1, BUFFERLEN #0x200
call fgets
INPUT_BUFFER 在 .bss 部分定义为 .common INPUT_BUFFER, BUFFERLEN, 8
这一切都很好,直到我添加了映射到内存中的新代码 R/W/X - 如果您了解 Forth,您就会知道我需要具有这组不寻常权限的内存,因为我必须允许用户定义新关键字。
现在尝试获取输入在第四次调用时失败。 (尽管如果我定义了 mmap,问题就消失了。)
这是一个失败的例子:
欢迎来到 RISCYFORTH - 版权所有 Adrian McMenamin 2020 - 2021
RISCYFORTH 是一种基于 FORTH 的线程解释语言。
目前我们 运行 处于 EXECUTE 模式,您输入的任何内容都会
在输入每一行时执行。
RISCYFORTH 根据 GNU 通用 Public 许可证第 2 版获得许可。
参见 https://github.com/mcmenaminadrian/riscyforth.git
346 5622 * dup
OK
dup
OK
dup
OK
.
1945212 OK
z 0000000000000000 ra 0000000000010442 sp 0000003ffffffaf8 gp 000000000001fe30
tp 0000000000000000 t0 000000000009fa60 t1 000000000000000a t2 0000000000000003
s0 0000000000000000 s1 0000000000000000 a0 000000000001e530 a1 000000000009fa60
a2 0000000000000200 a3 0000000000000000 a4 0000000000221000 a5 0000000000000001
a6 000000000001ec88 a7 00000000000000d6 s2 00000000000001ff s3 000000000009fa60
s4 000000000001e530 s5 000000000009fa60 s6 0000000000000000 s7 0000000000011b64
s8 0000000000010400 s9 000000000011fc58 sA 000000000009f950 sB 0000000000000000
t3 0000000000000064 t4 0000003ffffffb08 t5 0000000000000054 t6 0000000000000054
pc 0000000000011ca4 va/inst 0000000000000008 sr 8000000200006020
User load segfault @ 0x0000000000000008
这里是代码失败的地方:
0000000000011c64 <_fgets_r>:
11c64: 4785 li a5,1
11c66: 0ac7de63 bge a5,a2,11d22 <_fgets_r+0xbe>
11c6a: 7139 addi sp,sp,-64
11c6c: f822 sd s0,48(sp)
11c6e: f04a sd s2,32(sp)
11c70: ec4e sd s3,24(sp)
11c72: e852 sd s4,16(sp)
11c74: fc06 sd ra,56(sp)
11c76: f426 sd s1,40(sp)
11c78: e456 sd s5,8(sp)
11c7a: e05a sd s6,0(sp)
11c7c: 8932 mv s2,a2
11c7e: 8a2a mv s4,a0
11c80: 89ae mv s3,a1
11c82: 8436 mv s0,a3
11c84: c119 beqz a0,11c8a <_fgets_r+0x26>
11c86: 493c lw a5,80(a0)
11c88: cbc1 beqz a5,11d18 <_fgets_r+0xb4>
11c8a: 397d addiw s2,s2,-1
11c8c: 8ace mv s5,s3
11c8e: a819 j 11ca4 <_fgets_r+0x40>
11c90: 601c ld a5,0(s0)
11c92: 9f05 subw a4,a4,s1
11c94: 9aa6 add s5,s5,s1
11c96: 94be add s1,s1,a5
11c98: c418 sw a4,8(s0)
11c9a: e004 sd s1,0(s0)
11c9c: 3cb000ef jal ra,12866 <memcpy>
11ca0: 04090f63 beqz s2,11cfe <_fgets_r+0x9a>
11ca4: 441c lw a5,8(s0)
(_fgets_r 被 fgets 调用)
0000000000011d26 <fgets>:
11d26: 87aa mv a5,a0
11d28: 8401b503 ld a0,-1984(gp) # 1f670 <_impure_ptr>
11d2c: 86b2 mv a3,a2
11d2e: 862e mv a2,a1
11d30: 85be mv a1,a5
11d32: bf0d j 11c64 <_fgets_r>
memcpy 出了点问题,给我留下了一个空指针的等价物,但我不清楚是什么 - 但我更基本的问题是:我有基本程序吗调用 fgets 正确吗?
我相信我以前有过并且没有遇到过任何问题,但是关于这个的文档很少而且相差很远,所以确认会很有帮助并且至少会建议我更仔细地查看 mmap 代码。
当然,如果有人能发现任何其他错误,我们将不胜感激。
通过反复打开文件,我的代码占用了越来越多的内存,最终覆盖了通过 mmap 分配的部分内存范围。
我通过将文件指针的值存储在 .bss (inputfileptr) 中并只打开一次来解决这个问题:
la t0, inputfileptr
lw a0, 0(t0)
bne a0, zero, getstdin_getin
la a1, stdinopen
call fdopen
la t0, inputfileptr
sw a0, 0(t0)
getstdin_getin:
PUSH a0 #store value on stack
更新:写出来让我发现我哪里出错了,但不知道为什么。我显然以错误的方式调用 fgets,因为在五次调用之后我到达了地址 0x221000,这是映射内存所在的位置——我在更高的地址写入——但我不知道为什么会这样。有人可以解释一下吗?
这有点复杂,我不知道为什么会出现这种行为:我不知道是我的基础知识有误,还是 Spike/PK 的一个特性。
注意:这里的libc由newlib提供,代码编译为riscv64-unknown-elf。
精简版 我输入了用 RISC-V 汇编编写的代码,之前 运行 很顺利,但是自从我引入了对 mmap 的系统调用后,它第五次崩溃了它被执行了。问题是因为我的调用顺序错误还是 Spike 模拟器和 PK 代理内核可能有问题?
详细解释
我正在编写一种类似于 Forth 的线程解释语言。它目前针对 Spike 模拟器上的 PK 代理内核,但希望很快在 'real' 硬件上 运行。代码位于 https://github.com/mcmenaminadrian/riscyforth
TIL 实现了一个无限循环来获取输入,依次调用获取标准输入文件指针的例程,然后是获取输入的例程。
获取标准输入文件指针(存储在堆栈中):
mv a0, zero
la a1, stdinopen
call fdopen
addi sp, sp, -8
sw a0 0(sp)
(stdinopen 在 .data 部分中作为 .asciz“r”)
然后获取输入 - 将其弹出堆栈...
lw a2, 0(sp)
addi sp, sp, 8
la a0, INPUT_BUFFER
li a1, BUFFERLEN #0x200
call fgets
INPUT_BUFFER 在 .bss 部分定义为 .common INPUT_BUFFER, BUFFERLEN, 8
这一切都很好,直到我添加了映射到内存中的新代码 R/W/X - 如果您了解 Forth,您就会知道我需要具有这组不寻常权限的内存,因为我必须允许用户定义新关键字。
现在尝试获取输入在第四次调用时失败。 (尽管如果我定义了 mmap,问题就消失了。)
这是一个失败的例子:
欢迎来到 RISCYFORTH - 版权所有 Adrian McMenamin 2020 - 2021
RISCYFORTH 是一种基于 FORTH 的线程解释语言。 目前我们 运行 处于 EXECUTE 模式,您输入的任何内容都会 在输入每一行时执行。 RISCYFORTH 根据 GNU 通用 Public 许可证第 2 版获得许可。 参见 https://github.com/mcmenaminadrian/riscyforth.git
346 5622 * dup OK
dup OK
dup OK
. 1945212 OK
z 0000000000000000 ra 0000000000010442 sp 0000003ffffffaf8 gp 000000000001fe30 tp 0000000000000000 t0 000000000009fa60 t1 000000000000000a t2 0000000000000003 s0 0000000000000000 s1 0000000000000000 a0 000000000001e530 a1 000000000009fa60 a2 0000000000000200 a3 0000000000000000 a4 0000000000221000 a5 0000000000000001 a6 000000000001ec88 a7 00000000000000d6 s2 00000000000001ff s3 000000000009fa60 s4 000000000001e530 s5 000000000009fa60 s6 0000000000000000 s7 0000000000011b64 s8 0000000000010400 s9 000000000011fc58 sA 000000000009f950 sB 0000000000000000 t3 0000000000000064 t4 0000003ffffffb08 t5 0000000000000054 t6 0000000000000054 pc 0000000000011ca4 va/inst 0000000000000008 sr 8000000200006020 User load segfault @ 0x0000000000000008
这里是代码失败的地方:
0000000000011c64 <_fgets_r>:
11c64: 4785 li a5,1
11c66: 0ac7de63 bge a5,a2,11d22 <_fgets_r+0xbe>
11c6a: 7139 addi sp,sp,-64
11c6c: f822 sd s0,48(sp)
11c6e: f04a sd s2,32(sp)
11c70: ec4e sd s3,24(sp)
11c72: e852 sd s4,16(sp)
11c74: fc06 sd ra,56(sp)
11c76: f426 sd s1,40(sp)
11c78: e456 sd s5,8(sp)
11c7a: e05a sd s6,0(sp)
11c7c: 8932 mv s2,a2
11c7e: 8a2a mv s4,a0
11c80: 89ae mv s3,a1
11c82: 8436 mv s0,a3
11c84: c119 beqz a0,11c8a <_fgets_r+0x26>
11c86: 493c lw a5,80(a0)
11c88: cbc1 beqz a5,11d18 <_fgets_r+0xb4>
11c8a: 397d addiw s2,s2,-1
11c8c: 8ace mv s5,s3
11c8e: a819 j 11ca4 <_fgets_r+0x40>
11c90: 601c ld a5,0(s0)
11c92: 9f05 subw a4,a4,s1
11c94: 9aa6 add s5,s5,s1
11c96: 94be add s1,s1,a5
11c98: c418 sw a4,8(s0)
11c9a: e004 sd s1,0(s0)
11c9c: 3cb000ef jal ra,12866 <memcpy>
11ca0: 04090f63 beqz s2,11cfe <_fgets_r+0x9a>
11ca4: 441c lw a5,8(s0)
(_fgets_r 被 fgets 调用)
0000000000011d26 <fgets>:
11d26: 87aa mv a5,a0
11d28: 8401b503 ld a0,-1984(gp) # 1f670 <_impure_ptr>
11d2c: 86b2 mv a3,a2
11d2e: 862e mv a2,a1
11d30: 85be mv a1,a5
11d32: bf0d j 11c64 <_fgets_r>
memcpy 出了点问题,给我留下了一个空指针的等价物,但我不清楚是什么 - 但我更基本的问题是:我有基本程序吗调用 fgets 正确吗?
我相信我以前有过并且没有遇到过任何问题,但是关于这个的文档很少而且相差很远,所以确认会很有帮助并且至少会建议我更仔细地查看 mmap 代码。
当然,如果有人能发现任何其他错误,我们将不胜感激。
通过反复打开文件,我的代码占用了越来越多的内存,最终覆盖了通过 mmap 分配的部分内存范围。 我通过将文件指针的值存储在 .bss (inputfileptr) 中并只打开一次来解决这个问题:
la t0, inputfileptr
lw a0, 0(t0)
bne a0, zero, getstdin_getin
la a1, stdinopen
call fdopen
la t0, inputfileptr
sw a0, 0(t0)
getstdin_getin:
PUSH a0 #store value on stack