.section __DATA,__data
.p2align 2
buffer:
.zero 4096
.section __TEXT,__text
.global _main
.build_version macos, 13, 0
.p2align 2
_main:
// x9: buf ptr
// x10: file descriptor storage
// x11: file size in bytes
//init ptr to buf
adrp x9, buffer@PAGE
add x9, x9, buffer@PAGEOFF
// open file
adr x0, file_path
mov x1, #0
mov x2, #444
mov x16, #5
svc 0
// copy file descriptor to x10
mov x10, x0
.p2align 2
stream_buffer:
// make syscall read, file descriptor is in x10
mov x0, x10
mov x1, x9
mov x2, #4096
mov x16, #3
svc 0
// if x0 == 0, exit, no bytes were read
cmp x0, #0
beq exit
blt error
// store number of bytes read
mov x11, x0
// write to stdout from buffer
mov x0, #1
mov x1, x9
mov x2, x11
mov x16, #4
svc 0
b stream_buffer
.p2align 2
exit:
// exit with status code 0
mov x0, #0
mov x16, #1
svc 0
.p2align 2
error:
mov x0, #1
adr x1, file_not_found_error_string
mov x2, #20
mov x16, #4
svc 0
b exit
.p2align 2
file_path:
.asciz "/test.txt"
.p2align 2
file_not_found_error_string:
.asciz "file was not found.\n"
我正在尝试通过编写一个模拟“cat”linux 命令的简单程序来学习汇编。我使用的是配备 M1 芯片的 MacBook Air 2020。我的程序编译得很好,但是在执行二进制文件时,我遇到了我的程序需要输入,然后它会回显输入的任何内容。我相信我滥用了我的文件描述符。任何帮助表示赞赏。
哦,这太搞笑了。
您的代码最终从 stdin 读取的事实是代码中错误的顶峰,同时还伴随着一些意外的操作系统行为。
让我们先从高层次的角度来看待这个问题:
/test.txt
进行阅读。但是你使用的是arm64 macOS,这意味着除非你不遗余力地搞乱操作系统,否则系统卷是只读的并且
/test.txt
不存在。
所以你的
open
系统调用失败了,但是你没有检测到这一点,因为你没有在那里进行错误检查。不好!x0
为 -1
,因为这就是从 C 调用时 open()
所做的事情,但这不是系统调用 ABI。如果您查看 /usr/lib/system/libsystem_kernel.dylib
是一个反汇编程序并寻求 ___open
,您会看到以下内容:
;-- ___open:
;-- func.00002308:
0x00002308 b00080d2 mov x16, 5
0x0000230c 011000d4 svc 0x80
0x00002310 03010054 b.lo 0x2330
0x00002314 7f2303d5 pacibsp
0x00002318 fd7bbfa9 stp x29, x30, [sp, -0x10]!
0x0000231c fd030091 mov x29, sp
0x00002320 8a030094 bl sym._cerror
0x00002324 bf030091 mov sp, x29
0x00002328 fd7bc1a8 ldp x29, x30, [sp], 0x10
0x0000232c ff0f5fd6 retab
0x00002330 c0035fd6 ret
这里的关键部分是
b.lo
。系统调用使用进位标志(NZCV 中的“C”)来指示是否存在错误。这意味着:
b.lo
-> x0
保存文件描述符b.hs
-> x0
持有 errno
值因此您的系统调用失败并在
x0
中返回错误值。特别是ENOENT
,因为它找不到您要求的文件。并且 ENOENT
恰好是 2
,因此当您将该错误值传递给下一个系统调用时,您最终会从文件描述符 2
中读取,即 stderr。但现在,因为您从命令行调用了二进制文件,所以文件描述符 0、1 和 2 恰好都是同一个文件描述符,因此在这种情况下从 stderr 读取的行为就像从 stdin 读取一样。
那么如何解决这个问题呢?在第一个
b.hs error
之后添加 svc
。