assembly 在Apple Mac M1 CPU上打开和阅读ARM64组件中的文件时出现问题

k10s72fa  于 11个月前  发布在  Mac
关注(0)|答案(1)|浏览(182)
.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。我的程序编译得很好,但是当执行二进制文件时,我遇到了我的程序期望输入,然后它会返回任何输入。我相信我滥用了我的文件描述符。感谢任何帮助。

iszxjhcz

iszxjhcz1#

噢,这太搞笑了。
事实上,您的代码最终从stdin阅读是代码中错误的顶峰,并伴有一些意外的操作系统行为。
让我们先从一个高层次的Angular 来看:
1.打开/test.txt进行阅读。
1.你可以从中读取多达4096个字节。
1.将这些字节写入标准输出。
但是你在arm 64 macOS上,这意味着除非你花了很大的力气去弄乱操作系统,否则系统卷是只读的,/test.txt不存在。
所以你的open系统调用失败了,但是你没有检测到,因为你没有在那里做错误检查。
现在,在这种情况下,你可能会假设x0-1,因为如果从C调用open(),这就是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阅读一样。
那么如何解决这个问题呢?在第一个svc之后放一个b.hs error
然后选择一个实际存在的文件路径。

相关问题