7

As far as I know, in CPython, open() and read() - the API to read a file is written in C code. The C code probably calls some C library which knows how to make system call.

What about a language such as Go? Isn't Go itself now written in Go? Does Go call C libraries behind the scenes?

Dave C
  • 7,729
  • 4
  • 49
  • 65
CppLearner
  • 16,273
  • 32
  • 108
  • 163

4 Answers4

12

The short answer is "it depends".

Go compiles for multiple combinations of H/W and OS, and they all have different approaches to how syscalls are to be made when working with them.

For instance, Solaris does not provide a stable supported set of syscalls, so they go through the systems libc — just as required by the vendor.

Windows does support a rather stable set of syscalls but it is defined as a C API provided by a set of standard DLLs. The functions exposed by those DLLs are mostly shims which use a single "make a syscall by number" function, but these numbers are not documented and are different between the kernel flavours and releases (perhaps, intentionally).

Linux does provide a stable and documented set of numbered syscalls and hence there Go just calls the kernel directly.

Now keep in mind that for Go to "call the kernel directly" means following the so-called ABI of the H/W and OS combo. For instance, on modern Linux on amd64 making a syscall requires filling a set of CPU registers with certain values, doing some other arrangements and then issuing the SYSENTER CPU instruction.

On Windows, you have to use its native calling convention (which is stdcall, not cdecl).

kostix
  • 51,517
  • 14
  • 93
  • 176
10

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.

Bailey Parker
  • 15,599
  • 5
  • 53
  • 91
2

The sys package takes care of the syscalls to the underlying OS. Depending on the OS you're using different packages are used to generate the appropriate calls. Here is a link to the README for Go running on Unix systems: https://github.com/golang/sys/blob/master/unix/README.md the parts on mksyscall.go, which are hand-written Go files which implement system calls that need special handling, and type files, should walk you through how it works.

retgits
  • 1,323
  • 1
  • 9
  • 14
0

The Go compiler (which translates the Go code to target CPU code) is written in Go but that is different to the run time support code which is what you are talking about. The standard library is mainly written in Go and probably knows how to directly make system calls with no C code involved. However, there may be a bit of C support code, depending on the target platform.

AJR
  • 1,547
  • 5
  • 16