Why is `write()` performed via a `call` instruction instead of an interrupt?

Viewed 145

I was trying to play a bit in assembly. My understanding is that sys calls are made through an interrupt generated by the syscall instruction.

However after compiling a simple program on my computer using clang -S:

int main() {
  write(0, "HI", 2);
  return 0;
}

The relevant assembly generated is:

    movl    $0, -4(%rbp)
    leaq    L_.str(%rip), %rsi
    movl    $2, %edx
    movb    $0, %al
    callq   _write

Why is that ?

Is there a way to see the actually sys call being performed on OS X ?

2 Answers

Because you're calling a function from the C standard library. That function may or may not wrap a syscall, could use an unexpected syscall (e.g. a more modern one than the original), and generally has to perform various setup and teardown operation to adapt between the C and syscall calling conventions.

The wrapper is usually fairly thin, but it's still a wrapper (e.g. on linux syscalls usually return either a success or -errno in case of failure, the libc then has to check for failure, stash the errno in the corresponding threadlocal and return the "proper" value defined by the standard or POSIX instead).

As @user207421 mentioned in the comment write being a C standard library function must eventually wrap a platform specific write system call. In MacOS facade layer is defined in libsystem_kernel.dylib which is utilised by MacOS C standard library implementation libSystem.B.dylib. The system calls number are not public interface and are subject to change across MacOS versions. Furthermore to excellent @fuz point some of them might even become obsolete and have no direct syscall entry/number anymore but will remain supported by the wrapper layer.

When you launch your program you can verify the actual syscall in action in lldb with:

lldb ./a.out
b write
run
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00007fff63e0f040 libsystem_kernel.dylib`write
libsystem_kernel.dylib`write:
->  0x7fff63e0f040 <+0>:  mov    eax, 0x2000004
    0x7fff63e0f045 <+5>:  mov    r10, rcx
    0x7fff63e0f048 <+8>:  syscall 
    0x7fff63e0f04a <+10>: jae    0x7fff63e0f054            ; <+20>
Target 0: (a.out) stopped.
(lldb) 
Related