системный вызов только чтение / запись 1 байт? - PullRequest
1 голос
/ 03 августа 2020

Я новичок в сборке и пытаюсь написать версию встроенного "echo", но работаю только с 1 байтом за раз.

У меня есть следующее, которое работает так, как я хочу, за исключением он переполняет более 1 байта как при чтении, так и при записи, хотя я явно указываю 1 байт в x2 для обоих системных вызовов. Что я делаю не так?

Пример выполнения:

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

1 Ответ

5 голосов
/ 03 августа 2020

В вашем коде есть несколько проблем:

  • Как упоминалось в комментариях, адрес выходного буфера должен быть в x1 при вызове sys_write, как вы делали с temp
  • При сравнении temp с символом новой строки вы должны были использовать ldrb w1, [x0] вместо ldr x1, temp, где x0 указывает на temp. Последний будет читать 4 байта, хотя не гарантируется, что верхние три байта равны нулю.

Я также улучшил некоторые части вашего кода:

  • cmp может быть используется с 12-битным немедленным запуском, поэтому нет необходимости помещать 0x71 в регистр.
  • Перемещение второго вызова sys_write перед _start позволяет избежать безусловного перехода.
  • sys_exit использует только x0 в качестве параметра, поэтому нет необходимости устанавливать x1 и x2 на ноль.

Вот последний код, протестированный на 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

Редактировать после комментария: To flu sh stdin при выходе, вы можете добавить эти строки перед шагом 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
...