如何创建一个mips程序,以便在main函数中打印出version1,然后将整个代码复制到内存中,最后执行复制的版本。复制的代码版本必须打印版本2。除了版本1和版本2之外,您无法在数据部分中添加任何内容。
如何将整个代码复制到内存中并执行它?我之前从未做过这样的事情所以我不知道从哪里开始。
.data
version1: .asciiz "This is version1"
version2: .asciiz "this is version2"
main:
li $v0, 4
la $a0, version1
syscall
#(how do I copy code and execute it?????)
答案 0 :(得分:1)
自我修改代码的能力取决于执行环境 使用MARS可以启用此选项。 该代码假设数据存储器具有Little Endianness(不假设代码存储器)。
你的教授可能想要的是:
la
是由ori
和lui
组成的伪指令,因此您可以正确地将要复制的指令计为4个。nop
在程序流程中为四条指令预留空间。复制过程很简单。你可以通过切割器使用标签来获得汇编程序的帮助:只需在代码后面放一个标签进行复制(如果没有则复制之前),然后在这两者之间复制所有数据。
由于我们知道要复制的代码的长度并且它很小,我们可以手动复制。
为了修改复制的代码,我们需要看看它看起来像机器代码
addiu $v0, 0, 4 #24020004
lui $at, HHHH #3c01HHHH
ori $a0, $at, LLLL #3424LLLL
syscall #0000000c
如您所见,您已经替换了第2和第3指令的较低HW 要使用的值是 version2 的地址 可以使用基本位操作来获得该地址的上部和下部HW。
您还必须添加代码才能很好地终止程序。
这是为MARS制作的故意简化工作示例(在设置中激活自我修改代码)。
.data
version1: .asciiz "This is version1"
version2: .asciiz "this is version2"
.text
main:
li $v0, 4 #1 instruction addiu $v0, $0, 4
la $a0, version1 #2 instructions lui $a0, H ori $a0, L
syscall #1 instruction
#Load src and dest address
la $t0, main
la $t1, new_code
#Copy the four words of code
lw $t2, ($t0)
sw $t2, ($t1)
lw $t2, 4($t0)
sw $t2, 4($t1)
lw $t2, 8($t0)
sw $t2, 8($t1)
lw $t2, 0xc($t0)
sw $t2, 0xc($t1)
#Load the address of version2
la $t0, version2
add $t2, $0, $0
lui $t2, 0xffff #t2 = 0ffff0000h
andi $t3, $t0, 0xffff #t3 = Lower HW of address
srl $t0, $t0, 0x10 #t0 = Upper HW of address
#Edit ori $a0, L
lw $t4, 8($t1) #Load the instruction in register
and $t4, $t4, $t2 #Clear lower hw
or $t4, $t4, $t3 #Set lower hw
sw $t4, 8($t1) #Save the instruction
#Edit lui $a0, H
lw $t4, 4($t1) #Load the instruction in register
and $t4, $t4, $t2 #Clear lower hw
or $t4, $t4, $t0 #Set lower hw
sw $t4, 4($t1) #Save the instruction
new_code:
nop
nop
nop
nop
li $v0, 10
syscall
如果您对动态分配内存的更通用版本(使用系统调用9)感兴趣,请对齐返回的指针,复制代码,修改代码并将调用添加到系统调用10,此处为
.data
version1: .asciiz "This is version1"
version2: .asciiz "this is version2"
.text
main:
__copy_start__: #Sign the start of code to copy
li $v0, 4 #1 instruction addiu $v0, $0, 4
la $a0, version1 #2 instruction2 lui $a0, H ori $a0, L
syscall #1 instruction
__copy_end__:
li $v0, 9 #Allocate buffer
li $a0, 27 #16 bytes (4 instructions) + 8 bytes (2 instructions) + 3 byte for aligning
syscall
#Align the pointer by consuming the first bytes (this is usually not needed, just for completeness)
addi $v0, $v0, 3
andi $v0, $v0, 0xfffffffc
#Prepare for the copy
la $t0, __copy_start__ #t0 = Source start
la $t1, __copy_end__ #t1 = Source end (exclusive)
add $t2, $0, $v0 #t2 = Destination start
ori $t4, $0, 1 #t4 = 1: Extra code to be copied 0: Extra code copied
do_copy:
#Move from Source to Dest
lw $t3, ($t0)
sw $t3, ($t2)
#Increment the pointers
addi $t0, $t0, 4
addi $t2, $t2, 4
#If not reached the Source end, copy again
bne $t0, $t1, do_copy
#Copy done
#If the extra code has been copied, do the jump to the new code
beqz $t4, do_jump
#Extra code need to be copied
la $t0, __copy_extra__ #New source start
la $t1, __copy_extra_end__ #New source end
add $t4, $0, $0 #Signal extra code is being copied
#Copy again
b do_copy
do_jump:
#Get the address of version2
la $t0, version2
#Save the low half word into the low halfword of the 3rd instruction (ori $a0, L)
sh $t0, 8($v0)
#Get the upper hw in the lower hw of $t0
srl $t0, $t0, 16
#Save the high half word into the low hw of the 2nd instruction (lui $a0, H)
sh $t0, 4($v0)
#Jump indirect
jr $v0
#Extra code to append to the end of the new code
__copy_extra__:
li $v0, 10
syscall
__copy_extra_end__: