/ C++

Syscalls for Linux on Clang++

Here is a snippet of code to perform system calls on x86_64 in C++ (Clang or GCC) without any standard library:

#pragma once
#include <cstdint>
#include <cstddef>

template<int64_t syscall_id, int arg_count>
struct syscall;

template<int64_t syscall_id>
struct syscall<syscall_id, 1> {
    int64_t operator()(int64_t p1) const {
        int64_t ret;
        asm volatile
        (
            "syscall"
            : "=a" (ret)
            : "0"(syscall_id), "D"(p1)
            : "rcx", "r11", "memory"
        );
        return ret;
    }
};

template<int64_t syscall_id>
struct syscall<syscall_id, 2> {
    int64_t operator()(int64_t p1,int64_t p2) const {
        int64_t ret;
        asm volatile
        (
            "syscall"
            : "=a" (ret)
            : "0"(syscall_id), "D"(p1), "S"(p2)
            : "rcx", "r11", "memory"
        );
        return ret;
    }
};

template<int64_t syscall_id>
struct syscall<syscall_id, 3> {
    int64_t operator()(int64_t p1,int64_t p2,int64_t p3) const {
        int64_t ret;
        asm volatile
        (
            "syscall"
            : "=a" (ret)
            : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3)
            : "rcx", "r11", "memory"
        );
        return ret;
    }
};

template<int64_t syscall_id>
struct syscall<syscall_id, 4> {
    int64_t operator()(int64_t p1,int64_t p2,int64_t p3,int64_t p4) const {
        int64_t ret;
        register long r10 asm("r10") = p4;
        asm volatile
        (
            "syscall"
            : "=a" (ret)
            : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3), "r"(r10)
            : "rcx", "r11", "memory"
        );
        return ret;
    }
};

template<int64_t syscall_id>
struct syscall<syscall_id, 5> {
    int64_t operator()(int64_t p1,int64_t p2,int64_t p3,int64_t p4,int64_t p5) const {
        int64_t ret;
        register long r10 asm("r10") = p4;
        register long r8 asm("r8") = p5;
        asm volatile
        (
            "syscall"
            : "=a" (ret)
            : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3), "r"(r10), "r"(r8)
            : "rcx", "r11", "memory"
        );
        return ret;
    }
};

template<int64_t syscall_id>
struct syscall<syscall_id, 6> {
    int64_t operator()(int64_t p1,int64_t p2,int64_t p3,int64_t p4,int64_t p5,int64_t p6) const {
        int64_t ret;
        register long r10 asm("r10") = p4;
        register long r8 asm("r8") = p5;
        register long r9 asm("r9") = p6;
        asm volatile
        (
            "syscall"
            : "=a" (ret)
            : "0"(syscall_id), "D"(p1), "S"(p2), "d"(p3), "r"(r10), "r"(r8), "r"(r9)
            : "rcx", "r11", "memory"
        );
        return ret;
    }
};

And here are a few examples of how to implement some very common calls in a more detailed fashion:


constexpr auto _read = syscall<0, 3>{};
constexpr auto _write = syscall<1, 3>{};
constexpr auto _mmap = syscall<9, 6>{};
constexpr auto _munmap = syscall<11, 2>{};
constexpr auto _exit = syscall<60, 1>{};

inline int read(int fd, char* buffer, size_t sz) {
    return _read((int64_t)fd, (int64_t)buffer, (int64_t)sz);
}

inline int write(int fd, char* buffer, size_t sz) {
    return _write((int64_t)fd, (int64_t)buffer, (int64_t)sz);
}

inline void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
    return (void*)_mmap((int64_t)addr, (int64_t)length, (int64_t)prot, (int64_t)flags, (int64_t)fd, (int64_t)offset);
}

inline int munmap(void *addr, size_t length) {
    return _munmap((int64_t)addr, (int64_t)length);
}

extern "C" {

inline __attribute__ ((__noreturn__)) void exit(int status) {
    _exit((int64_t)status);
    while(true);
}

}

Just leaving those snippets here for anyone to use them. Consider them MIT licensed.

If you like my content and want more, please donate. If everyone that finds my content useful paid $1 every week, I would be able to produce content for 1 week a month without relying on other sources of income for that week.

Syscalls for Linux on Clang++
Share this