/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ /* nolibc.h * Copyright (C) 2017-2018 Willy Tarreau */ /* * This file is designed to be used as a libc alternative for minimal programs * with very limited requirements. It consists of a small number of syscall and * type definitions, and the minimal startup code needed to call main(). * All syscalls are declared as static functions so that they can be optimized * away by the compiler when not used. * * Syscalls are split into 3 levels: * - The lower level is the arch-specific syscall() definition, consisting in * assembly code in compound expressions. These are called my_syscall0() to * my_syscall6() depending on the number of arguments. The MIPS * implementation is limited to 5 arguments. All input arguments are cast * to a long stored in a register. These expressions always return the * syscall's return value as a signed long value which is often either a * pointer or the negated errno value. * * - The second level is mostly architecture-independent. It is made of * static functions called sys_() which rely on my_syscallN() * depending on the syscall definition. These functions are responsible * for exposing the appropriate types for the syscall arguments (int, * pointers, etc) and for setting the appropriate return type (often int). * A few of them are architecture-specific because the syscalls are not all * mapped exactly the same among architectures. For example, some archs do * not implement select() and need pselect6() instead, so the sys_select() * function will have to abstract this. * * - The third level is the libc call definition. It exposes the lower raw * sys_() calls in a way that looks like what a libc usually does, * takes care of specific input values, and of setting errno upon error. * There can be minor variations compared to standard libc calls. For * example the open() call always takes 3 args here. * * The errno variable is declared static and unused. This way it can be * optimized away if not used. However this means that a program made of * multiple C files may observe different errno values (one per C file). For * the type of programs this project targets it usually is not a problem. The * resulting program may even be reduced by defining the NOLIBC_IGNORE_ERRNO * macro, in which case the errno value will never be assigned. * * Some stdint-like integer types are defined. These are valid on all currently * supported architectures, because signs are enforced, ints are assumed to be * 32 bits, longs the size of a pointer and long long 64 bits. If more * architectures have to be supported, this may need to be adapted. * * Some macro definitions like the O_* values passed to open(), and some * structures like the sys_stat struct depend on the architecture. * * The definitions start with the architecture-specific parts, which are picked * based on what the compiler knows about the target architecture, and are * completed with the generic code. Since it is the compiler which sets the * target architecture, cross-compiling normally works out of the box without * having to specify anything. * * Finally some very common libc-level functions are provided. It is the case * for a few functions usually found in string.h, ctype.h, or stdlib.h. Nothing * is currently provided regarding stdio emulation. * * The macro NOLIBC is always defined, so that it is possible for a program to * check this macro to know if it is being built against and decide to disable * some features or simply not to include some standard libc files. * * Ideally this file should be split in multiple files for easier long term * maintenance, but provided as a single file as it is now, it's quite * convenient to use. Maybe some variations involving a set of includes at the * top could work. * * A simple static executable may be built this way : * $ gcc -fno-asynchronous-unwind-tables -fno-ident -s -Os -nostdlib \ * -static -include nolibc.h -lgcc -o hello hello.c * * A very useful calling convention table may be found here : * http://man7.org/linux/man-pages/man2/syscall.2.html * * This doc is quite convenient though not necessarily up to date : * https://w3challs.com/syscalls/ * */ /* Some archs (at least aarch64) don't expose the regular syscalls anymore by * default, either because they have an "_at" replacement, or because there are * more modern alternatives. For now we'd rather still use them. */ #define __ARCH_WANT_SYSCALL_NO_AT #define __ARCH_WANT_SYSCALL_NO_FLAGS #define __ARCH_WANT_SYSCALL_DEPRECATED #include #include #include #include #include #define NOLIBC /* this way it will be removed if unused */ static int errno; #ifndef NOLIBC_IGNORE_ERRNO #define SET_ERRNO(v) do { errno = (v); } while (0) #else #define SET_ERRNO(v) do { } while (0) #endif /* errno codes all ensure that they will not conflict with a valid pointer * because they all correspond to the highest addressable memry page. */ #define MAX_ERRNO 4095 /* Declare a few quite common macros and types that usually are in stdlib.h, * stdint.h, ctype.h, unistd.h and a few other common locations. */ #define NULL ((void *)0) /* stdint types */ typedef unsigned char uint8_t; typedef signed char int8_t; typedef unsigned short uint16_t; typedef signed short int16_t; typedef unsigned int uint32_t; typedef signed int int32_t; typedef unsigned long long uint64_t; typedef signed long long int64_t; typedef unsigned long size_t; typedef signed long ssize_t; typedef unsigned long uintptr_t; typedef signed long intptr_t; typedef signed long ptrdiff_t; /* for stat() */ typedef unsigned int dev_t; typedef unsigned long ino_t; typedef unsigned int mode_t; typedef signed int pid_t; typedef unsigned int uid_t; typedef unsigned int gid_t; typedef unsigned long nlink_t; typedef signed long off_t; typedef signed long blksize_t; typedef signed long blkcnt_t; typedef signed long time_t; /* for poll() */ struct pollfd { int fd; short int events; short int revents; }; /* for select() */ struct timeval { long tv_sec; long tv_usec; }; /* for pselect() */ struct timespec { long tv_sec; long tv_nsec; }; /* for gettimeofday() */ struct timezone { int tz_minuteswest; int tz_dsttime; }; /* for getdents64() */ struct linux_dirent64 { uint64_t d_ino; int64_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[]; }; /* commonly an fd_set represents 256 FDs */ #define FD_SETSIZE 256 typedef struct { uint32_t fd32[FD_SETSIZE/32]; } fd_set; /* needed by wait4() */ struct rusage { struct timeval ru_utime; struct timeval ru_stime; long ru_maxrss; long ru_ixrss; long ru_idrss; long ru_isrss; long ru_minflt; long ru_majflt; long ru_nswap; long ru_inblock; long ru_oublock; long ru_msgsnd; long ru_msgrcv; long ru_nsignals; long ru_nvcsw; long ru_nivcsw; }; /* stat flags (WARNING, octal here) */ #define S_IFDIR 0040000 #define S_IFCHR 0020000 #define S_IFBLK 0060000 #define S_IFREG 0100000 #define S_IFIFO 0010000 #define S_IFLNK 0120000 #define S_IFSOCK 0140000 #define S_IFMT 0170000 #define S_ISDIR(mode) (((mode) & S_IFDIR) == S_IFDIR) #define S_ISCHR(mode) (((mode) & S_IFCHR) == S_IFCHR) #define S_ISBLK(mode) (((mode) & S_IFBLK) == S_IFBLK) #define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG) #define S_ISFIFO(mode) (((mode) & S_IFIFO) == S_IFIFO) #define S_ISLNK(mode) (((mode) & S_IFLNK) == S_IFLNK) #define S_ISSOCK(mode) (((mode) & S_IFSOCK) == S_IFSOCK) #define DT_UNKNOWN 0 #define DT_FIFO 1 #define DT_CHR 2 #define DT_DIR 4 #define DT_BLK 6 #define DT_REG 8 #define DT_LNK 10 #define DT_SOCK 12 /* all the *at functions */ #ifndef AT_FDWCD #define AT_FDCWD -100 #endif /* lseek */ #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 /* reboot */ #define LINUX_REBOOT_MAGIC1 0xfee1dead #define LINUX_REBOOT_MAGIC2 0x28121969 #define LINUX_REBOOT_CMD_HALT 0xcdef0123 #define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedc #define LINUX_REBOOT_CMD_RESTART 0x01234567 #define LINUX_REBOOT_CMD_SW_SUSPEND 0xd000fce2 /* The format of the struct as returned by the libc to the application, which * significantly differs from the format returned by the stat() syscall flavours. */ struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ }; #define WEXITSTATUS(status) (((status) & 0xff00) >> 8) #define WIFEXITED(status) (((status) & 0x7f) == 0) /* Below comes the architecture-specific code. For each architecture, we have * the syscall declarations and the _start code definition. This is the only * global part. On all architectures the kernel puts everything in the stack * before jumping to _start just above us, without any return address (_start * is not a function but an entry pint). So at the stack pointer we find argc. * Then argv[] begins, and ends at the first NULL. Then we have envp which * starts and ends with a NULL as well. So envp=argv+argc+1. */ #if defined(__x86_64__) /* Syscalls for x86_64 : * - registers are 64-bit * - syscall number is passed in rax * - arguments are in rdi, rsi, rdx, r10, r8, r9 respectively * - the system call is performed by calling the syscall instruction * - syscall return comes in rax * - rcx and r8..r11 may be clobbered, others are preserved. * - the arguments are cast to long and assigned into the target registers * which are then simply passed as registers to the asm code, so that we * don't have to experience issues with register constraints. * - the syscall number is always specified last in order to allow to force * some registers before (gcc refuses a %-register at the last position). */ #define my_syscall0(num) \ ({ \ long _ret; \ register long _num asm("rax") = (num); \ \ asm volatile ( \ "syscall\n" \ : "=a" (_ret) \ : "0"(_num) \ : "rcx", "r8", "r9", "r10", "r11", "memory", "cc" \ ); \ _ret; \ }) #define my_syscall1(num, arg1) \ ({ \ long _ret; \ register long _num asm("rax") = (num); \ register long _arg1 asm("rdi") = (long)(arg1); \ \ asm volatile ( \ "syscall\n" \ : "=a" (_ret) \ : "r"(_arg1), \ "0"(_num) \ : "rcx", "r8", "r9", "r10", "r11", "memory", "cc" \ ); \ _ret; \ }) #define my_syscall2(num, arg1, arg2) \ ({ \ long _ret; \ register long _num asm("rax") = (num); \ register long _arg1 asm("rdi") = (long)(arg1); \ register long _arg2 asm("rsi") = (long)(arg2); \ \ asm volatile ( \ "syscall\n" \ : "=a" (_ret) \ : "r"(_arg1), "r"(_arg2), \ "0"(_num) \ : "rcx", "r8", "r9", "r10", "r11", "memory", "cc" \ ); \ _ret; \ }) #define my_syscall3(num, arg1, arg2, arg3) \ ({ \ long _ret; \ register long _num asm("rax") = (num); \ register long _arg1 asm("rdi") = (long)(arg1); \ register long _arg2 asm("rsi") = (long)(arg2); \ register long _arg3 asm("rdx") = (long)(arg3); \ \ asm volatile ( \ "syscall\n" \ : "=a" (_ret) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ "0"(_num) \ : "rcx", "r8", "r9", "r10", "r11", "memory", "cc" \ ); \ _ret; \ }) #define my_syscall4(num, arg1, arg2, arg3, arg4) \ ({ \ long _ret; \ register long _num asm("rax") = (num); \ register long _arg1 asm("rdi") = (long)(arg1); \ register long _arg2 asm("rsi") = (long)(arg2); \ register long _arg3 asm("rdx") = (long)(arg3); \ register long _arg4 asm("r10") = (long)(arg4); \ \ asm volatile ( \ "syscall\n" \ : "=a" (_ret), "=r"(_arg4) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ "0"(_num) \ : "rcx", "r8", "r9", "r11", "memory", "cc" \ ); \ _ret; \ }) #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ ({ \ long _ret; \ register long _num asm("rax") = (num); \ register long _arg1 asm("rdi") = (long)(arg1); \ register long _arg2 asm("rsi") = (long)(arg2); \ register long _arg3 asm("rdx") = (long)(arg3); \ register long _arg4 asm("r10") = (long)(arg4); \ register long _arg5 asm("r8") = (long)(arg5); \ \ asm volatile ( \ "syscall\n" \ : "=a" (_ret), "=r"(_arg4), "=r"(_arg5) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ "0"(_num) \ : "rcx", "r9", "r11", "memory", "cc" \ ); \ _ret; \ }) #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ ({ \ long _ret; \ register long _num asm("rax") = (num); \ register long _arg1 asm("rdi") = (long)(arg1); \ register long _arg2 asm("rsi") = (long)(arg2); \ register long _arg3 asm("rdx") = (long)(arg3); \ register long _arg4 asm("r10") = (long)(arg4); \ register long _arg5 asm("r8") = (long)(arg5); \ register long _arg6 asm("r9") = (long)(arg6); \ \ asm volatile ( \ "syscall\n" \ : "=a" (_ret), "=r"(_arg4), "=r"(_arg5) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ "r"(_arg6), "0"(_num) \ : "rcx", "r11", "memory", "cc" \ ); \ _ret; \ }) /* startup code */ asm(".section .text\n" ".global _start\n" "_start:\n" "pop %rdi\n" // argc (first arg, %rdi) "mov %rsp, %rsi\n" // argv[] (second arg, %rsi) "lea 8(%rsi,%rdi,8),%rdx\n" // then a NULL then envp (third arg, %rdx) "and $-16, %rsp\n" // x86 ABI : esp must be 16-byte aligned when "sub $8, %rsp\n" // entering the callee "call main\n" // main() returns the status code, we'll exit with it. "movzb %al, %rdi\n" // retrieve exit code from 8 lower bits "mov $60, %rax\n" // NR_exit == 60 "syscall\n" // really exit "hlt\n" // ensure it does not return ""); /* fcntl / open */ #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #define O_CREAT 0x40 #define O_EXCL 0x80 #define O_NOCTTY 0x100 #define O_TRUNC 0x200 #define O_APPEND 0x400 #define O_NONBLOCK 0x800 #define O_DIRECTORY 0x10000 /* The struct returned by the stat() syscall, equivalent to stat64(). The * syscall returns 116 bytes and stops in the middle of __unused. */ struct sys_stat_struct { unsigned long st_dev; unsigned long st_ino; unsigned long st_nlink; unsigned int st_mode; unsigned int st_uid; unsigned int st_gid; unsigned int __pad0; unsigned long st_rdev; long st_size; long st_blksize; long st_blocks; unsigned long st_atime; unsigned long st_atime_nsec; unsigned long st_mtime; unsigned long st_mtime_nsec; unsigned long st_ctime; unsigned long st_ctime_nsec; long __unused[3]; }; #elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) /* Syscalls for i386 : * - mostly similar to x86_64 * - registers are 32-bit * - syscall number is passed in eax * - arguments are in ebx, ecx, edx, esi, edi, ebp respectively * - all registers are preserved (except eax of course) * - the system call is performed by calling int $0x80 * - syscall return comes in eax * - the arguments are cast to long and assigned into the target registers * which are then simply passed as registers to the asm code, so that we * don't have to experience issues with register constraints. * - the syscall number is always specified last in order to allow to force * some registers before (gcc refuses a %-register at the last position). * * Also, i386 supports the old_select syscall if newselect is not available */ #define __ARCH_WANT_SYS_OLD_SELECT #define my_syscall0(num) \ ({ \ long _ret; \ register long _num asm("eax") = (num); \ \ asm volatile ( \ "int $0x80\n" \ : "=a" (_ret) \ : "0"(_num) \ : "memory", "cc" \ ); \ _ret; \ }) #define my_syscall1(num, arg1) \ ({ \ long _ret; \ register long _num asm("eax") = (num); \ register long _arg1 asm("ebx") = (long)(arg1); \ \ asm volatile ( \ "int $0x80\n" \ : "=a" (_ret) \ : "r"(_arg1), \ "0"(_num) \ : "memory", "cc" \ ); \ _ret; \ }) #define my_syscall2(num, arg1, arg2) \ ({ \ long _ret; \ register long _num asm("eax") = (num); \ register long _arg1 asm("ebx") = (long)(arg1); \ register long _arg2 asm("ecx") = (long)(arg2); \ \ asm volatile ( \ "int $0x80\n" \ : "=a" (_ret) \ : "r"(_arg1), "r"(_arg2), \ "0"(_num) \ : "memory", "cc" \ ); \ _ret; \ }) #define my_syscall3(num, arg1, arg2, arg3) \ ({ \ long _ret; \ register long _num asm("eax") = (num); \ register long _arg1 asm("ebx") = (long)(arg1); \ register long _arg2 asm("ecx") = (long)(arg2); \ register long _arg3 asm("edx") = (long)(arg3); \ \ asm volatile ( \ "int $0x80\n" \ : "=a" (_ret) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ "0"(_num) \ : "memory", "cc" \ ); \ _ret; \ }) #define my_syscall4(num, arg1, arg2, arg3, arg4) \ ({ \ long _ret; \ register long _num asm("eax") = (num); \ register long _arg1 asm("ebx") = (long)(arg1); \ register long _arg2 asm("ecx") = (long)(arg2); \ register long _arg3 asm("edx") = (long)(arg3); \ register long _arg4 asm("esi") = (long)(arg4); \ \ asm volatile ( \ "int $0x80\n" \ : "=a" (_ret) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ "0"(_num) \ : "memory", "cc" \ ); \ _ret; \ }) #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ ({ \ long _ret; \ register long _num asm("eax") = (num); \ register long _arg1 asm("ebx") = (long)(arg1); \ register long _arg2 asm("ecx") = (long)(arg2); \ register long _arg3 asm("edx") = (long)(arg3); \ register long _arg4 asm("esi") = (long)(arg4); \ register long _arg5 asm("edi") = (long)(arg5); \ \ asm volatile ( \ "int $0x80\n" \ : "=a" (_ret) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ "0"(_num) \ : "memory", "cc" \ ); \ _ret; \ }) /* startup code */ asm(".section .text\n" ".global _start\n" "_start:\n" "pop %eax\n" // argc (first arg, %eax) "mov %esp, %ebx\n" // argv[] (second arg, %ebx) "lea 4(%ebx,%eax,4),%ecx\n" // then a NULL then envp (third arg, %ecx) "and $-16, %esp\n" // x86 ABI : esp must be 16-byte aligned when "push %ecx\n" // push all registers on the stack so that we "push %ebx\n" // support both regparm and plain stack modes "push %eax\n" "call main\n" // main() returns the status code in %eax "movzbl %al, %ebx\n" // retrieve exit code from lower 8 bits "movl $1, %eax\n" // NR_exit == 1 "int $0x80\n" // exit now "hlt\n" // ensure it does not ""); /* fcntl / open */ #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #define O_CREAT 0x40 #define O_EXCL 0x80 #define O_NOCTTY 0x100 #define O_TRUNC 0x200 #define O_APPEND 0x400 #define O_NONBLOCK 0x800 #define O_DIRECTORY 0x10000 /* The struct returned by the stat() syscall, 32-bit only, the syscall returns * exactly 56 bytes (stops before the unused array). */ struct sys_stat_struct { unsigned long st_dev; unsigned long st_ino; unsigned short st_mode; unsigned short st_nlink; unsigned short st_uid; unsigned short st_gid; unsigned long st_rdev; unsigned long st_size; unsigned long st_blksize; unsigned long st_blocks; unsigned long st_atime; unsigned long st_atime_nsec; unsigned long st_mtime; unsigned long st_mtime_nsec; unsigned long st_ctime; unsigned long st_ctime_nsec; unsigned long __unused[2]; }; #elif defined(__ARM_EABI__) /* Syscalls for ARM in ARM or Thumb modes : * - registers are 32-bit * - stack is 8-byte aligned * ( http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html) * - syscall number is passed in r7 * - arguments are in r0, r1, r2, r3, r4, r5 * - the system call is performed by calling svc #0 * - syscall return comes in r0. * - only lr is clobbered. * - the arguments are cast to long and assigned into the target registers * which are then simply passed as registers to the asm code, so that we * don't have to experience issues with register constraints. * - the syscall number is always specified last in order to allow to force * some registers before (gcc refuses a %-register at the last position). * * Also, ARM supports the old_select syscall if newselect is not available */ #define __ARCH_WANT_SYS_OLD_SELECT #define my_syscall0(num) \ ({ \ register long _num asm("r7") = (num); \ register long _arg1 asm("r0"); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_num) \ : "memory", "cc", "lr" \ ); \ _arg1; \ }) #define my_syscall1(num, arg1) \ ({ \ register long _num asm("r7") = (num); \ register long _arg1 asm("r0") = (long)(arg1); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_arg1), \ "r"(_num) \ : "memory", "cc", "lr" \ ); \ _arg1; \ }) #define my_syscall2(num, arg1, arg2) \ ({ \ register long _num asm("r7") = (num); \ register long _arg1 asm("r0") = (long)(arg1); \ register long _arg2 asm("r1") = (long)(arg2); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_arg1), "r"(_arg2), \ "r"(_num) \ : "memory", "cc", "lr" \ ); \ _arg1; \ }) #define my_syscall3(num, arg1, arg2, arg3) \ ({ \ register long _num asm("r7") = (num); \ register long _arg1 asm("r0") = (long)(arg1); \ register long _arg2 asm("r1") = (long)(arg2); \ register long _arg3 asm("r2") = (long)(arg3); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ "r"(_num) \ : "memory", "cc", "lr" \ ); \ _arg1; \ }) #define my_syscall4(num, arg1, arg2, arg3, arg4) \ ({ \ register long _num asm("r7") = (num); \ register long _arg1 asm("r0") = (long)(arg1); \ register long _arg2 asm("r1") = (long)(arg2); \ register long _arg3 asm("r2") = (long)(arg3); \ register long _arg4 asm("r3") = (long)(arg4); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ "r"(_num) \ : "memory", "cc", "lr" \ ); \ _arg1; \ }) #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ ({ \ register long _num asm("r7") = (num); \ register long _arg1 asm("r0") = (long)(arg1); \ register long _arg2 asm("r1") = (long)(arg2); \ register long _arg3 asm("r2") = (long)(arg3); \ register long _arg4 asm("r3") = (long)(arg4); \ register long _arg5 asm("r4") = (long)(arg5); \ \ asm volatile ( \ "svc #0\n" \ : "=r" (_arg1) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ "r"(_num) \ : "memory", "cc", "lr" \ ); \ _arg1; \ }) /* startup code */ asm(".section .text\n" ".global _start\n" "_start:\n" #if defined(__THUMBEB__) || defined(__THUMBEL__) /* We enter here in 32-bit mode but if some previous functions were in * 16-bit mode, the assembler cannot know, so we need to tell it we're in * 32-bit now, then switch to 16-bit (is there a better way to do it than * adding 1 by hand ?) and tell the asm we're now in 16-bit mode so that * it generates correct instructions. Note that we do not support thumb1. */ ".code 32\n" "add r0, pc, #1\n" "bx r0\n" ".code 16\n" #endif "pop {%r0}\n" // argc was in the stack "mov %r1, %sp\n" // argv = sp "add %r2, %r1, %r0, lsl #2\n" // envp = argv + 4*argc ... "add %r2, %r2, $4\n" // ... + 4 "and %r3, %r1, $-8\n" // AAPCS : sp must be 8-byte aligned in the "mov %sp, %r3\n" // callee, an bl doesn't push (lr=pc) "bl main\n" // main() returns the status code, we'll exit with it. "and %r0, %r0, $0xff\n" // limit exit code to 8 bits "movs r7, $1\n" // NR_exit == 1 "svc $0x00\n" ""); /* fcntl / open */ #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #define O_CREAT 0x40 #define O_EXCL 0x80 #define O_NOCTTY 0x100 #define O_TRUNC 0x200 #define O_APPEND 0x400 #define O_NONBLOCK 0x800 #define O_DIRECTORY 0x4000 /* The struct returned by the stat() syscall, 32-bit only, the syscall returns * exactly 56 bytes (stops before the unused array). In big endian, the format * differs as devices are returned as short only. */ struct sys_stat_struct { #if defined(__ARMEB__) unsigned short st_dev; unsigned short __pad1; #else unsigned long st_dev; #endif unsigned long st_ino; unsigned short st_mode; unsigned short st_nlink; unsigned short st_uid; unsigned short st_gid; #if defined(__ARMEB__) unsigned short st_rdev; unsigned short __pad2; #else unsigned long st_rdev; #endif unsigned long st_size; unsigned long st_blksize; unsigned long st_blocks; unsigned long st_atime; unsigned long st_atime_nsec; unsigned long st_mtime; unsigned long st_mtime_nsec; unsigned long st_ctime; unsigned long st_ctime_nsec; unsigned long __unused[2]; }; #elif defined(__aarch64__) /* Syscalls for AARCH64 : * - registers are 64-bit * - stack is 16-byte aligned * - syscall number is passed in x8 * - arguments are in x0, x1, x2, x3, x4, x5 * - the system call is performed by calling svc 0 * - syscall return comes in x0. * - the arguments are cast to long and assigned into the target registers * which are then simply passed as registers to the asm code, so that we * don't have to experience issues with register constraints. * * On aarch64, select() is not implemented so we have to use pselect6(). */ #define __ARCH_WANT_SYS_PSELECT6 #define my_syscall0(num) \ ({ \ register long _num asm("x8") = (num); \ register long _arg1 asm("x0"); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_num) \ : "memory", "cc" \ ); \ _arg1; \ }) #define my_syscall1(num, arg1) \ ({ \ register long _num asm("x8") = (num); \ register long _arg1 asm("x0") = (long)(arg1); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_arg1), \ "r"(_num) \ : "memory", "cc" \ ); \ _arg1; \ }) #define my_syscall2(num, arg1, arg2) \ ({ \ register long _num asm("x8") = (num); \ register long _arg1 asm("x0") = (long)(arg1); \ register long _arg2 asm("x1") = (long)(arg2); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_arg1), "r"(_arg2), \ "r"(_num) \ : "memory", "cc" \ ); \ _arg1; \ }) #define my_syscall3(num, arg1, arg2, arg3) \ ({ \ register long _num asm("x8") = (num); \ register long _arg1 asm("x0") = (long)(arg1); \ register long _arg2 asm("x1") = (long)(arg2); \ register long _arg3 asm("x2") = (long)(arg3); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ "r"(_num) \ : "memory", "cc" \ ); \ _arg1; \ }) #define my_syscall4(num, arg1, arg2, arg3, arg4) \ ({ \ register long _num asm("x8") = (num); \ register long _arg1 asm("x0") = (long)(arg1); \ register long _arg2 asm("x1") = (long)(arg2); \ register long _arg3 asm("x2") = (long)(arg3); \ register long _arg4 asm("x3") = (long)(arg4); \ \ asm volatile ( \ "svc #0\n" \ : "=r"(_arg1) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ "r"(_num) \ : "memory", "cc" \ ); \ _arg1; \ }) #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ ({ \ register long _num asm("x8") = (num); \ register long _arg1 asm("x0") = (long)(arg1); \ register long _arg2 asm("x1") = (long)(arg2); \ register long _arg3 asm("x2") = (long)(arg3); \ register long _arg4 asm("x3") = (long)(arg4); \ register long _arg5 asm("x4") = (long)(arg5); \ \ asm volatile ( \ "svc #0\n" \ : "=r" (_arg1) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ "r"(_num) \ : "memory", "cc" \ ); \ _arg1; \ }) #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ ({ \ register long _num asm("x8") = (num); \ register long _arg1 asm("x0") = (long)(arg1); \ register long _arg2 asm("x1") = (long)(arg2); \ register long _arg3 asm("x2") = (long)(arg3); \ register long _arg4 asm("x3") = (long)(arg4); \ register long _arg5 asm("x4") = (long)(arg5); \ register long _arg6 asm("x5") = (long)(arg6); \ \ asm volatile ( \ "svc #0\n" \ : "=r" (_arg1) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ "r"(_arg6), "r"(_num) \ : "memory", "cc" \ ); \ _arg1; \ }) /* startup code */ asm(".section .text\n" ".global _start\n" "_start:\n" "ldr x0, [sp]\n" // argc (x0) was in the stack "add x1, sp, 8\n" // argv (x1) = sp "lsl x2, x0, 3\n" // envp (x2) = 8*argc ... "add x2, x2, 8\n" // + 8 (skip null) "add x2, x2, x1\n" // + argv "and sp, x1, -16\n" // sp must be 16-byte aligned in the callee "bl main\n" // main() returns the status code, we'll exit with it. "and x0, x0, 0xff\n" // limit exit code to 8 bits "mov x8, 93\n" // NR_exit == 93 "svc #0\n" ""); /* fcntl / open */ #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #define O_CREAT 0x40 #define O_EXCL 0x80 #define O_NOCTTY 0x100 #define O_TRUNC 0x200 #define O_APPEND 0x400 #define O_NONBLOCK 0x800 #define O_DIRECTORY 0x4000 /* The struct returned by the newfstatat() syscall. Differs slightly from the * x86_64's stat one by field ordering, so be careful. */ struct sys_stat_struct { unsigned long st_dev; unsigned long st_ino; unsigned int st_mode; unsigned int st_nlink; unsigned int st_uid; unsigned int st_gid; unsigned long st_rdev; unsigned long __pad1; long st_size; int st_blksize; int __pad2; long st_blocks; long st_atime; unsigned long st_atime_nsec; long st_mtime; unsigned long st_mtime_nsec; long st_ctime; unsigned long st_ctime_nsec; unsigned int __unused[2]; }; #elif defined(__mips__) && defined(_ABIO32) /* Syscalls for MIPS ABI O32 : * - WARNING! there's always a delayed slot! * - WARNING again, the syntax is different, registers take a '$' and numbers * do not. * - registers are 32-bit * - stack is 8-byte aligned * - syscall number is passed in v0 (starts at 0xfa0). * - arguments are in a0, a1, a2, a3, then the stack. The caller needs to * leave some room in the stack for the callee to save a0..a3 if needed. * - Many registers are clobbered, in fact only a0..a2 and s0..s8 are * preserved. See: https://www.linux-mips.org/wiki/Syscall as well as * scall32-o32.S in the kernel sources. * - the system call is performed by calling "syscall" * - syscall return comes in v0, and register a3 needs to be checked to know * if an error occured, in which case errno is in v0. * - the arguments are cast to long and assigned into the target registers * which are then simply passed as registers to the asm code, so that we * don't have to experience issues with register constraints. */ #define my_syscall0(num) \ ({ \ register long _num asm("v0") = (num); \ register long _arg4 asm("a3"); \ \ asm volatile ( \ "addiu $sp, $sp, -32\n" \ "syscall\n" \ "addiu $sp, $sp, 32\n" \ : "=r"(_num), "=r"(_arg4) \ : "r"(_num) \ : "memory", "cc", "at", "v1", "hi", "lo", \ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ ); \ _arg4 ? -_num : _num; \ }) #define my_syscall1(num, arg1) \ ({ \ register long _num asm("v0") = (num); \ register long _arg1 asm("a0") = (long)(arg1); \ register long _arg4 asm("a3"); \ \ asm volatile ( \ "addiu $sp, $sp, -32\n" \ "syscall\n" \ "addiu $sp, $sp, 32\n" \ : "=r"(_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1) \ : "memory", "cc", "at", "v1", "hi", "lo", \ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ ); \ _arg4 ? -_num : _num; \ }) #define my_syscall2(num, arg1, arg2) \ ({ \ register long _num asm("v0") = (num); \ register long _arg1 asm("a0") = (long)(arg1); \ register long _arg2 asm("a1") = (long)(arg2); \ register long _arg4 asm("a3"); \ \ asm volatile ( \ "addiu $sp, $sp, -32\n" \ "syscall\n" \ "addiu $sp, $sp, 32\n" \ : "=r"(_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1), "r"(_arg2) \ : "memory", "cc", "at", "v1", "hi", "lo", \ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ ); \ _arg4 ? -_num : _num; \ }) #define my_syscall3(num, arg1, arg2, arg3) \ ({ \ register long _num asm("v0") = (num); \ register long _arg1 asm("a0") = (long)(arg1); \ register long _arg2 asm("a1") = (long)(arg2); \ register long _arg3 asm("a2") = (long)(arg3); \ register long _arg4 asm("a3"); \ \ asm volatile ( \ "addiu $sp, $sp, -32\n" \ "syscall\n" \ "addiu $sp, $sp, 32\n" \ : "=r"(_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1), "r"(_arg2), "r"(_arg3) \ : "memory", "cc", "at", "v1", "hi", "lo", \ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ ); \ _arg4 ? -_num : _num; \ }) #define my_syscall4(num, arg1, arg2, arg3, arg4) \ ({ \ register long _num asm("v0") = (num); \ register long _arg1 asm("a0") = (long)(arg1); \ register long _arg2 asm("a1") = (long)(arg2); \ register long _arg3 asm("a2") = (long)(arg3); \ register long _arg4 asm("a3") = (long)(arg4); \ \ asm volatile ( \ "addiu $sp, $sp, -32\n" \ "syscall\n" \ "addiu $sp, $sp, 32\n" \ : "=r" (_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \ : "memory", "cc", "at", "v1", "hi", "lo", \ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ ); \ _arg4 ? -_num : _num; \ }) #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ ({ \ register long _num asm("v0") = (num); \ register long _arg1 asm("a0") = (long)(arg1); \ register long _arg2 asm("a1") = (long)(arg2); \ register long _arg3 asm("a2") = (long)(arg3); \ register long _arg4 asm("a3") = (long)(arg4); \ register long _arg5 = (long)(arg5); \ \ asm volatile ( \ "addiu $sp, $sp, -32\n" \ "sw %7, 16($sp)\n" \ "syscall\n " \ "addiu $sp, $sp, 32\n" \ : "=r" (_num), "=r"(_arg4) \ : "0"(_num), \ "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \ : "memory", "cc", "at", "v1", "hi", "lo", \ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ ); \ _arg4 ? -_num : _num; \ }) /* startup code, note that it's called __start on MIPS */ asm(".section .text\n" ".set nomips16\n" ".global __start\n" ".set noreorder\n" ".option pic0\n" ".ent __start\n" "__start:\n" "lw $a0,($sp)\n" // argc was in the stack "addiu $a1, $sp, 4\n" // argv = sp + 4 "sll $a2, $a0, 2\n" // a2 = argc * 4 "add $a2, $a2, $a1\n" // envp = argv + 4*argc ... "addiu $a2, $a2, 4\n" // ... + 4 "li $t0, -8\n" "and $sp, $sp, $t0\n" // sp must be 8-byte aligned "addiu $sp,$sp,-16\n" // the callee expects to save a0..a3 there! "jal main\n" // main() returns the status code, we'll exit with it. "nop\n" // delayed slot "and $a0, $v0, 0xff\n" // limit exit code to 8 bits "li $v0, 4001\n" // NR_exit == 4001 "syscall\n" ".end __start\n" ""); /* fcntl / open */ #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #define O_APPEND 0x0008 #define O_NONBLOCK 0x0080 #define O_CREAT 0x0100 #define O_TRUNC 0x0200 #define O_EXCL 0x0400 #define O_NOCTTY 0x0800 #define O_DIRECTORY 0x10000 /* The struct returned by the stat() syscall. 88 bytes are returned by the * syscall. */ struct sys_stat_struct { unsigned int st_dev; long st_pad1[3]; unsigned long st_ino; unsigned int st_mode; unsigned int st_nlink; unsigned int st_uid; unsigned int st_gid; unsigned int st_rdev; long st_pad2[2]; long st_size; long st_pad3; long st_atime; long st_atime_nsec; long st_mtime; long st_mtime_nsec; long st_ctime; long st_ctime_nsec; long st_blksize; long st_blocks; long st_pad4[14]; }; #endif /* Below are the C functions used to declare the raw syscalls. They try to be * architecture-agnostic, and return either a success or -errno. Declaring them * static will lead to them being inlined in most cases, but it's still possible * to reference them by a pointer if needed. */ static __attribute__((unused)) void *sys_brk(void *addr) { return (void *)my_syscall1(__NR_brk, addr); } static __attribute__((noreturn,unused)) void sys_exit(int status) { my_syscall1(__NR_exit, status & 255); while(1); // shut the "noreturn" warnings. } static __attribute__((unused)) int sys_chdir(const char *path) { return my_syscall1(__NR_chdir, path); } static __attribute__((unused)) int sys_chmod(const char *path, mode_t mode) { #ifdef __NR_fchmodat return my_syscall4(__NR_fchmodat, AT_FDCWD, path, mode, 0); #else return my_syscall2(__NR_chmod, path, mode); #endif } static __attribute__((unused)) int sys_chown(const char *path, uid_t owner, gid_t group) { #ifdef __NR_fchownat return my_syscall5(__NR_fchownat, AT_FDCWD, path, owner, group, 0); #else return my_syscall3(__NR_chown, path, owner, group); #endif } static __attribute__((unused)) int sys_chroot(const char *path) { return my_syscall1(__NR_chroot, path); } static __attribute__((unused)) int sys_close(int fd) { return my_syscall1(__NR_close, fd); } static __attribute__((unused)) int sys_dup(int fd) { return my_syscall1(__NR_dup, fd); } static __attribute__((unused)) int sys_dup2(int old, int new) { return my_syscall2(__NR_dup2, old, new); } static __attribute__((unused)) int sys_execve(const char *filename, char *const argv[], char *const envp[]) { return my_syscall3(__NR_execve, filename, argv, envp); } static __attribute__((unused)) pid_t sys_fork(void) { return my_syscall0(__NR_fork); } static __attribute__((unused)) int sys_fsync(int fd) { return my_syscall1(__NR_fsync, fd); } static __attribute__((unused)) int sys_getdents64(int fd, struct linux_dirent64 *dirp, int count) { return my_syscall3(__NR_getdents64, fd, dirp, count); } static __attribute__((unused)) pid_t sys_getpgrp(void) { return my_syscall0(__NR_getpgrp); } static __attribute__((unused)) pid_t sys_getpid(void) { return my_syscall0(__NR_getpid); } static __attribute__((unused)) int sys_gettimeofday(struct timeval *tv, struct timezone *tz) { return my_syscall2(__NR_gettimeofday, tv, tz); } static __attribute__((unused)) int sys_ioctl(int fd, unsigned long req, void *value) { return my_syscall3(__NR_ioctl, fd, req, value); } static __attribute__((unused)) int sys_kill(pid_t pid, int signal) { return my_syscall2(__NR_kill, pid, signal); } static __attribute__((unused)) int sys_link(const char *old, const char *new) { #ifdef __NR_linkat return my_syscall5(__NR_linkat, AT_FDCWD, old, AT_FDCWD, new, 0); #else return my_syscall2(__NR_link, old, new); #endif } static __attribute__((unused)) off_t sys_lseek(int fd, off_t offset, int whence) { return my_syscall3(__NR_lseek, fd, offset, whence); } static __attribute__((unused)) int sys_mkdir(const char *path, mode_t mode) { #ifdef __NR_mkdirat return my_syscall3(__NR_mkdirat, AT_FDCWD, path, mode); #else return my_syscall2(__NR_mkdir, path, mode); #endif } static __attribute__((unused)) long sys_mknod(const char *path, mode_t mode, dev_t dev) { #ifdef __NR_mknodat return my_syscall4(__NR_mknodat, AT_FDCWD, path, mode, dev); #else return my_syscall3(__NR_mknod, path, mode, dev); #endif } static __attribute__((unused)) int sys_mount(const char *src, const char *tgt, const char *fst, unsigned long flags, const void *data) { return my_syscall5(__NR_mount, src, tgt, fst, flags, data); } static __attribute__((unused)) int sys_open(const char *path, int flags, mode_t mode) { #ifdef __NR_openat return my_syscall4(__NR_openat, AT_FDCWD, path, flags, mode); #else return my_syscall3(__NR_open, path, flags, mode); #endif } static __attribute__((unused)) int sys_pivot_root(const char *new, const char *old) { return my_syscall2(__NR_pivot_root, new, old); } static __attribute__((unused)) int sys_poll(struct pollfd *fds, int nfds, int timeout) { return my_syscall3(__NR_poll, fds, nfds, timeout); } static __attribute__((unused)) ssize_t sys_read(int fd, void *buf, size_t count) { return my_syscall3(__NR_read, fd, buf, count); } static __attribute__((unused)) ssize_t sys_reboot(int magic1, int magic2, int cmd, void *arg) { return my_syscall4(__NR_reboot, magic1, magic2, cmd, arg); } static __attribute__((unused)) int sys_sched_yield(void) { return my_syscall0(__NR_sched_yield); } static __attribute__((unused)) int sys_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout) { #if defined(__ARCH_WANT_SYS_OLD_SELECT) && !defined(__NR__newselect) struct sel_arg_struct { unsigned long n; fd_set *r, *w, *e; struct timeval *t; } arg = { .n = nfds, .r = rfds, .w = wfds, .e = efds, .t = timeout }; return my_syscall1(__NR_select, &arg); #elif defined(__ARCH_WANT_SYS_PSELECT6) && defined(__NR_pselect6) struct timespec t; if (timeout) { t.tv_sec = timeout->tv_sec; t.tv_nsec = timeout->tv_usec * 1000; } return my_syscall6(__NR_pselect6, nfds, rfds, wfds, efds, timeout ? &t : NULL, NULL); #else #ifndef __NR__newselect #define __NR__newselect __NR_select #endif return my_syscall5(__NR__newselect, nfds, rfds, wfds, efds, timeout); #endif } static __attribute__((unused)) int sys_setpgid(pid_t pid, pid_t pgid) { return my_syscall2(__NR_setpgid, pid, pgid); } static __attribute__((unused)) pid_t sys_setsid(void) { return my_syscall0(__NR_setsid); } static __attribute__((unused)) int sys_stat(const char *path, struct stat *buf) { struct sys_stat_struct stat; long ret; #ifdef __NR_newfstatat /* only solution for arm64 */ ret = my_syscall4(__NR_newfstatat, AT_FDCWD, path, &stat, 0); #else ret = my_syscall2(__NR_stat, path, &stat); #endif buf->st_dev = stat.st_dev; buf->st_ino = stat.st_ino; buf->st_mode = stat.st_mode; buf->st_nlink = stat.st_nlink; buf->st_uid = stat.st_uid; buf->st_gid = stat.st_gid; buf->st_rdev = stat.st_rdev; buf->st_size = stat.st_size; buf->st_blksize = stat.st_blksize; buf->st_blocks = stat.st_blocks; buf->st_atime = stat.st_atime; buf->st_mtime = stat.st_mtime; buf->st_ctime = stat.st_ctime; return ret; } static __attribute__((unused)) int sys_symlink(const char *old, const char *new) { #ifdef __NR_symlinkat return my_syscall3(__NR_symlinkat, old, AT_FDCWD, new); #else return my_syscall2(__NR_symlink, old, new); #endif } static __attribute__((unused)) mode_t sys_umask(mode_t mode) { return my_syscall1(__NR_umask, mode); } static __attribute__((unused)) int sys_umount2(const char *path, int flags) { return my_syscall2(__NR_umount2, path, flags); } static __attribute__((unused)) int sys_unlink(const char *path) { #ifdef __NR_unlinkat return my_syscall3(__NR_unlinkat, AT_FDCWD, path, 0); #else return my_syscall1(__NR_unlink, path); #endif } static __attribute__((unused)) pid_t sys_wait4(pid_t pid, int *status, int options, struct rusage *rusage) { return my_syscall4(__NR_wait4, pid, status, options, rusage); } static __attribute__((unused)) pid_t sys_waitpid(pid_t pid, int *status, int options) { return sys_wait4(pid, status, options, 0); } static __attribute__((unused)) pid_t sys_wait(int *status) { return sys_waitpid(-1, status, 0); } static __attribute__((unused)) ssize_t sys_write(int fd, const void *buf, size_t count) { return my_syscall3(__NR_write, fd, buf, count); } /* Below are the libc-compatible syscalls which return x or -1 and set errno. * They rely on the functions above. Similarly they're marked static so that it * is possible to assign pointers to them if needed. */ static __attribute__((unused)) int brk(void *addr) { void *ret = sys_brk(addr); if (!ret) { SET_ERRNO(ENOMEM); return -1; } return 0; } static __attribute__((noreturn,unused)) void exit(int status) { sys_exit(status); } static __attribute__((unused)) int chdir(const char *path) { int ret = sys_chdir(path); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int chmod(const char *path, mode_t mode) { int ret = sys_chmod(path, mode); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int chown(const char *path, uid_t owner, gid_t group) { int ret = sys_chown(path, owner, group); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int chroot(const char *path) { int ret = sys_chroot(path); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int close(int fd) { int ret = sys_close(fd); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int dup2(int old, int new) { int ret = sys_dup2(old, new); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int execve(const char *filename, char *const argv[], char *const envp[]) { int ret = sys_execve(filename, argv, envp); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) pid_t fork(void) { pid_t ret = sys_fork(); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int fsync(int fd) { int ret = sys_fsync(fd); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int getdents64(int fd, struct linux_dirent64 *dirp, int count) { int ret = sys_getdents64(fd, dirp, count); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) pid_t getpgrp(void) { pid_t ret = sys_getpgrp(); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) pid_t getpid(void) { pid_t ret = sys_getpid(); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int gettimeofday(struct timeval *tv, struct timezone *tz) { int ret = sys_gettimeofday(tv, tz); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int ioctl(int fd, unsigned long req, void *value) { int ret = sys_ioctl(fd, req, value); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int kill(pid_t pid, int signal) { int ret = sys_kill(pid, signal); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int link(const char *old, const char *new) { int ret = sys_link(old, new); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) off_t lseek(int fd, off_t offset, int whence) { off_t ret = sys_lseek(fd, offset, whence); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int mkdir(const char *path, mode_t mode) { int ret = sys_mkdir(path, mode); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int mknod(const char *path, mode_t mode, dev_t dev) { int ret = sys_mknod(path, mode, dev); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int mount(const char *src, const char *tgt, const char *fst, unsigned long flags, const void *data) { int ret = sys_mount(src, tgt, fst, flags, data); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int open(const char *path, int flags, mode_t mode) { int ret = sys_open(path, flags, mode); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int pivot_root(const char *new, const char *old) { int ret = sys_pivot_root(new, old); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int poll(struct pollfd *fds, int nfds, int timeout) { int ret = sys_poll(fds, nfds, timeout); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) ssize_t read(int fd, void *buf, size_t count) { ssize_t ret = sys_read(fd, buf, count); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int reboot(int cmd) { int ret = sys_reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, 0); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) void *sbrk(intptr_t inc) { void *ret; /* first call to find current end */ if ((ret = sys_brk(0)) && (sys_brk(ret + inc) == ret + inc)) return ret + inc; SET_ERRNO(ENOMEM); return (void *)-1; } static __attribute__((unused)) int sched_yield(void) { int ret = sys_sched_yield(); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout) { int ret = sys_select(nfds, rfds, wfds, efds, timeout); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int setpgid(pid_t pid, pid_t pgid) { int ret = sys_setpgid(pid, pgid); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) pid_t setsid(void) { pid_t ret = sys_setsid(); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) unsigned int sleep(unsigned int seconds) { struct timeval my_timeval = { seconds, 0 }; if (sys_select(0, 0, 0, 0, &my_timeval) < 0) return my_timeval.tv_sec + !!my_timeval.tv_usec; else return 0; } static __attribute__((unused)) int stat(const char *path, struct stat *buf) { int ret = sys_stat(path, buf); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int symlink(const char *old, const char *new) { int ret = sys_symlink(old, new); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int tcsetpgrp(int fd, pid_t pid) { return ioctl(fd, TIOCSPGRP, &pid); } static __attribute__((unused)) mode_t umask(mode_t mode) { return sys_umask(mode); } static __attribute__((unused)) int umount2(const char *path, int flags) { int ret = sys_umount2(path, flags); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) int unlink(const char *path) { int ret = sys_unlink(path); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage) { pid_t ret = sys_wait4(pid, status, options, rusage); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) pid_t waitpid(pid_t pid, int *status, int options) { pid_t ret = sys_waitpid(pid, status, options); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) pid_t wait(int *status) { pid_t ret = sys_wait(status); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } static __attribute__((unused)) ssize_t write(int fd, const void *buf, size_t count) { ssize_t ret = sys_write(fd, buf, count); if (ret < 0) { SET_ERRNO(-ret); ret = -1; } return ret; } /* some size-optimized reimplementations of a few common str* and mem* * functions. They're marked static, except memcpy() and raise() which are used * by libgcc on ARM, so they are marked weak instead in order not to cause an * error when building a program made of multiple files (not recommended). */ static __attribute__((unused)) void *memmove(void *dst, const void *src, size_t len) { ssize_t pos = (dst <= src) ? -1 : (long)len; void *ret = dst; while (len--) { pos += (dst <= src) ? 1 : -1; ((char *)dst)[pos] = ((char *)src)[pos]; } return ret; } static __attribute__((unused)) void *memset(void *dst, int b, size_t len) { char *p = dst; while (len--) *(p++) = b; return dst; } static __attribute__((unused)) int memcmp(const void *s1, const void *s2, size_t n) { size_t ofs = 0; char c1 = 0; while (ofs < n && !(c1 = ((char *)s1)[ofs] - ((char *)s2)[ofs])) { ofs++; } return c1; } static __attribute__((unused)) char *strcpy(char *dst, const char *src) { char *ret = dst; while ((*dst++ = *src++)); return ret; } static __attribute__((unused)) char *strchr(const char *s, int c) { while (*s) { if (*s == (char)c) return (char *)s; s++; } return NULL; } static __attribute__((unused)) char *strrchr(const char *s, int c) { const char *ret = NULL; while (*s) { if (*s == (char)c) ret = s; s++; } return (char *)ret; } static __attribute__((unused)) size_t nolibc_strlen(const char *str) { size_t len; for (len = 0; str[len]; len++); return len; } #define strlen(str) ({ \ __builtin_constant_p((str)) ? \ __builtin_strlen((str)) : \ nolibc_strlen((str)); \ }) static __attribute__((unused)) int isdigit(int c) { return (unsigned int)(c - '0') <= 9; } static __attribute__((unused)) long atol(const char *s) { unsigned long ret = 0; unsigned long d; int neg = 0; if (*s == '-') { neg = 1; s++; } while (1) { d = (*s++) - '0'; if (d > 9) break; ret *= 10; ret += d; } return neg ? -ret : ret; } static __attribute__((unused)) int atoi(const char *s) { return atol(s); } static __attribute__((unused)) const char *ltoa(long in) { /* large enough for -9223372036854775808 */ static char buffer[21]; char *pos = buffer + sizeof(buffer) - 1; int neg = in < 0; unsigned long n = neg ? -in : in; *pos-- = '\0'; do { *pos-- = '0' + n % 10; n /= 10; if (pos < buffer) return pos + 1; } while (n); if (neg) *pos-- = '-'; return pos + 1; } __attribute__((weak,unused)) void *memcpy(void *dst, const void *src, size_t len) { return memmove(dst, src, len); } /* needed by libgcc for divide by zero */ __attribute__((weak,unused)) int raise(int signal) { return kill(getpid(), signal); } /* Here come a few helper functions */ static __attribute__((unused)) void FD_ZERO(fd_set *set) { memset(set, 0, sizeof(*set)); } static __attribute__((unused)) void FD_SET(int fd, fd_set *set) { if (fd < 0 || fd >= FD_SETSIZE) return; set->fd32[fd / 32] |= 1 << (fd & 31); } /* WARNING, it only deals with the 4096 first majors and 256 first minors */ static __attribute__((unused)) dev_t makedev(unsigned int major, unsigned int minor) { return ((major & 0xfff) << 8) | (minor & 0xff); }