Syscall - a new package for invoking system calls

Hey all -

After playing around with Elixir during my Distributed Algorithms class at my university, I quickly fell in love with the language. Coming from a low-level background, however, I wanted to invoke system calls (i.e., read(2), write(2), open(2), etc.). Unfortunately, I couldn’t find a way to do it, so as a first contribution to the ecosystem of Elixir, I created a package that lets you invoke the system calls (right now, only on a recent, x86_64 Linux system; albeit in the future I plan to add support for macOS and Windows). It also provides handy stubs for you, so invoking a system call is as easy as:

Syscall.sys_exit_group 42

(to exit the program with status code 42)

The package can be found on Hex: https://hex.pm/packages/syscall
and on GitHub: https://github.com/levex/elixir-syscall

I would appreciate any feedback (particular around my handling of the priv folder in the Mixfile) and if this is something you would use then that’s great!

Best wishes,

Lev

5 Likes

@lkurusa: Can you provide a more useful example for which Elixir projects your library would be helpful and when? I don’t even remember syscall, so I’m like a beginner in such topic. Then someone like me sees that in your example you are calling sys_exit_group - it could be really confused especially for new developers who heard about Erlang/Elixir words like Let it fail! or 99.999999999% uptime. :077:

I intend to use this library as a base for a ptrace(2) library that I’m planning to write. These two together will be used to create a very simple, proof-of-concept debugger like gdb.

In a nutshell, system calls (syscalls for short) are the mechanism your application uses to communicate with the kernel and get things done. You use them to write to files, to read from files, make network connections, etc.

You’re right the example I gave isn’t the most perfect - the problem is that more “noteworthy” examples require some mangling with the arguments, i.e. to print a String to the screen, it first has to be converted into a C String (a list of char, basically). See string_to_buffer/1 in the library for more information. :slight_smile:

1 Like

Basically not.

A C-String is much closer to a zero-terminated binary than to a list, e.g. <<?H, ?e, ?l, ?l, ?o, 0>>.

Spoken exactly, the C-String is a pointer to a single byte in memory, which is then read continiuosly until the first byte which is equal to zero.

Lists though are not continous in memory, each cell of it points to the next cell, which again points to the next.

I have to be honest though, binaries are fat pointers, meaning we have a pointer to the beginning and either the size or the end of the memory block. The memory is continous.

3 Likes

@NobbZ That is all correct. I intended to avoid going into details, because the original topic of the post was not much about C :slight_smile:

1 Like

I still don’t understand the purpose of this library.

  1. Elixir / Erlang do have several syscall builtin functions with configurable parameters (say buffer sizes for reads/writes). Why are they not good enough for you? Why do you want to be able to call any possible syscall? Some of them might as well break the BEAM VM.
  2. Why would you implement something like gdb in Elixir at all? Doing :observer.start() from an iex shell will give you tons of info in a neat GUI. And you can inspect remote nodes – including production apps – with it as well.

It partially looks like you are trying to invent an Elixir way to write C to me.

If you really want to interface between native libs and Elixir, NIFs and/or Ports are plenty enough. Furthermore, crossing the BEAM<->native boundary incurs performance overhead and conversion of data. As a guy who wrote tons of native<->Java bridges 10+ years ago it’s IMO much more efficient to just write your absolutely-cannot-be-Elixir module in C and provide a NIF / Port way to use it. It can happily do its work and only cross the BEAM<->native boundary twice: (1) when receiving input parameters and (2) when returning the result.

Doing it with your library would result on much more than two boundary crossings. I don’t see the win.

5 Likes

@dimitarvp, Thanks for your reply!

  1. Elixir / Erlang do have several syscall builtin functions with configurable parameters

I haven’t found these, could you please give me a pointer to the documentation?

Why do you want to be able to call any possible syscall? Some of them might as well break the BEAM VM.

Why not? :slight_smile: They might break the VM, but that is fine to me. Given that this is a low-level package, it is not aimed at the general public. Chances that if you are interested in this package (or a method of invoking system calls) then you know what you are doing and are also aware of the risks.

Why would you implement something like gdb in Elixir at all? Doing :observer.start() from an iex shell will give you tons of info in a neat GUI. And you can inspect remote nodes – including production apps – with it as well.

As a proof of concept and a learning project for me. In particular, the debugger I envision is not targeting Elixir programs but mostly C programs. I wrote basic debuggers before and I find Elixir a language that could give some interesting potential benefits.

It partially looks like you are trying to invent an Elixir way to write C to me.

This is true for the lowest level parts, but the higher level parts of the debugger will be in “original” Elixir style.

Surely there is performance overhead to crossing the BEAM<->native boundary, of which I’m aware but as I mention above people who are interested in this library are going to be aware of this and hopefully, they are aware of alternative solutions. The goal of the library is to avoid every single project in Elixir that just needs one system call invoked to be writing their own NIFs/Ports and instead just grab this from Hex and call the system call. Are there projects like that or will there be projects like that? Well, I don’t know :slight_smile:

4 Likes