#pragma once
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
#include <gp/containers/buffer.hpp>
|
|
|
|
#if defined(__x86_64__)
|
|
#if defined(__linux__)
|
|
|
|
constexpr bool is_x86_64_linux = true;
|
|
#else
|
|
constexpr bool is_x86_64_linux = false;
|
|
#endif
|
|
#else
|
|
constexpr bool is_x86_64_linux = false;
|
|
#endif
|
|
|
|
template<int64_t syscall_id, int arg_count>
|
|
struct syscall;
|
|
|
|
template<int64_t syscall_id>
|
|
requires is_x86_64_linux
|
|
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>
|
|
requires is_x86_64_linux
|
|
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>
|
|
requires is_x86_64_linux
|
|
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>
|
|
requires is_x86_64_linux
|
|
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>
|
|
requires is_x86_64_linux
|
|
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>
|
|
requires is_x86_64_linux
|
|
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;
|
|
}
|
|
};
|
|
|
|
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" {
|
|
/// Yes, __attribute__((__noreturn__)), not [[noreturn]], because some God forsaken potato decided that those attributes are not the attributes I am looking for
|
|
inline __attribute__ ((__noreturn__)) void exit(int status) {
|
|
_exit((int64_t)status);
|
|
while(true);
|
|
}
|
|
|
|
}
|
|
|
|
inline int read(int fd, gp::buffer<char> buffer) {
|
|
return _read((int64_t)fd, (int64_t)buffer.begin().data, (int64_t)buffer.size());
|
|
}
|
|
|
|
inline int write(int fd, gp::buffer<char> buffer) {
|
|
return _write((int64_t)fd, (int64_t)buffer.begin().data, (int64_t)buffer.size());
|
|
}
|