MIPS汇编文件读取陷入无限循环问题求助
MIPS文件加密程序读取异常排查
问题症状
编写的MIPS汇编加密程序在读取文本文件时,无法识别文件末尾,会持续读取直到内存空间耗尽,系统调用返回的读取字节数为0xffffffff(即-1)。
错误细节
- 读取操作返回值异常:文件读取系统调用后
$v0寄存器值为0xffffffff,而非预期的正整数(读取字节数)或正确的文件末尾标识 - 程序陷入无限循环:未正确处理文件读取结束的情况,导致反复执行读取操作
相关代码
.include "SysCalls.asm" .eqv $key $a2 .eqv $filename $a3 .eqv $descriptor $t1 .eqv textBufSize 1024 .eqv maxKeySize 60 .eqv fnameSize 255 .eqv readOnly 0 .eqv writeOnlyCreate 1 .eqv writeOnlyAppend 9 .eqv sframe 16 .data encryptOption: .asciiz "1: Encrypt the File \n" decryptOption: .asciiz "2: Decrypt the File \n" exitOption: .asciiz "3: Exit \n" keyPrompt: .asciiz "Enter a key (up to 60 characters): \n" filenamePrompt: .asciiz "Enter a filename (up to 255 characters): \n" notFound: .asciiz "File Error! \n" filenameBuf: .space fnameSize keyBuffer: .space maxKeySize textBuffer: .space textBufSize .align 2 keySize: .word 0 .text .eqv Encrypt 1 .eqv Decrypt 2 .eqv Exit 3 printMenu: la $a0, encryptOption li $v0, SysPrintString syscall la $a0, decryptOption li $v0, SysPrintString syscall la $a0, exitOption li $v0, SysPrintString syscall getMenuOption: li $v0, SysReadInt syscall beq $v0, Encrypt, startEncrypt beq $v0, Decrypt, startDecrypt beq $v0, Exit, exit j getMenuOption startEncrypt: la $a0, filenamePrompt la $a1, filenameBuf li $a2, fnameSize jal getString li $a1, readOnly li $a2, 0 la $a0, filenameBuf jal openFile bltz $descriptor, printMenu # go to file error if it's not found la $a0, keyPrompt la $a1, keyBuffer li $a2, maxKeySize jal getString beqz $a3, fileError # making sure key isn't empty (0 length) by checking # of bytes read sw $a3, keySize la $t0, textBuffer # temp ptr to text la $t1, keyBuffer # temp ptr to key li $t2, 0 # loading data counter w/ 0 li $t3, 0 # loading key counter lw $t4, keySize getData: move $a0, $descriptor # move the file descriptor to argument reg to pass during jal la $a1, textBuffer li $a2, textBufSize li $t2, 0 # loading data counter w/ 0 jal readFile beq $a3, 0, printMenu encrypt: beq $t2, $a3, getData # if read in 1024 bytes, get next data block beq $t3, $t4, resetKey # if read in full key, go to reset key data pointer lb $s0, 0($t0) # store first byte of text into $s0 beq $s0, '\n', skipNL # if this byte is NL skip it and return lb $s1, 0($t1) # get next key byte addu $s0, $s1, $s0 # encrypt s0 data with key sb $s0, 0($t0) # store encrypted data back into text buffer addi $t0, $t0, 1 # increment text buffer data pointer addi $t1, $t1, 1 # increment key buffer data pointer addi $t2, $t2, 1 # increment data counter addi $t3, $t3, 1 # increment key counter j encrypt resetKey: la $t0, keyBuffer subu $t3, $t3, maxKeySize j encrypt skipNL: addi $t0, $t0, 1 addi $t3, $t3, 1 j encrypt startDecrypt: j printMenu #------------------------------------------------------------------------------ # void readFile(int descriptor, char[] buffer, int maxSize) # # - routine to open a file with name "filename" # # input: $a0 --> file descriptor # $a1 --> address of input buffer # ` $a2 --> max number of characters to read # # return: $a3 --> number of bytes read in #------------------------------------------------------------------------------ readFile: subi $sp, $sp, 4 # reserving space on stack and preserving state sw $ra, 0($sp) li $v0, SysReadFile syscall move $a3, $v0 lw $ra, 0($sp) addi $sp, $sp, 4 jr $ra # return to calling function #------------------------------------------------------------------------------ # void openFile(string filename, int flags, int mode) # # - routine to open a file with name "filename" # # input: $a0 --> filename buffer - null terminated # $a1 --> flags # ` $a2 --> mode # # return: $t1 --> file descriptor #------------------------------------------------------------------------------ openFile: subi $sp, $sp, sframe # reserving space on stack and preserving state sw $a0, 0($sp) sw $a1, 4($sp) sw $a2, 8($sp) sw $ra, 12($sp) li $v0, SysOpenFile syscall move $descriptor, $v0 # save file descriptor from v0 bltz $v0, fileError # go to file error if it's not found j retOpenFile fileError: la $a0, notFound li $v0, SysPrintString syscall retOpenFile: lw $a0, 0($sp) # restore state and remove stack frame lw $a1, 4($sp) lw $a2, 8($sp) lw $ra, 12($sp) addi $sp, $sp, sframe jr $ra # return to calling function #------------------------------------------------------------------------------ # void getString(char[] buffer, int maxSize, String prompt) # # - routine to read in string and strip out ending "\n" # # input: $a0 --> prompt # $a1 --> location of where to store the string # $a2 --> max size of the string to read in # # return: $a3 --> # of bytes stored in the "buffer" #------------------------------------------------------------------------------ .eqv $aRetValue $a3 .eqv $tPrompt $t0 .eqv $tCounter $t1 .eqv $tBuffer $t2 .eqv $tMaxSize $t3 getString: subi $sp, $sp, sframe # reserving space on stack and preserving state sw $a0, 0($sp) sw $a1, 4($sp) sw $a2, 8($sp) sw $ra, 12($sp) move $tPrompt, $a0 move $tBuffer, $a1 move $tMaxSize, $a2 li $v0, SysPrintString syscall move $a0, $tBuffer move $a1, $tMaxSize li $v0, SysReadString syscall # removes NL character from the end of the string and replace it will null move $a0, $tBuffer # put filename address in a0 for cleaning li $tCounter, 0 # initializing counter to 0 nextChar: lb $t0, 0($a0) addi $tCounter, $tCounter, 1 # incrementing counter bne $t0, '\n', skipChar # if it is not new line character, then skip the character li $t0, 0 # if it is new line character, change it to null terminator sb $t0, 0($a0) # putting the null terminator into place where NL is j retGetString # return to caller skipChar: addi $a0, $a0, 1 # increment filename data pointer j nextChar retGetString: move $aRetValue, $tCounter # return the number of bytes in the string buffer lw $a0, 0($sp) # restore state and remove stack frame lw $a1, 4($sp) lw $a2, 8($sp) lw $ra, 12($sp) addi $sp, $sp, sframe jr $ra # return to calling function #------------------------------------------------------------------------------ # EXIT from Program #------------------------------------------------------------------------------ exit: li $v0, SysExit syscall
问题根源与修复方案
1. 文件结束判断逻辑错误
MIPS系统调用中,SysReadFile(通常为系统调用码14)的返回规则:
- 成功读取:返回读取到的字节数(正整数)
- 文件末尾/读取失败:返回负数(通常为-1,即
0xffffffff)
原代码中使用beq $a3, 0, printMenu判断文件结束,完全不符合系统调用的返回规则,导致无法识别文件末尾。
修复:
将getData中的判断改为检测返回值是否为负数:
getData: move $a0, $descriptor la $a1, textBuffer li $a2, textBufSize li $t2, 0 jal readFile bltz $a3, exitEncrypt # 读取到末尾或失败,退出加密流程 beqz $a3, exitEncrypt # 兼容部分环境返回0的情况 encrypt: beq $t2, $a3, processBuffer # 处理完当前缓冲区,先写入再读下一块 ...
2. 缺失加密后数据写入与文件关闭
原代码只完成了缓冲区加密,但没有将加密后的内容写入输出文件,也没有关闭打开的文件,会导致资源泄漏且加密结果无法保存。
补充核心代码:
startEncrypt: # ... 原有打开输入文件、获取密钥代码 ... # 创建输出文件(示例:原文件名后加.enc) la $a0, filenameBuf la $a1, filenameBuf li $t5, 0 findEnd: lb $t6, 0($a0) beqz $t6, appendExt addi $a0, $a0, 1 addi $t5, $t5, 1 j findEnd appendExt: la $t7, ".enc" li $t8, 0 appendLoop: lb $t6, 0($t7) beqz $t6, createOutput sb $t6, 0($a0) addi $a0, $a0, 1 addi $t7, $t7, 1 j appendLoop createOutput: sb $zero, 0($a0) la $a0, filenameBuf li $a1, writeOnlyCreate li $a2, 0644 jal openFile move $t5, $descriptor # 保存输出文件描述符到$t5 # ... 原有getData、encrypt循环代码 ... processBuffer: # 写入加密后的缓冲区到输出文件 move $a0, $t5 la $a1, textBuffer move $a2, $a3 li $v0, SysWriteFile syscall # 重置缓冲区与密钥指针 la $t0, textBuffer la $t1, keyBuffer li $t3, 0 j getData exitEncrypt: # 关闭输入和输出文件 li $v0, SysCloseFile move $a0, $descriptor syscall move $a0, $t5 syscall j printMenu
3. 密钥重置逻辑错误
原resetKey中错误地重置了文本缓冲区指针$t0,应该重置的是密钥指针$t1,且应使用实际密钥长度而非固定值:
resetKey: la $t1, keyBuffer # 修正为重置密钥指针 subu $t3, $t3, $t4 # 使用实际密钥长度$t4 j encrypt
总结
核心问题是对MIPS文件读取系统调用的返回值规则理解错误,导致无法识别文件末尾。补充缺失的文件写入和关闭逻辑、修正密钥重置的指针错误后,即可解决无限读取的问题。
内容的提问来源于stack exchange,提问作者JG98
相关产品推荐
相关产品推荐

