我该如何在MacOS X下使用GCC内联汇编调用write系统调用?

3

write系统调用具有以下功能原型:

size_t write(int, const void *buf, size_t nbytes);

我该如何在MacOS X下使用GCC内联汇编调用write系统调用?

2
使用标准的C语言write函数相比,有什么优势? - Bo Persson
5
实际优势?显然没有。学习优势?很多。 - knorv
1
在吹毛求疵的一面:POSIX并未标准化_syscall接口_,而只是标准化了_system library接口_;实际的调用接口没有被定义为文档或稳定的,事实上可能有多种方法 - 例如BSD、Solaris和Linux内核同时支持几种方法(旧的int $0x80和/或lcall 0x27:0以及Intel/AMD的sysenter / syscall)。MacOSX在这方面有“特权”,因为它只支持具有syscall指令的x86硬件... 因此不需要其他任何东西... - FrankH.
@FrankH。没错 - 我已经相应地更新了问题。谢谢! - knorv
请注意,在 MacOS X 上,返回值实际上是 ssize_t 而不是 size_t - Daniel Roethlisberger
2个回答

11
一种通用的解决此类问题的方法:编写一个短小的测试程序,执行您感兴趣的write()调用,然后使用gcc -S生成汇编或使用otool反汇编二进制文件;找出如何组装write()调用并将其转换为适当的内联汇编代码。
(编辑)要查看实际的系统调用,请跟踪汇编代码中的库调用。在write()的示例中,通过跟随间接引用,您将通过libSystem.B.dylib到达libsystem_kernel.dylib中的_write,您可以使用otool对其进行反汇编。
(编辑2)下面是完整的示例,使用此测试程序test.c
#include <stdio.h>
int main(void)
{
    char buf[] = "test\n";
    ssize_t n;
    n = write(2, buf, sizeof(buf));
    return n;
}

编译并测试,使用-O1 -fverbose-asm来获取简洁易懂的机器码:

% gcc -O1 -fverbose-asm -o test test.c
% ./test
test

使用otool来反汇编test二进制文件中的main()函数:

% otool -p _main -tvV test
test:
(__TEXT,__text) section
_main:
0000000100000ef0    pushq   %rbp
0000000100000ef1    movq    %rsp,%rbp
0000000100000ef4    subq    $0x10,%rsp
0000000100000ef8    leaq    0xfa(%rbp),%rsi
0000000100000efc    movb    $0x74,0xfa(%rbp)
0000000100000f00    movb    $0x65,0xfb(%rbp)
0000000100000f04    movb    $0x73,0xfc(%rbp)
0000000100000f08    movb    $0x74,0xfd(%rbp)
0000000100000f0c    movb    $0x0a,0xfe(%rbp)
0000000100000f10    movb    $0x00,0xff(%rbp)
0000000100000f14    movl    $0x00000002,%edi
0000000100000f19    movl    $0x00000006,%edx
0000000100000f1e    xorb    %al,%al
0000000100000f20    callq   0x100000f32 ; symbol stub for: _write
0000000100000f25    addq    $0x10,%rsp
0000000100000f29    popq    %rbp
0000000100000f2a    ret

列出动态链接到test的库,以查找_write
% otool -L test
test:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)

尝试在libSystem.B.dylib中查找_write

% otool -p _write -tvV /usr/lib/libSystem.B.dylib
/usr/lib/libSystem.B.dylib:
(__TEXT,__text) section
Can't find -p symbol: _write

检查 libSystem.B.dylib 的依赖项:

