knorv
knorv

Reputation: 50127

How do I call the write syscall using inline assembler in GCC under MacOS X?

The write syscall has the following functional prototype:

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

How do I call the write syscall using inline assembler in GCC under MacOS X?

Upvotes: 3

Views: 4803

Answers (2)

Daniel Roethlisberger
Daniel Roethlisberger

Reputation: 7065

A generic solution to this type of question: Write a short test program doing the write() call you are interested in, and then use gcc -S to produce assembly or use otool to disassemble the binary; find out how the write() call was assembled and transform that into the appropriate inline assembly.

(edit) To see the actual syscall, follow the library call in the assembly code. In the example of write(), following the indirections will lead you via libSystem.B.dylib to _write in libsystem_kernel.dylib, which you can disassemble using otool.

(edit2) Full example below, using this test program test.c:

#include <stdio.h>
int main(void)
{
    char buf[] = "test\n";
    ssize_t n;
    n = write(2, buf, sizeof(buf));
    return n;
}

Compile and test, using -O1 -fverbose-asm to get concise, readable machine code:

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

Use otool to disassemble main() in the test binary:

% 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

List the libraries test is dynamically linked against to find _write:

% otool -L test
test:
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)

Try to locate _write in libSystem.B.dylib:

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

Check the dependencies of 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)

Guess that _write is probably contained in libsystem_kernel.dylib (or alternatively try all of them):

% 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
[...]

Now we have all the assembly we need to construct the inline assembly version of 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;
}

Compile and test:

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

That seems to work. The inline assembly above is not very refined; for example, you could pass in the first and third arguments dynamically as well, instead of hardcoding them in the assembly code.

Upvotes: 11

johannes
johannes

Reputation: 15989

The Linux solution is something like this:

.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

Should be similar on other Unix systems like Mac, but better check the syscall number (0x04) in the documentation.

Upvotes: 2

Related Questions