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

x86_64汇编LUT转大写程序出现字符异常与截断问题求助

x86_64 Linux汇编大写转换程序的问题及修复

我正在编写一个x86_64 Linux汇编程序,通过预分配的LUT(查找表)将文本中的字母转换为大写。C语言版本逻辑正常,但汇编版遇到两个问题:

  • 字符'A'在Emacs中被替换为奇怪符号^@
  • 文本末尾被截断

猜测是内存寻址错误或文件未正确关闭导致的。尝试调用syscall执行_close时程序有时崩溃,可能是寄存器被污染,但尚未定位到原因。目前使用GDB的r i命令查看寄存器,并用SASM辅助调试,仍未找到问题所在。

编译脚本Make

newupper: newupper.o
    gcc -o newupper newupper.o -no-pie
newupper.o: newupper.asm
    nasm -f elf64 -g -F dwarf newupper.asm -l newupper.lst

汇编测试代码

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; 读取文本文件,将所有小写字母转换为大写后写入新文件
    ;; 使用LUT实现转换:['a']='A',['A']='A'
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    section .bss
    Buff resb 128
    upperlut resb 128
    section .data
        Bufflen dq 128

    UPPERA dq 65
    LOWERA dq 97
    FnameR db "data.txt", 0
    FnameW db "out.txt", 0
    myFd dq 0
    myFd2 dq 0
    section .text
LutInit:
    mov r10, 0
    .L1:
    cmp r10, 128
    jge .L3
    mov [upperlut+r10], r10
        inc r10
    jmp .L1

        .L3:
    xor r10, r10
    mov r10, 0
    mov r11, [UPPERA]
    mov r12, [LOWERA]
    .L2:
    cmp r10, 26
    jge .END
    mov r9, r10
    add r9, [UPPERA]
    mov [upperlut+r10+r11], r9
    mov [upperlut+r10+r12], r9
    inc r10
    jmp .L2
    .END:
OpenFile:
    ;; sys_open 参数:const char *filename, int flags, int mode
    mov r10, rcx          ; 保存指针,避免syscall破坏rcx
    mov rax, 2            ; sys_open的系统调用号
    syscall
    cmp rax, 0
    jle Exit
    mov [r10], rax        ; 将文件描述符写入myFd或myFd2
    ret

global main
main:
    mov rbp, rsp; 方便调试
     ; 打开读取文件
        call LutInit
    mov rdi, FnameR
    mov rsi, 0
    mov rdx, 0
    mov rcx, myFd        
    call OpenFile

    mov rdi, FnameW
    mov rsi, 0102o
    mov rdx, 00600o
    mov rcx, myFd2       
    call OpenFile

Read:
    mov rax, 0
    mov rdi, [myFd]
    mov rsi, Buff
    mov rdx, [Bufflen]
    syscall

    cmp rax, 0
    je Exit

    mov r12, 0
    .L1:
    cmp r12, rax
    jge Write
    ;; 通过LUT转换字符
        movzx r14, byte [Buff+r12]
        movzx r13, byte [upperlut+r14]
        mov byte [Buff+r12], r13b
    inc r12
    jmp .L1
Write:
        xor r15, r15
        mov r15, rax
    mov rax, 1
    mov rdi, [myFd2]
    mov rsi, Buff
    mov rdx, r15
    syscall
    jmp Read
Exit:
        ;; mov rax, 3
        ;; mov rdi, [myFd2]
        ;; syscall
        
        ;; mov rax, 3
        ;; mov rdi, [myFd]
        ;; syscall
        ret

问题分析与修复

1. LUT初始化的内存写入错误

这是导致'A'变成^@的核心原因:
在LutInit的.L1循环中,mov [upperlut+r10], r10是将64位寄存器r10的值写入字节数组upperlut,会一次性覆盖8个字节的内存。比如当r10=65(对应字符'A')时,这条指令会把upperlut+65到upperlut+72的8个字节全部设为65的64位值(低字节65,高字节0),后续的LUT赋值会被覆盖,最终读取upperlut+65时得到空字符(0,显示为^@)。

修复:将64位寄存器改为8位寄存器,只写入单个字节:

; .L1循环中修改
mov [upperlut+r10], r10b

; .L2循环中修改
mov [upperlut+r10+r11], r9b
mov [upperlut+r10+r12], r9b

2. 文件未关闭导致缓存未刷新

注释掉的sys_close调用会导致操作系统的文件缓存未完全写入磁盘,从而出现文本末尾截断的情况。直接ret返回main函数时,gcc运行时库会清理资源,但手动关闭文件更安全。

修复:取消注释Exit段的close调用,确保先关写入文件,再关读取文件:

Exit:
    mov rax, 3            ; sys_close的系统调用号
    mov rdi, [myFd2]
    syscall               ; 关闭写入文件

    mov rax, 3
    mov rdi, [myFd]
    syscall               ; 关闭读取文件
    ret

额外优化:OpenFile错误处理

当sys_open失败时(rax<=0),直接跳转到Exit会导致已打开的文件未被关闭。可以在OpenFile中添加局部错误处理,或者在Exit中检查文件描述符是否有效再关闭。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 17:53:09