You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 02:35:55