diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/elf.h | 26 | ||||
-rw-r--r-- | include/fmt.h | 125 | ||||
-rw-r--r-- | include/syscall.h | 56 |
3 files changed, 207 insertions, 0 deletions
diff --git a/include/elf.h b/include/elf.h new file mode 100644 index 0000000..7e279fe --- /dev/null +++ b/include/elf.h @@ -0,0 +1,26 @@ +// Copyright (c) 2020 Johannes Stoelp + +#pragma once + +#include <bits/stdint-uintn.h> +#include <stdint.h> + +enum eAuxvTag { + AT_NULL = 0, /* ignored */ + AT_IGNORE = 1, /* ignored */ + AT_EXECFD = 2, /* val */ + AT_PHDR = 3, /* ptr */ + AT_PHENT = 4, /* val */ + AT_PHNUM = 5, /* val */ + AT_PAGESZ = 6, /* val */ + AT_BASE = 7, /* ptr */ + AT_FLAGS = 8, /* val */ + AT_ENTRY = 9, /* ptr */ + AT_NOTELF = 10, /* val */ + AT_UID = 11, /* val */ + AT_EUID = 12, /* val */ + AT_GID = 13, /* val */ + AT_EGID = 14, /* val */ + + AT_MAX_CNT, +}; diff --git a/include/fmt.h b/include/fmt.h new file mode 100644 index 0000000..c74ac4c --- /dev/null +++ b/include/fmt.h @@ -0,0 +1,125 @@ +// Copyright (c) 2020 Johannes Stoelp + +#pragma once + +#include <stdarg.h> + +#define ALLOW_UNUSED __attribute__((unused)) + +ALLOW_UNUSED +static const char* num2dec(char* buf, unsigned long len, unsigned long long num) { + char* pbuf = buf + len - 1; + *pbuf = '\0'; + + if (num == 0) { + *(--pbuf) = '0'; + } + + while (num > 0 && pbuf != buf) { + char d = (num % 10) + '0'; + *(--pbuf) = d; + num /= 10; + } + return pbuf; +} + +ALLOW_UNUSED +static const char* num2hex(char* buf, unsigned long len, unsigned long long num) { + char* pbuf = buf + len - 1; + *pbuf = '\0'; + + if (num == 0) { + *(--pbuf) = '0'; + } + + while (num > 0 && pbuf != buf) { + char d = (num & 0xf); + *(--pbuf) = d + (d > 9 ? 'a' - 10 : '0'); + num >>= 4; + } + return pbuf; +} + +ALLOW_UNUSED +static int dynld_vsnprintf(char* buf, unsigned long len, const char* fmt, va_list ap) { + unsigned i = 0; + +#define put(c) \ + { \ + char _c = (c); \ + if (i < len) { \ + buf[i] = _c; \ + } \ + ++i; \ + } + +#define puts(s) \ + while (*s) { \ + put(*s++); \ + } + + char scratch[16]; + int l_cnt = 0; + + while (*fmt) { + if (*fmt != '%') { + put(*fmt++); + continue; + } + + l_cnt = 0; + + continue_fmt: + switch (*(++fmt /* constume '%' */)) { + case 'l': + ++l_cnt; + goto continue_fmt; + case 'd': { + long val = l_cnt > 0 ? va_arg(ap, long) : va_arg(ap, int); + if (val < 0) { + val *= -1; + put('-'); + } + const char* ptr = num2dec(scratch, sizeof(scratch), val); + puts(ptr); + } break; + case 'x': { + unsigned long val = l_cnt > 0 ? va_arg(ap, unsigned long) : va_arg(ap, unsigned); + const char* ptr = num2hex(scratch, sizeof(scratch), val); + puts(ptr); + } break; + case 's': { + const char* ptr = va_arg(ap, const char*); + puts(ptr); + } break; + case 'p': { + const void* val = va_arg(ap, const void*); + const char* ptr = num2hex(scratch, sizeof(scratch), (unsigned long long)val); + put('0'); + put('x'); + puts(ptr); + } break; + default: + put(*fmt); + break; + } + ++fmt; + } + +#undef puts +#undef put + + if (buf) { + i < len ? (buf[i] = '\0') : (buf[len - 1] = '\0'); + } + return i; +} + +ALLOW_UNUSED +static int dynld_snprintf(char* buf, unsigned long len, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + int ret = dynld_vsnprintf(buf, len, fmt, ap); + va_end(ap); + return ret; +} diff --git a/include/syscall.h b/include/syscall.h new file mode 100644 index 0000000..0460ede --- /dev/null +++ b/include/syscall.h @@ -0,0 +1,56 @@ +// Copyright (c) 2020 Johannes Stoelp + +#pragma once + +#if !defined(__linux__) || !defined(__x86_64__) +# error "Only supported on linux(x86_64)!" +#endif + +// Inline ASM +// Syntax: +// asm asm-qualifiers (AssemblerTemplate : OutputOperands : InputOperands : Clobbers) +// +// Output operand constraints: +// = | operand (variable) is written to by this instruction +// + | operand (variable) is written to / read from by this instruction +// +// Input/Output operand constraints: +// r | allocate general purpose register +// +// Machine specific constraints (x86_64): +// a | a register (eg rax) +// d | d register (eg rdx) +// D | di register (eg rdi) +// S | si register (eg rsi) +// +// Local register variables: +// In case a specific register is required which can not be specified via a +// machine specific constraint. +// ```c +// register long r12 asm ("r12") = 42; +// asm("nop" : : "r"(r10)); +// ``` +// +// Reference: +// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html +// https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints +// https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html + +// Linux syscall ABI +// x86-64 +// #syscall: rax +// ret : rax +// instr : syscall +// args : rdi rsi rdx r10 r8 r9 +// +// Reference: +// syscall(2) + +#define argcast(A) ((long)(A)) +#define syscall3(n,a1,a2,a3) _syscall3(n, argcast(a1), argcast(a2), argcast(a3)) + +static inline long _syscall3(long n, long a1, long a2, long a3) { + long ret; + asm volatile("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), "d"(a3) : "memory"); + return ret; +} |