因此,在过去的几个小时里,我一直在尝试学习MIPS,但我遇到了一个问题,我认为这是由于我对MIPS如何进行动态链接的调用缺乏了解(与很大程度上未记录的GAS伪操作)。
据我目前的理解是:
.cpload $t9
只是通知汇编程序您想要获取全局指针的地址(其中所有指向动态链接函数的jal
指令实际上都是jalr $t9
)和.cprestore *offset*
告诉汇编器将$gp
的值存储在*offset*($sp)
处,以便在动态链接的函数调用期间不会破坏它。
我在接连调用libc函数方面取得了一定的成功,但是,我最终还是遇到了段错误,我认为这些段错误与我不知道的某种形式的破坏有关。 >
有问题的代码如下:
.data
in: .asciz "%d"
out: .asciz "%d\n"
.text
.globl main
main:
.ent main
.frame $sp, 32, $ra
.set noreorder
.cpload $t9
.set reorder
addiu $sp, $sp, -32
sw $ra, ($sp)
.cprestore 4
addiu $a1, $sp, 8
la $a0, in
jal scanf
lw $a1, 8($sp)
la $a0, out
li $v0, 0
jal printf
lw $ra, ($sp)
li $v0, 0
jr $ra
addiu $sp, $sp, 32
.end main
在我为scanf
输入值之后,代码出现段错误。使用qemu的tty报告:
[14517.685107] do_page_fault(): sending SIGSEGV to clz for invalid read access from 00000000
[14517.685474] epc = 00000000 in clz[56255000+1000]
[14517.685680] ra = 56255830 in clz[56255000+1000]
我认为问题可能是由加载和跳转指令后的分支延迟槽引起的,但是我隐约读到.set reorder
允许汇编程序用nop
(或其他)修补它们。不影响上一条指令的指令)。
所以,我不知道为什么会这样。我不认为这完全是printf
的问题,因为我可以用立即数来称呼它,并且在调用其他libc函数(例如{{1} }。
如果有人能发现问题和/或深入了解如何以惯用的方式进行动态呼叫,我将非常感激(我在网络上找不到很少的信息,而free
却忽略了很多细节-例如符号)。
我想澄清的是:
1)一个人应该多久使用一次objdump
?这样做一次是否意味着每个动态调用都会获取保留的值(只要不更改堆栈配置)?如果扩展堆栈,然后进行动态调用,是否需要使用.cprestore
来通知汇编程序.cprestore
与$gp
的相对位置不同。
2)在执行可以使用分支延迟时隙进行的指令时,应该明确一点吗? (例如:我应该在某些指令后明确编写$sp
还是由汇编程序代我处理?)
3)我读过MIPS堆栈必须是双字(8字节)对齐的吗?这是真的?我肯定已经遇到了其他与字对齐的堆栈帧大小(甚至存储偏移)的问题。
我知道这是一个漫长而复杂的问题,但我确实感到茫然。感谢您的阅读,我们将不胜感激。