MIPS 程序自我复制?

MIPS program to replicate itself?

如何创建一个 mips 程序,以便在 main 函数中打印出 version1,然后将整个代码复制到内存中,最后执行复制的版本。复制的代码版本必须打印 version2。除了版本 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?????)

自行修改代码的能力取决于执行环境。
使用 MARS 可以启用此选项。 代码假设数据内存采用小端字节序(代码内存没有假设)。

你的教授可能想要的是:

  1. 您认识到 la 是由 orilui 组成的伪指令,因此您正确地将要复制的指令计为四个。
  2. 您在程序流程中为使用 nop 的四个指令保留 space。
  3. 您认识编辑操作数的指令格式。

复制过程很简单。您可以通过切肉刀使用标签来获得汇编器的帮助:只需在要复制的代码之后放置一个标签(如果有 none,则在前面放置一个标签),然后复制这两者之间的所有数据。
由于我们知道要复制的代码长度,而且比较小,所以可以手工复制。

为了修改复制的代码,我们需要看看它看起来像机器码

addiu $v0, 0, 4      #24020004
lui $at, HHHH        #3c01HHHH
ori $a0, $at, LLLL   #3424LLLL
syscall              #0000000c

如您所见,您已经替换了第 2 条和第 3 条指令的低位硬件。
要使用的值是 version2.
的地址 该地址的高位和低位 HW 可以通过基本的位操作获得。

您还必须添加代码才能很好地终止程序。

这里是为 MARS 制作的有意简化的工作示例(在设置中激活自修改代码)。

.data
   version1:    .asciiz   "This is version1"
   version2:    .asciiz   "this is version2"

.text

main:
    li $v0, 4               #1 instruction  addiu $v0, [=11=], 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, [=11=], [=11=] 
    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, [=12=], 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, [=12=], $v0            #t2 = Destination start
    ori $t4, [=12=], 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, [=12=], [=12=]         #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__: