Yes go is now written in go. But, you don't need C to make syscalls.
An important thing to call out is that syscalls aren't "written in C." You can make syscalls from C on Unix because of <unistd.h>
. In particular, how Linux defines this header is a little convoluted, but you can see from this file the general idea. Syscalls are defined with a name and a number. When you call read
for example, what really happens behind the scenes is the parameters are setup in the proper registers/memory (linux expects the syscall number in eax
) followed by the instruction syscall
which fires interrupt 0x80
. The OS has already setup the proper interrupt handlers that will receive this interrupt and the OS goes about doing whatever is needed for that syscall. So, you don't need something written in C (or a standard library for that matter) to make syscalls. You just need to understand the call ABI and know the interrupt numbers.
However, as @retgits points out golang's approach is to piggyback off the fact that libc already has all of the logic for handling syscalls. mksyscall.go
is a CLI script that parses these libc files to extract the necessary information.
You can actually trace the life of a syscall if you compile a go script like:
package main
import (
"syscall"
)
func main() {
var buf []byte
syscall.Read(9, buf)
}
Run objdump -D
on the resulting binary. The go runtime is rather large, so your best bet is to find the main
function, see where it calls syscall.Read
and then search for the offsets from there: syscall.Read
calls syscall.syscall
, syscall.syscall
calls runtime.libcCall
(which switches from the go ABI to C ABI compatibility so that arguments are located where the OS expects--you can see this in runtime
, for darwin for example), runtime.libcCall
calls runtime.asmcgocall
, etc.
For extra fun, run that binary with gdb and continue stepping in until you hit the syscall.