IEEE-754单精度浮点数解码程序出现Segmentation Fault请求排查
IEEE-754单精度浮点数解码程序段错误问题修复
我正在编写一个读取并解码IEEE-754单精度浮点数的程序,要求持续处理输入直到用户输入0。对每个输入数字,要解码为科学计数法显示,计算并显示其平方值,非负数还要计算显示平方根。但目前程序只有输入0能正常退出(退出码0),其他输入都会触发Segmentation Fault。怀疑是寄存器保存/恢复环节出问题,但不确定。另外要求不大幅修改代码,且禁止使用PUSH、POP、条件执行以及VLDMIA/VSTMDB或STMFD/LDMFD指令。
原代码
.data prompt: .asciz "\nEnter the single precision floating point value (0 to exit): " initValMsg: .asciz "The initial value is: " squareMsg: .asciz "The value squared is: " rootMsg: .asciz "The root of the value is: " newline: .asciz "\n" scanfFormat: .asciz "%f" printfFormat: .asciz "%d" plusChar: .byte '+', 0 minusChar: .byte '-', 0 oneDot: .asciz "1." expChar: .asciz " E" .text .global main main: STMDB SP!, {R4, LR} main_loop: LDR R0, =prompt BL printf SUB SP, SP, #8 MOV R1, SP LDR R0, =scanfFormat BL scanf LDR R0, [SP] ADD SP, SP, #8 CMP R0, #0 BEQ end_program MOV R4, R0 LDR R0, =initValMsg BL printf MOV R0, R4 BL decode_and_display VMOV S0, R4 VMUL.F32 S0, S0, S0 VMOV R0, S0 LDR R0, =squareMsg BL printf BL decode_and_display MOVS R4, R4 BMI skip_sqrt VMOV S0, R4 VSQRT.F32 S0, S0 VMOV R0, S0 LDR R0, =rootMsg BL printf BL decode_and_display skip_sqrt: B main_loop end_program: LDMIA SP!, {R4, LR} MOV R0, #0 BX LR decode_and_display: STMDB SP!, {R4-R6, LR} MOV R4, R0 MOVS R4, R4 BPL positive_sign LDR R0, =minusChar BL printf B print_mantissa positive_sign: LDR R0, =plusChar BL printf print_mantissa: LDR R0, =oneDot BL printf MOV R5, R4, LSL #1 MOV R5, R5, LSR #24 SUB R5, R5, #127 MOV R6, R4, LSL #9 MOV R2, #23 print_mantissa_loop: CMP R2, #0 BEQ print_exponent MOVS R6, R6, LSL #1 BPL mantissa_bit_zero MOV R0, #'1' BL putchar B mantissa_next_bit mantissa_bit_zero: MOV R0, #'0' BL putchar mantissa_next_bit: SUB R2, R2, #1 B print_mantissa_loop print_exponent: LDR R0, =expChar BL printf MOV R0, R5 LDR R1, =printfFormat BL printf LDR R0, =newline BL printf LDMIA SP!, {R4-R6, LR} BX LR
错误分析
- 浮点数读取方式错误:
scanf用%f读取的是32位float类型,直接用LDR R0, [SP]把栈中数据当作整数加载,会破坏浮点数的二进制表示,后续解码和计算都会出错。 - 寄存器调用约定违反:ARM APCS调用约定中,
printf等函数会破坏R0-R3寄存器。在计算平方/平方根后,把结果放到R0,随后立刻用LDR R0, =squareMsg覆盖了R0,导致传给decode_and_display的参数变成字符串地址而非浮点数的二进制表示,直接引发段错误。 - 栈空间处理冗余:
scanf读取float仅需4字节栈空间,虽然8字节对齐符合ARM要求,但读取方式错误才是核心问题。
修复后的代码(最小修改)
.data prompt: .asciz "\nEnter the single precision floating point value (0 to exit): " initValMsg: .asciz "The initial value is: " squareMsg: .asciz "The value squared is: " rootMsg: .asciz "The root of the value is: " newline: .asciz "\n" scanfFormat: .asciz "%f" printfFormat: .asciz "%d" plusChar: .byte '+', 0 minusChar: .byte '-', 0 oneDot: .asciz "1." expChar: .asciz " E" .text .global main main: STMDB SP!, {R4, LR} main_loop: LDR R0, =prompt BL printf SUB SP, SP, #8 MOV R1, SP LDR R0, =scanfFormat BL scanf ; 修复:用浮点指令加载float,再转存到R0 VLDR S0, [SP] VMOV R0, S0 ADD SP, SP, #8 CMP R0, #0 BEQ end_program MOV R4, R0 LDR R0, =initValMsg BL printf MOV R0, R4 BL decode_and_display ; 修复:保存平方结果到R1,避免被printf覆盖 VMOV S0, R4 VMUL.F32 S0, S0, S0 VMOV R1, S0 LDR R0, =squareMsg BL printf MOV R0, R1 BL decode_and_display MOVS R4, R4 BMI skip_sqrt ; 修复:保存平方根结果到R1,避免被printf覆盖 VMOV S0, R4 VSQRT.F32 S0, S0 VMOV R1, S0 LDR R0, =rootMsg BL printf MOV R0, R1 BL decode_and_display skip_sqrt: B main_loop end_program: LDMIA SP!, {R4, LR} MOV R0, #0 BX LR decode_and_display: STMDB SP!, {R4-R6, LR} MOV R4, R0 MOVS R4, R4 BPL positive_sign LDR R0, =minusChar BL printf B print_mantissa positive_sign: LDR R0, =plusChar BL printf print_mantissa: LDR R0, =oneDot BL printf MOV R5, R4, LSL #1 MOV R5, R5, LSR #24 SUB R5, R5, #127 MOV R6, R4, LSL #9 MOV R2, #23 print_mantissa_loop: CMP R2, #0 BEQ print_exponent MOVS R6, R6, LSL #1 BPL mantissa_bit_zero MOV R0, #'1' BL putchar B mantissa_next_bit mantissa_bit_zero: MOV R0, #'0' BL putchar mantissa_next_bit: SUB R2, R2, #1 B print_mantissa_loop print_exponent: LDR R0, =expChar BL printf MOV R0, R5 LDR R1, =printfFormat BL printf LDR R0, =newline BL printf LDMIA SP!, {R4-R6, LR} BX LR
修复细节说明
- 用
VLDR S0, [SP]加载scanf读取的float,再通过VMOV R0, S0转存到R0,保证浮点数的二进制表示正确传递。 - 计算平方和平方根后,将结果保存到R1(非临时寄存器,调用printf后不会被破坏),等printf输出完提示信息后,再把R1的值放回R0传给
decode_and_display。 - 保留了原代码的寄存器推/弹方式(STMDB/LDMIA),符合禁止指令的要求,没有大幅修改原有结构。
内容的提问来源于stack exchange,提问作者Userdargo
相关产品推荐
相关产品推荐