% otool -L /usr/lib/libSystem.B.dylib
/usr/lib/libSystem.B.dylib:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
    /usr/lib/system/libcache.dylib (compatibility version 1.0.0, current version 47.0.0)
    /usr/lib/system/libcommonCrypto.dylib (compatibility version 1.0.0, current version 55010.0.0)
    /usr/lib/system/libcompiler_rt.dylib (compatibility version 1.0.0, current version 6.0.0)
    /usr/lib/system/libcopyfile.dylib (compatibility version 1.0.0, current version 85.1.0)
    /usr/lib/system/libdispatch.dylib (compatibility version 1.0.0, current version 187.9.0)
    /usr/lib/system/libdnsinfo.dylib (compatibility version 1.0.0, current version 395.11.0)
    /usr/lib/system/libdyld.dylib (compatibility version 1.0.0, current version 195.6.0)
    /usr/lib/system/libkeymgr.dylib (compatibility version 1.0.0, current version 23.0.0)
    /usr/lib/system/liblaunch.dylib (compatibility version 1.0.0, current version 392.38.0)
    /usr/lib/system/libmacho.dylib (compatibility version 1.0.0, current version 800.0.0)
    /usr/lib/system/libmathCommon.A.dylib (compatibility version 1.0.0, current version 2026.0.0)
    /usr/lib/system/libquarantine.dylib (compatibility version 1.0.0, current version 36.6.0)
    /usr/lib/system/libremovefile.dylib (compatibility version 1.0.0, current version 21.1.0)
    /usr/lib/system/libsystem_blocks.dylib (compatibility version 1.0.0, current version 53.0.0)
    /usr/lib/system/libsystem_c.dylib (compatibility version 1.0.0, current version 763.13.0)
    /usr/lib/system/libsystem_dnssd.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/system/libsystem_info.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/system/libsystem_kernel.dylib (compatibility version 1.0.0, current version 1699.26.8)
    /usr/lib/system/libsystem_network.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/system/libsystem_notify.dylib (compatibility version 1.0.0, current version 80.1.0)
    /usr/lib/system/libsystem_sandbox.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/system/libunc.dylib (compatibility version 1.0.0, current version 24.0.0)
    /usr/lib/system/libunwind.dylib (compatibility version 1.0.0, current version 30.0.0)
    /usr/lib/system/libxpc.dylib (compatibility version 1.0.0, current version 77.19.0)

猜测 _write 可能包含在 libsystem_kernel.dylib 中(或者尝试所有库):

% otool -p _write -tvV /usr/lib/system/libsystem_kernel.dylib | head -20
(__TEXT,__text) section
_write:
0000000000017fd4    movl    $0x02000004,%eax
0000000000017fd9    movq    %rcx,%r10
0000000000017fdc    syscall
0000000000017fde    jae 0x00017fe5
0000000000017fe0    jmp cerror
0000000000017fe5    ret
0000000000017fe6    nop
0000000000017fe7    nop
[...]

现在我们已经拥有了构建test的内联汇编所需的所有组件:

#include <stdio.h>
int main(void)
{
    char buf[] = "test\n";
    ssize_t n;
    asm volatile (
        "movl $0x00000002, %%edi\n"  /* first argument */
        "movl $0x00000006, %%edx\n"  /* third argument */
        "movl $0x02000004, %%eax\n"  /* syscall number */
        "syscall\n"
        : "=A"(n)         /* %rax: return value */
        : "S"(buf));      /* %rsi: second argument */
    return n;
}

编译和测试:
% gcc -O1 -fverbose-asm -o test-asm test-asm.c
% ./test-asm
test

看起来这个方法是可行的。上面的内联汇编并不是很精细;例如,你也可以将第一个和第三个参数动态传递,而不是在汇编代码中硬编码它们。


1
请注意,这将显示对库的系统调用包装器函数的调用。它不会显示对内核的实际调用。这样就错过了调用内核空间的大部分乐趣,但我对Darwin/Mach/...知之甚少,无法回答。 - johannes
根据 OP 想要实现的目标,可能需要实际的系统调用;我已相应地编辑了我的解决方案。 - Daniel Roethlisberger

2
Linux解决方案大致如下:
.data mytext:
    .ascii "Hello World\n"  
.text 
  _mywrite:
    movl $0x04,         %eax     # Syscall No. 4 = write
    movl $0x01,         %ebx     # File. 1 = stdout 
    movl $mytext,       %ecx
    movl $0x0c,         %edx     # text length (hope I counted correctly)
    int  $0x80                   # Interrupt 0x80 -> make syscall

在其他类Unix系统(例如Mac)上应该是类似的,但最好检查文档中的系统调用号码(0x04)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接