系统调用 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
作为参数,因此无需将 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
在注释后编辑: 要在退出时刷新标准输入,您可以在第 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
我是汇编新手,正在尝试编写内置“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
作为参数,因此无需将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
在注释后编辑: 要在退出时刷新标准输入,您可以在第 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