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

x86_64汇编中创建栈缓冲区打印数字及段错误排查

x86_64 NASM汇编:解决_printNum函数段错误与栈缓冲区创建方法

在Linux环境下使用NASM编写x86_64汇编代码时,实现_printNum函数打印RAX中存储的数字,出现乱码O=N84NioR/\_dsPfec IS/pRDNui/0SmL3l及Segmentation fault错误。尝试两种实现方式均触发段错误:

  • 第一种:逐个弹出余数转ASCII后调用sys_write打印
  • 第二种:将转换后的ASCII字符存入缓冲区再打印

经调试确认,问题根源在于未正确使用栈上缓冲区存储数字字符,需将转换后的字符存入栈上缓冲区后,再将缓冲区地址传递给sys_write的rsi参数。以下是两种错误实现的代码,以及正确的栈缓冲区创建与使用方法。

第一种错误实现代码

_printNum:  ;prints number stored in RAX 

    push rax                ;push number onto stack
    mov rcx, 0              ;set counter to 0
    mov rbx, 10             ;set divisor to 10
    _pnl:   ;loop1 - extract digits 

        mov rdx, 0          ;set rdx to 0
        div rbx             ;divide number by 10
        push rdx            ;push remainder
        inc rcx             ;increment counter
        cmp rax, 0          ;repeat until quotient = 0
        jne _pnl
    _pnl2:  ;loop2 - reverse and print

        mov rdi, 1          ;stdout
        mov rdx, 1          ;length of 1 byte
        pop rax             ;pop remainder
        lea rsi, [rax + '0'];convert digit into ASCII character and move it into rsi
        mov rax, 1          ;sys_write
        syscall             ;print digit

        dec rcx             ;decrement counter
        cmp rcx, 0          ;repeat until counter = 0
        jne _pnl2
    pop rax                 ;restore number into rax
    ret

错误分析:lea rsi, [rax + '0'] 是非法操作,sys_write要求rsi指向合法的内存地址,此处直接使用寄存器+常量的偏移并非可访问内存,触发段错误。

第二种错误实现代码

_printNum:  ;prints number stored in RAX 

    push rax                ;push number onto stack
    mov rcx, 0              ;set counter to 0
    mov rbx, 10             ;set divisor to 10
    _pnl:   ;loop1 - extract digits 

        mov rdx, 0          ;set rdx to 0
        div rbx             ;divide number by 10
        push rdx            ;push remainder
        inc rcx             ;increment counter
        cmp rax, 0          ;repeat until quotient = 0
        jne _pnl
    _pnl2:  ;loop2 - reverse and print

        pop rax             ;pop remainder
        add rax, '0'        ;convert digit to ASCII
        mov [rsi + rcx], rax;add digit to buffer
        dec rcx             ;decrement counter
        cmp rcx, 0          ;repeat until counter = 0
        jne _pnl2
    mov rax, 1              ;sys_write
    mov rdx, rcx            ;length of number
    mov rdi, 1              ;stdout
    syscall                 ;print number
    pop rax                 ;restore number into rax
    ret

错误分析:rsi未初始化,指向随机内存地址,写入该地址触发段错误;同时最后mov rdx, rcx时rcx已为0,导致sys_write打印长度为0,无法输出内容。

正确实现:栈上创建缓冲区打印数字

_printNum:
    push rbx                ; 保存被调用者保存寄存器(x86_64 SysV ABI要求)
    push rcx
    push rdx

    mov rcx, 0              ; 数字位数计数器
    mov rbx, 10             ; 除数为10

    ; 第一步:分解数字,将余数压栈
    _pn_extract:
        xor rdx, rdx        ; 清零rdx,div使用rdx:rax作为被除数
        div rbx             ; rax = rax / 10, rdx = rax % 10
        push rdx            ; 保存余数
        inc rcx             ; 位数+1
        test rax, rax       ; 判断商是否为0
        jnz _pn_extract

    ; 第二步:在栈上开辟缓冲区,将余数转换为ASCII存入缓冲区
    sub rsp, 20             ; 开辟20字节缓冲区(足够存储64位整数的所有数字)
    mov rsi, rsp            ; rsi指向栈缓冲区起始地址
    mov rdx, rcx            ; 保存数字长度到rdx

    _pn_convert:
        pop rax             ; 弹出余数
        add al, '0'         ; 转换为ASCII字符(用al避免高位干扰)
        mov [rsi], al       ; 存入缓冲区
        inc rsi             ; 缓冲区指针后移
        dec rcx
        jnz _pn_convert

    ; 第三步:调用sys_write打印缓冲区内容
    mov rax, 1              ; sys_write系统调用号
    mov rdi, 1              ; stdout文件描述符
    syscall

    ; 恢复栈空间和寄存器
    add rsp, 20             ; 释放栈缓冲区
    pop rdx
    pop rcx
    pop rbx
    ret

栈缓冲区创建说明

  1. 开辟缓冲区:使用sub rsp, size指令在栈上预留空间,此处预留20字节足够存储64位无符号整数的最大位数(20位)。
  2. 获取缓冲区地址:rsp寄存器此时指向缓冲区起始地址,将其赋值给rsi即可作为sys_write的内存参数。
  3. 释放缓冲区:函数返回前用add rsp, size恢复栈指针,避免栈溢出。
  4. 寄存器保护:x86_64 SysV ABI中rbx、rcx、rdx属于被调用者保存寄存器,需在函数开头压栈保存,结尾弹栈恢复。

内容的提问来源于stack exchange,提问作者cuper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 14:13:14