Invoke Linux syscalls from the CLI + interpret syslang source files.
Find a file
2025-08-23 09:51:15 +02:00
examples add example 2025-05-13 00:14:47 +02:00
src remove comments, add --quiet 2025-05-12 01:01:06 +02:00
.gitignore add code 2025-05-11 00:34:43 +02:00
build_release fix readme 2025-05-11 00:45:31 +02:00
Cargo.lock remove comments, add --quiet 2025-05-12 01:01:06 +02:00
Cargo.toml remove comments, add --quiet 2025-05-12 01:01:06 +02:00
LICENSE.txt add code 2025-05-11 00:34:43 +02:00
README.md Update README.md 2025-08-23 09:51:15 +02:00

Dynamic Syscalls

Syslang 🧩

The project has now evolved into a very simple programming language. You can interpret .scx files using the --interpret <scx source file> argument.

The language currently supports:

  • One keyword: syscall
  • Two data types: numbers like 42 and strings like "this is a string"
  • Comments: start a line with # to write a comment

What it doesn't have (yet):

  • Variables
  • Loops
  • Functions
  • Transpilation or compilation
  • And more. I'm not even sure if it qualifies as a full programming language at this point. Maybe it's more of a DSL (domain-specific language).

Purpose 🎯

The purpose of this project is to reduce the friction involved in making a system call directly from userspace. It allows you to invoke syscalls without the need to write or compile a full C or Rust program. Just provide the syscall number and up to six arguments, and it will do the rest.

Usage 🛠️

The first argument is the syscall number. You do not need to type hint it. The next arguments (up to a maximum of six) must be type hinted:

  • Use n:<value> to indicate an unsigned number (usize)
  • Use s:<value> to indicate a string whose pointer should be passed

Arguments are passed in the order expected by the syscall.

Interpreting Syslang source files

  • --interpret <.scx source file> to interpret the file
  • --quiet to enable quiet mode when interpreting a source file (silences any prints)

To get a grasp on the syntax, check out the .scx files under the examples/ directory.

Examples 📌

If you want to use the write(2) syscall (instead of bothering with echo(1) or printf(1)) you can use this program to directly interface with the kernel.

Reference for syscall numbers: Linux System Call Table (x86_64) (Double-click on a row to reveal the arguments list.)

Writing to stdout

System call number 1 corresponds to write, which takes:

  1. A file descriptor (e.g., 1 for stdout)
  2. A pointer to the buffer (e.g., "Hello, World\n")
  3. The number of bytes to write (e.g., 13, including the newline)

Example:

./dynamic-syscall 1 n:1 $'s:Hello, World\n' n:13

Note: To correctly pass special characters like \n, use ANSI C quoting by prefixing your string with $ and enclosing it in single quotes. Otherwise, characters like \n will be passed as the literal backslash and n (i.e., "\\n"), because of how Bash handles escaping.

Sending SIGKILL to a process

  1. Identify the PID of the target process (e.g., 414195).
  2. Run:
./dynamic-syscall 62 n:414195 n:9

Explanation:

  • Syscall 62 is kill(2), which takes:

    • A pid (pid_t): the ID of the process to signal
    • A signal number (int): in this case, 9 (SIGKILL) to immediately terminate the process

Limitations ⚠️

Currently, only two argument types are supported:

  • s: string (converted to a pointer)
  • n: unsigned number (usize)

You can pass negative numbers, and they usually work, because casting from i32 to usize preserves the bit pattern. However, this is architecture dependent and not type safe. Be cautious.

Future Improvements (TODO) 🗒️

  • Allow plain numbers (e.g., 42) to default to usize without requiring the n: prefix?
  • Add support for additional types (i32, NULL, raw pointers, flags, etc.)
  • Optional unescaping of common character sequences (\n, \t, etc.) internally
  • For debug printing, have a map for each syscall number and print their names (e.g., 1 would be write)
  • Allow non-base-10 numbers (e.g., 0xFF or 0o644)