【问题标题】:Storing keyboard Input in x64 assembly (Mac OS/X)在 x64 程序集中存储键盘输入 (Mac OS/X)
【发布时间】:2026-01-04 08:35:01
【问题描述】:

一段时间以来,我一直在尝试从键盘获取一个数字并将其与堆栈上的值进行比较。如果正确,它将打印“Hello World!”如果不正确,它应该打印出“Nope!”。但是,现在发生的情况是无论调用输入“jne”、打印 nope 还是 segfault。也许你们中的一个可以伸出援助之手。

.section __DATA,__data
    str:
      .asciz "Hello world!\n"

sto:
     .asciz "Nope!\n"

.section __TEXT,__text
.globl _main
_main:
    push %rbp
    mov %rsp,%rbp
    sub $0x20, %rsp
    movl $0x0, -0x4(%rbp)
    movl $0x2, -0x8(%rbp)

    movl $0x2000003, %eax
    mov $0, %edi
    subq $0x4, %rsi
    movq %rsi, %rcx
    syscall

    cmp -0x8(%rbp), %edx
    je L1
    jne L2
    xor %rbx, %rbx
    xor %rax, %rax
    movl $0x2000001, %eax          
    syscall

L1:
    xor %rax, %rax
    movl $0x2000004, %eax         
    movl $1, %edi                 
    movq str@GOTPCREL(%rip), %rsi 
    movq $14, %rdx               
    syscall
    ret

L2:
    xor %eax, %eax
    movl $0x2000004, %eax          
    movl $1, %edi                   
    movq sto@GOTPCREL(%rip), %rsi   
    movq $6, %rdx                 
    syscall
    ret

【问题讨论】:

    标签: macos assembly x86-64 gnu-assembler


    【解决方案1】:

    我将从OS/X Syscall tutorial 开始(在您的情况下是 64 位部分)。它是为 NASM 语法编写的,但重要的信息是 SYSCALL 调用约定的文本和链接。 SYSCALL 表位于此Apple webpage。有关 64 位 OS/X 标准调用约定的更多信息,请参阅System V 64-bit ABI

    SYSCALL 约定的重要性:

    • 参数通过这些寄存器 rdi、rsi、rdx、r10、r8 和 r9 按顺序传递
    • rax 寄存器中的系统调用号
    • 调用是通过syscall指令完成的
    • OS X 的贡献在于您必须将 0x20000000 添加到系统调用号(仍然必须弄清楚原因)

    您的sys_read 系统调用存在许多问题。 SYSCALL 表是这样写的:

    3 AUE_NULL    ALL { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); } 
    

    所以根据调用约定,int fdRDI 中,user_addr_t cbuf(指向保存返回数据的字符缓冲区的指针)在 RSI 中,@987654328 @(缓冲区可以包含的最大字节数)在 RDX 中。

    您的程序段在 ret 上出错,因为您没有正确的函数结尾来匹配顶部的函数序言:

    push %rbp                 #
    mov  %rsp,%rbp            # Function prologue
    

    你需要在底部做相反的事情,在RAX中设置结果代码,然后执行ret。比如:

    mov %rbp,%rsp             # \ Function epilogue
    pop %rbp                  # /
    xor %eax, %eax            # Return value = 0
    ret                       # Return to C runtime which will exit
                              #     gracefully and return to OS
    

    我做了其他一些小的清理工作,但试图保持代码的结构相似。您将不得不学习更多的汇编来更好地理解使用sys_read SYSCALL 的地址设置 RSI 的代码。一般来说,您应该尝试找到一本关于 x86-64 汇编语言编程的好教程/书籍。撰写关于该主题的入门书超出了此答案的范围。

    考虑到上述情况,可能更接近您正在寻找的代码:

    .section __DATA,__data
    str:
        .asciz "Hello world!\n"
    
    sto:
        .asciz "Nope!\n"
    
    .section __TEXT,__text
    
    .globl _main
    _main:
        push %rbp                 #
        mov  %rsp,%rbp            # Function prologue
    
        sub  $0x20, %rsp          # Allocate 32 bytes of space on stack
                                  #     for temp local variables
    
        movl $0x2, -4(%rbp)       # Number for comparison
    
                                  # 16-bytes from -20(%rbp) to -5(%rbp)
                                  #     for char input buffer
        movl $0x2000003, %eax
        mov $0, %edi              # 0 for STDIN
        lea -20(%rbp), %rsi       # Address of temporary buffer on stack
        mov $16, %edx             # Read 16 character maximum
        syscall
    
        movb (%rsi), %r10b        # RSI = pointer to buffer on stack
                                  # get first byte
        subb $48, %r10b           # Convert first character to number 0-9
        cmpb -4(%rbp), %r10b      # Did we find magic number (2)?
        jne L2                    #     If No exit with error message
    
    L1:                           # If the magic number matched print
                                  #     Hello World
        xor %rax, %rax
        movl $0x2000004, %eax
        movl $1, %edi
        movq str@GOTPCREL(%rip), %rsi
        movq $14, %rdx
        syscall
        jmp L0                    # Jump to exit code
    
    L2:                           # Print "Nope"
        xor %eax, %eax
        movl $0x2000004, %eax
        movl $1, %edi
        movq sto@GOTPCREL(%rip), %rsi
        movq $6, %rdx
        syscall
    
    L0:                           # Code to exit main
        mov %rbp,%rsp             # \ Function epilogue
        pop %rbp                  # /
        xor %eax, %eax            # Return value = 0
        ret                       # Return to C runtime which will exit
                                  #     gracefully and return to OS
    

    【讨论】: