NASM x64汇编调用函数后无法打印计算结果字符的问题排查
NASM x64汇编求和打印无数字输出问题
问题重现
我编写了一个NASM x64汇编程序,实现4和5的求和并打印结果,先完成了C原型:
#include <stdio.h> int sum(int x, int y) { int result = x + y; return result; } int main(void) { int x = 4; int y = 5; int result = sum(x, y); printf("The sum is: %d\n", result); return 0; }
随后改写为汇编程序:
; Nasm 2.15.05 (on Linux x64) STDOUT equ 0x01 ; for RDI (file descriptor) WRITE equ 0x01 ; for RAX (for syscall itself) section .data message db "The sum is: " messageLength equ $ - message section .text global _start _start: mov rax, WRITE mov rdi, STDOUT mov rsi, message mov rdx, messageLength syscall mov rdi, 0x4 mov rsi, 0x5 call _sum mov rcx, rax ; The result of the _sum (stored in RAX register) mov rax, WRITE mov rdi, STDOUT mov rsi, rcx mov rdx, 0x01 syscall ; exit mov rax, 0x3C mov rdi, 0x00 syscall _sum: mov rax, rdi add rax, rsi add rax, '0' ; To make it printable ASCII ret ; Result: 0x39 (A single character '9' in ASCII)
预期输出为The sum is: 9,但实际仅输出The sum is: 。通过GDB调试确认_sum函数计算成功,RAX寄存器中存储了0x39(字符'9'的ASCII值),程序无段错误,编译命令为:
nasm -f elf64 -o procedures_x64.o procedures_x64.asm ld -o procedures_x64 procedures_x64.o
环境:Windows 11下的WSL Ubuntu 22.04,CPU为Intel i9-13900H。
错误原因
核心错误出在write系统调用的参数使用错误:
- Linux x64的
write系统调用(syscall号0x01)要求rsi寄存器传入的是要输出数据的内存地址,而非直接传入字符的ASCII值。 - 你将存储字符'9'的RAX值(0x39)通过RCX中转后放到RSI,相当于让
write去访问内存地址0x39的内容。这个地址属于未映射的低内存区域,write调用会失败(返回错误码,但程序直接执行exit,所以没有触发段错误),因此无法输出数字。
修正方案
需要将字符的ASCII值存储到可访问的内存区域(比如栈或.data段),再让write指向该内存地址。以下是两种可行的修正方式:
方式1:使用栈临时存储
利用栈的可写空间临时存放字符,无需修改.data段:
; Nasm 2.15.05 (on Linux x64) STDOUT equ 0x01 ; for RDI (file descriptor) WRITE equ 0x01 ; for RAX (for syscall itself) section .data message db "The sum is: " messageLength equ $ - message section .text global _start _start: mov rax, WRITE mov rdi, STDOUT mov rsi, message mov rdx, messageLength syscall mov rdi, 0x4 mov rsi, 0x5 call _sum ; 将字符值压入栈,栈顶就是字符的内存地址 push rax mov rax, WRITE mov rdi, STDOUT mov rsi, rsp ; RSI指向栈顶的字符 mov rdx, 0x01 syscall pop rax ; 平衡栈,避免内存泄漏 ; exit mov rax, 0x3C mov rdi, 0x00 syscall _sum: mov rax, rdi add rax, rsi add rax, '0' ; To make it printable ASCII ret ; Result: 0x39 (A single character '9' in ASCII)
方式2:使用.data段预留缓冲区
在.data段专门分配一个字节存储结果字符:
; Nasm 2.15.05 (on Linux x64) STDOUT equ 0x01 ; for RDI (file descriptor) WRITE equ 0x01 ; for RAX (for syscall itself) section .data message db "The sum is: " messageLength equ $ - message result_char db 0 ; 预留一个字节存储结果字符 section .text global _start _start: mov rax, WRITE mov rdi, STDOUT mov rsi, message mov rdx, messageLength syscall mov rdi, 0x4 mov rsi, 0x5 call _sum ; 将字符值写入预留的缓冲区 mov [result_char], al ; 只用低8位即可,因为是单字符 mov rax, WRITE mov rdi, STDOUT mov rsi, result_char mov rdx, 0x01 syscall ; exit mov rax, 0x3C mov rdi, 0x00 syscall _sum: mov rax, rdi add rax, rsi add rax, '0' ; To make it printable ASCII ret ; Result: 0x39 (A single character '9' in ASCII)
验证
编译运行修正后的代码,即可得到预期输出The sum is: 9。
内容的提问来源于stack exchange,提问作者KnightChaser
相关产品推荐
相关产品推荐

