在 Apple Mac M64 CPU 上的 ARM1 程序集中打开和读取文件时出现问题

Trouble opening and reading file in ARM64 assembly on apple mac M1 cpu

提问人:Jake Snell 提问时间:11/1/2023 最后编辑:FrantJake Snell 更新时间:11/5/2023 访问量:41

问:

.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命令来学习汇编。我使用的是带有 M2020 芯片的 macbook air 1。我的程序编译良好,但是在执行二进制文件时,我遇到了我的程序期望输入,然后它会回显输入的任何内容。我认为我滥用了我的文件描述符。任何帮助表示赞赏。

macOS 组装 Apple-M1 ARM64

评论

0赞 Peter Cordes 11/1/2023
使用 dtrace 跟踪系统调用应该有助于查看程序实际进行的系统调用,解码它们的参数和(错误)返回值。

答:

1赞 Siguza 11/1/2023 #1

哦,这很搞笑。

您的代码最终从 stdin 读取的事实是代码中错误的高潮,以及一些意想不到的操作系统行为。

让我们先从高层次的角度来看一下:

  1. 你打开阅读。/test.txt
  2. 您最多可以从中读取 4096 个字节。
  3. 将这些字节写入 stdout。

但是你使用的是 arm64 macOS,这意味着除非你竭尽全力弄乱操作系统,否则系统卷是只读的,不存在。/test.txt

因此,您的系统调用失败了,但您没有检测到这一点,因为您没有在那里进行错误检查。坏!
现在,您可能会假设在这种情况下,因为如果从 C 调用,这就是这样做的,但这不是系统调用 ABI。如果你看是一个反汇编器并寻求,你会看到这个:
openx0-1open()/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

这里的关键部分是 .系统调用使用进位标志(NZCV 中的“C”)来指示是否存在错误。这意味着:b.lo

  • b.lo -> x0保存文件描述符
  • b.hs -> x0保持值errno

因此,您的系统调用失败并返回错误值。具体来说,因为它找不到您要求的文件。碰巧是 ,所以当你将该错误值传递给下一个系统调用时,你最终会从文件描述符中读取,即 stderr。但是现在,由于您从命令行调用了二进制文件,因此文件描述符 0、1 和 2 恰好都是同一个文件描述符,因此在这种情况下,从 stderr 读取的行为就像从 stdin 读取一样。x0ENOENTENOENT22

那么你如何解决这个问题呢?在第一个 .
然后选择一个实际存在的文件路径。
b.hs errorsvc

评论

0赞 Jake Snell 11/2/2023
这非常清晰和详细。谢谢。如果我有能力,我会投赞成票。