系统调用 read/write 只有 1 个字节?

syscall read/write 1 byte only?

我是汇编新手,正在尝试编写内置“echo”的一个版本,但一次只能在 1 个字节上运行。

我有以下按我想要的方式工作的方法,除了它在读写时溢出超过 1 个字节,即使我在两个系统调用中明确表示 x2 中有 1 个字节。我做错了什么?

示例运行:

sh-4.2$ ./echo1b
f
f
o
o
b
b
bar
bar
bazbazbaz
bazbazbaz
q
sh-4.2$

代码如下:

.data
temp:   .byte 1  

.text
.globl _start
_start:
    /* read one byte from stdin, store to temp */
    mov x0, #0x0
    adr x1, temp
    mov x2, #0x1
    mov x8, #0x3F
    svc #0x0

    /* write newline to stdout */
    mov x0, #0x1
    mov x1, #0xA
    mov x2, #0x1
    mov x8, #0x40
    svc #0x0
    
    /* if byte in temp is "q", exit */
    mov x5, #0x71
    ldr x1, temp
    cmp x1, x5
    beq exit

    /* otherwise, write it to stdout and repeat */
    mov x0, #0x1
    adr x1, temp
    mov x2, #0x1
    mov x8, #0x40
    svc #0x0
    b _start

exit:
    /* exit cleanly */  
    eor x0, x0, x0 
    eor x1, x1, x1
    eor x2, x2, x2
    mov x8, #0x5D
    svc #0x0

您的代码中存在几个问题:

  • 如评论中所述,调用 sys_write 时输出缓冲区的地址必须在 x1 中,就像您对 temp
  • 所做的那样
  • 当比较 temp 和换行符时,您必须使用 ldrb w1, [x0] 而不是 ldr x1, temp,其中 x0 指向 temp。后者将读取 4 个字节,但不能保证高三个字节为零。

我还改进了你的部分代码:

  • cmp 可与 12 位立即数一起使用,因此无需将 0x71 放入寄存器。
  • 将第二个 sys_write 调用移到 _start 之前避免无条件跳转。
  • sys_exit 仅使用 x0 作为参数,因此无需将 x1x2 设置为零。

这是最终代码,在 Raspbian 4.19(基于 debian)上测试:

.data
    temp:    .byte 1
    newline: .byte 0x0A

.text
.globl _start

loop:
    // 4: Otherwise, write it to stdout and repeat
    mov  x0, #0x1    // int    fd
    adr  x1, temp    // void*  buf
    mov  x2, #0x1    // size_t count
    mov  x8, #0x40   // sys_write
    svc  #0x0
    
_start:
    // 1: Read one byte from stdin and store to temp (including newline)
    mov  x0, #0x0   // int    fd
    adr  x1, temp   // void*  buf
    mov  x2, #0x1   // size_t count
    mov  x8, #0x3F  // sys_read
    svc  #0x0
    
    // 2: If byte in temp is 'q', exit
    adr  x0, temp
    ldrb w1, [x0] // instead of temp
    cmp  x1, #0x71
    bne  loop

    // 5: Exit cleanly
    eor  x0, x0, x0  // int status
    mov  x8, #0x5D   // sys_exit
    svc  #0x0

在注释后编辑: 要在退出时刷新标准输入,您可以在第 5 步之前添加以下行:

    // 5: Flush stdin (read until newline)
flush:
    mov  x0, #0x0   // int    fd
    adr  x1, temp   // void*  buf
    mov  x2, #0x1   // size_t count
    mov  x8, #0x3F  // sys_read
    svc  #0x0

    adr  x0, temp
    ldrb w1, [x0]
    cmp  x1, #0x0A
    bne flush       // loop until x0 == 0x0A