LC-3子程序触发Access Violation错误,请求排查解决
LC-3子程序栈规范备份导致Access Violation错误修复
问题描述
- 开发LC-3的
SUB_GET_STRING子程序,功能为统计用户输入字符数(不含换行符),输入以换行符结束,数组以NULL终止。 - 采用栈规范备份寄存器时,运行触发Access Violation错误,错误发生在执行
HALT相关指令时;改用ST/LD旧方式备份寄存器则正常运行。
错误代码
主程序(x3000)
.ORIG x3000 LEA R1, user_string ;R1 - 字符数组起始地址 LD R6, top_stack_addr ;初始化栈指针 LD R6,SUB_GET_STRING_PTR JSR SUB_GET_STRING_PTR HALT ;-------------- ;本地数据 ;-------------- user_string .BLKW #100 SUB_GET_STRING_PTR .FILL x3400 ;-------------- ;栈顶地址 ;-------------- top_stack_addr .FILL xFE00 .END
子程序(x3400)
.ORIG x3400 ;---------------------------------------------------------------------------------------------------------------- ; Subroutine: SUB_GET_STRING ; 参数(R1): 字符数组起始地址 ; 后置条件: 提示用户输入字符串,以回车键结束,字符存储在R1指向的数组中,数组以NULL终止(不存储换行符) ; 返回值(R5): 读取的非终止符字符数,R1保持初始数组地址不变 ;----------------------------------------------------------------------------------------------------------------- SUB_GET_STRING_3400 ; 按栈规范备份寄存器,先存R7 ADD R6, R6, #-1 STR R7, R6, #0 ADD R6, R6, #-1 STR R5, R6, #0 ADD R6, R6, #-1 STR R4, R6, #0 ADD R6, R6, #-1 STR R3, R6, #0 ADD R6, R6, #-1 STR R2, R6, #0 ADD R6, R6, #-1 STR R1, R6, #0 ;-------------------------------------------------------------------- LEA R0, user_prompt PUTS LD R6, Hex_0A NOT R6, R6 ADD R6, R6, #1 ;R6 = -x0A(换行符补码) AND R5, R5, #0 ;R5初始化为0,用于统计字符数 INPUT_LOOP GETC OUT ADD R3, R0, #0 ;R3暂存输入字符 ADD R3, R3, R6 ;判断是否为换行 BRnp #2 ;非换行则继续 STR R3, R1, #0 ;存储NULL终止符 BR Input_Stop STR R0, R1, #0 ;存储输入字符 ADD R1, R1, #1 ;移动数组指针 ADD R5, R5, #1 ;字符计数+1 BR INPUT_LOOP Input_Stop ;恢复寄存器 LDR R1, R6, #0 ADD R6, R6, #1 LDR R2, R6, #0 ADD R6, R6, #1 LDR R3, R6, #0 ADD R6, R6, #1 LDR R4, R6, #0 ADD R6, R6, #1 LDR R5, R6, #0 ADD R6, R6, #1 LDR R7, R6, #0 ADD R6, R6, #1 ;-------------------------------------------------------------------- RET ;本地数据 user_prompt .STRINGZ "Enter the string: " Hex_0A .FILL x0A ;换行符ASCII值 .END
错误原因分析
- 主程序栈指针被错误覆盖:主程序中先通过
LD R6, top_stack_addr设置栈指针,随后又执行LD R6,SUB_GET_STRING_PTR将R6覆盖为子程序地址,完全破坏了栈的初始设置。 - 子程序复用栈指针R6:LC-3中R6是专用栈指针,子程序中却将R6用来存储换行符的补码,导致栈指针被篡改。后续恢复寄存器时,使用被修改的R6访问内存,必然触发访问违规。
修复后的代码
主程序(x3000)
.ORIG x3000 LEA R1, user_string ; R1 - 字符数组起始地址 LD R6, top_stack_addr ; 初始化栈指针R6到栈顶 LD R2, SUB_GET_STRING_PTR ; R2存储子程序地址 JSRR R2 ; 调用子程序 HALT ; 本地数据 user_string .BLKW #100 SUB_GET_STRING_PTR .FILL x3400 ; 栈顶地址 top_stack_addr .FILL xFE00 .END
子程序(x3400)
.ORIG x3400 SUB_GET_STRING_3400 ; 按栈规范备份寄存器(R7先入栈) ADD R6, R6, #-1 STR R7, R6, #0 ADD R6, R6, #-1 STR R5, R6, #0 ADD R6, R6, #-1 STR R4, R6, #0 ADD R6, R6, #-1 STR R3, R6, #0 ADD R6, R6, #-1 STR R2, R6, #0 ADD R6, R6, #-1 STR R1, R6, #0 ; 输出输入提示 LEA R0, user_prompt PUTS ; 计算换行符的补码(-x0A),存储到R4(不再使用R6) LD R4, Hex_0A NOT R4, R4 ADD R4, R4, #1 AND R5, R5, #0 ; R5初始化为0,用于统计字符数 INPUT_LOOP GETC OUT ADD R3, R0, #0 ; R3暂存输入字符 ADD R3, R3, R4 ; 判断是否为换行(结果为0则是) BRnp CONTINUE_INPUT ; 非换行则继续处理 STR R3, R1, #0 ; 存储NULL终止符 BR Input_Stop CONTINUE_INPUT STR R0, R1, #0 ; 存储输入字符到数组 ADD R1, R1, #1 ; 移动数组指针到下一位 ADD R5, R5, #1 ; 字符计数+1 BR INPUT_LOOP Input_Stop ; 按入栈逆序恢复寄存器 LDR R1, R6, #0 ADD R6, R6, #1 LDR R2, R6, #0 ADD R6, R6, #1 LDR R3, R6, #0 ADD R6, R6, #1 LDR R4, R6, #0 ADD R6, R6, #1 LDR R5, R6, #0 ADD R6, R6, #1 LDR R7, R6, #0 ADD R6, R6, #1 RET ; 本地数据 user_prompt .STRINGZ "Enter the string: " Hex_0A .FILL x0A ; 换行符ASCII值 .END
修复说明
- 主程序中保留栈指针R6的正确初始化,改用R2存储子程序地址,通过
JSRR R2调用子程序,避免破坏栈指针。 - 子程序中改用R4存储换行符补码,严格保留R6作为栈指针的专用性,确保寄存器备份/恢复时访问正确的栈内存地址。
内容的提问来源于stack exchange,提问作者henrypham
相关产品推荐
相关产品推荐

