在汇编中获取字符串的第一个字符

Getting first character of string in assembly

我正在尝试编写一个比较单个字符的程序,但它说缓冲区不等于 'S' 即使我给了一个作为输入,我知道有一个换行符和终止符在输入字符串之后但 [.buffer] 不只是应该给出字符串的第一个字符吗?

section .text
    global main

main:
    jmp start_promt

start_promt:
section .data
    .promt       db      "Choose one of the following instructions: (S)tart, (Q)uit",10,0
    .promtLen    equ     $-.promt-1   ; memory address of begin of this instruction minus memory address of begin of promt gives length of promt  
    .bufsize     dq      100
section .bss
    .buffer resd 100
section .text
    mov     rax, 1          ; rax <- 0 (syscall number for 'write')
    mov     rdi, 1          ; rdi <- 0 (stdout file descriptor)
    mov     rsi, .promt      ; address of prompt message
    mov     rdx, .promtLen   ; size of promt message
    syscall                 ; execute  write(1, promt, promtLen)

    xor rax, rax            ; rax <- 0 (syscall number for 'read')
    xor rdi, rdi            ; rdi <- 0 (stdin file descriptor)
    mov rsi, .buffer         ; rsi <- address of the buffer.  lea rsi, [rel buffer]
    mov rdx, [.bufsize]        ; rdx <- size of the buffer
    syscall                 ; execute  read(0, buffer, BUFSIZE)

    mov rdx, [.buffer]

    cmp rdx, 'S'
    je start_game 

    cmp rdx, 'Q'
    je quit


    jmp start_promt

start_game:
; loop for user-instructions
; if done
    ;jmp start_promt

quit: 
    mov rax, 60
    mov rdi, 0
    syscall

继续上面的评论,比较失败是因为您试图将单个字节 'S''Q' 与 8 字节寄存器进行比较。那行不通的。 rdx 还有 7 个字节不会是 'S''Q' 并且每次测试都会失败。

步进gdb中的每条指令使其易于查看。 运行 gdb yourexecutable,然后 break _start 在程序开始时创建断点。 (为方便起见,通过设置 display/i $pc 显示当前正在执行的命令)现在只需使用 si 来逐步执行您的代码。当您进入数据输入和比较时,检查 .bufferrdx 的内容,例如

1: x/i $pc
=> 0x4000e5 <start_promt+51>:   syscall
(gdb)
SoooRO
0x00000000004000e7 in start_promt ()
1: x/i $pc
=> 0x4000e7 <start_promt+53>:   mov    0x600150,%rdx
(gdb) x/6c 0x600150
0x600150:       83 'S'  111 'o' 111 'o' 111 'o' 82 'R'  79 'O'
(gdb) si
0x00000000004000ef in start_promt ()
1: x/i $pc
=> 0x4000ef <start_promt+61>:   cmp    [=10=]x53,%rdx
(gdb) info reg rdx
rdx            0xa4f526f6f6f53     2901965242593107

在上面你可以看到在提示符下输入了 "SoooRO" 只是为了填充一些额外的字符。 .buffer 的地址是 0x600150 所以你可以用 x/6c 0x600150 检查缓冲区的内容(检查缓冲区中的 6 个字符)你会看到:

0x600150:       83 'S'  111 'o' 111 'o' 111 'o' 82 'R'  79 'O'

一切都很好 -- 那么为什么比较失败了?这也显示在调试中:

=> 0x4000ef <start_promt+61>:   cmp    [=12=]x53,%rdx
(gdb) info reg rdx
rdx            0xa4f526f6f6f53     2901965242593107

你看到寄存器的位置 0xa4f526f6f6f53(最后一个字节是 ASCII 0x53 - 'S')。

那么如何解决这个问题呢?使用正确的寄存器来存储一个字节。 dl是一字节寄存器,dx是两字节寄存器,edx是四字节寄存器,rdx是八字节寄存器。因此将第一个字符移动到 dl 然后与 dl 进行比较将进行您正在寻找的单字符比较,例如

    mov dl, [.buffer]

    cmp dl, 'S'
    je start_game 

    cmp dl, 'Q'
    je quit

如果您还有其他问题,请告诉我。