[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Mikhail Ilyin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# ELF loader\n\nA small elf loader. It can load static and dynamically linked ELF EXEC and DYN (pie) binaries. The loader is PIE program that doesn't depend on libc and calls kernel services directly (z_syscall.c).\n\nIf the loader needs to load a dynamically linked ELF it places an interpreter (usually ld.so) and a requested binary into a memory and then calls the interpreter entry point.\n\n\n## Build\n\nDefault build is for amd64:\n\n```\n$ make\n``` \n\nBuild for i386:\n\n```\n$ make ARCH=i386\n```\n\nSmall build (exclude all messages and printf):\n\n```\n$ make SMALL=1\n```\n\n## Load binaries\n\nRun basic hello world test:\n```\n$ ./test.sh \ndefault        : PASS\nstatic         : PASS\npie            : PASS\nstatic pie     : PASS\n```\n\nRun tests if the loader is built for i386:\n```\n$ M32= ./test.sh\n...\n```\n\nLoad ls:\n```\n$ ./loader /bin/ls\n```\n\nLoad galculator:\n```\n$ ./loader /usr/bin/galculator\n```\n\n"
  },
  {
    "path": "src/Makefile",
    "content": "# make ARCH=i386 SMALL=1 DEBUG=1\n\nARCH ?= amd64\n\nARCHS32 := i386\nARCHS64 := amd64 aarch64\nARCHS := $(ARCHS32) $(ARCHS64)\n\nCFLAGS += -pipe -Wall -Wextra -fPIC -fno-ident -fno-stack-protector -U _FORTIFY_SOURCE\nLDFLAGS += -nostartfiles -nodefaultlibs -nostdlib \nLDFLAGS += -pie -e z_start -Wl,-Bsymbolic,--no-undefined,--build-id=none\nTARGET := loader\n\nifeq \"$(filter $(ARCH),$(ARCHS))\" \"\"\n  $(error ARCH='$(ARCH)' is not supported)\nendif\n\nifeq \"$(filter $(ARCH),$(ARCHS32))\" \"$(ARCH)\"\n  CFLAGS += -m32 -DELFCLASS=ELFCLASS32\n  ASFLAGS += -m32\n  LDFLAGS += -m32\nelse\n  CFLAGS += -DELFCLASS=ELFCLASS64\nendif\n\nifdef DEBUG\n  CFLAGS += -O0 -g\n  ASFLAGS += -g\nelse\n  CFLAGS += -fvisibility=hidden\n  # Disable unwind info to make prog smaller.\n  CFLAGS += -Os -fno-asynchronous-unwind-tables -fno-unwind-tables\n  LDFLAGS += -s\nendif\n\nOBJS := $(patsubst %.c,%.o, $(wildcard *.c))\nOBJS += $(patsubst %.S,%.o, $(wildcard $(ARCH)/*.S))\n\nifdef SMALL\n  OBJS := $(filter-out z_printf.%,$(OBJS))\n  OBJS := $(filter-out z_err.%,$(OBJS))\n  CFLAGS += -DZ_SMALL\nendif\n\n.PHONY: clean all\n\nall: $(TARGET)\n\nloader: $(OBJS)\n\nclean:\n\trm -rf *.o $(TARGET) */*.o\n\n"
  },
  {
    "path": "src/aarch64/z_start.S",
    "content": "\t.text\n\t.align\t4\n\t.globl\tz_start\n\t.hidden\tz_start\n\t.type\tz_start,@function\nz_start:\n\tmov\tx29,\t#0\n\tmov\tx30,\t#0\n\tmov\tx1,\tx0\n\tmov\tx0,\tsp\n\tbl\tz_entry\n\twfi\n\n"
  },
  {
    "path": "src/aarch64/z_syscall.S",
    "content": "\t.text\n\t.align\t4\n\t.globl\tz_syscall\n\t.type\tz_syscall,@function\nz_syscall:\n\tuxtw\tx8,\tw0\n\tmov\tx0,\tx1\n\tmov\tx1,\tx2\n\tmov\tx2,\tx3\n\tmov\tx3,\tx4\n\tmov\tx4,\tx5\n\tmov\tx5,\tx6\n\tmov\tx6,\tx7\n\tsvc\t0x0\n\tret\n\n"
  },
  {
    "path": "src/aarch64/z_trampo.S",
    "content": "\t.text\n\t.align\t4\n\t.globl\tz_trampo\n\t.type\tz_trampo,@function\nz_trampo:\n\tmov\tx3,\tx0\n\tmov\tsp,\tx1\n\tmov\tx0,\tx2\n\tbr\tx3\n\t/* Should not reach. */\n\twfi\n\n"
  },
  {
    "path": "src/amd64/z_start.S",
    "content": "\t.text\n\t.align\t4\n\t.globl\tz_start\n\t.hidden\tz_start\n\t.type\tz_start,@function\nz_start:\n\tmov\t%rsp,\t%rdi\n\tmov\t%rdx,\t%rsi\n\tcall\tz_entry\n\thlt\n\n"
  },
  {
    "path": "src/amd64/z_syscall.S",
    "content": "# User space ABI:   rdi, rsi, rdx, rcx, r8, r9, the rest args on the stack\n# Kernel spaceABI:  rax (nsys), rdi, rsi, rdx, r10, r8, r9 (stack pointer)\n\t.text\n\t.align\t4\n\t.globl\tz_syscall\n\t.type\tz_syscall,@function\nz_syscall:\n\tmov\t%rdi, \t%rax\n\tmov\t%rsi, \t%rdi\n\tmov\t%rdx, \t%rsi\n\tmov\t%rcx, \t%rdx\n\tmov\t%r8,  \t%r10\n\tmov\t%r9,  \t%r8\n\tmov\t8(%rsp),%r9\n\tsyscall\n\tret\n\n"
  },
  {
    "path": "src/amd64/z_trampo.S",
    "content": "\t.text\n\t.align\t4\n\t.globl\tz_trampo\n\t.type\tz_trampo,@function\nz_trampo:\n\tmov\t%rsi,\t%rsp\n\tjmp\t*%rdi\n\t/* Should not reach. */\n\thlt\n\n"
  },
  {
    "path": "src/i386/z_start.S",
    "content": "\t.text\n\t.globl\tz_start\n\t.hidden\tz_start\n\t.type\tz_start,@function\nz_start:\n\tmov\t%esp,\t%eax\n\tpush\t%edx\n\tpush\t%eax\n\tcall\tz_entry\n\thlt\n\n"
  },
  {
    "path": "src/i386/z_syscall.S",
    "content": "\t.text\n\t.globl\tz_syscall\n\t.type\tz_syscall,@function\nz_syscall:\n\t/* Preserve ABI required registers. */\n\tpush\t%ebp\n\tpush\t%edi\n\tpush\t%esi\n\tpush\t%ebx\n\t/* Move arguments to registers. */\t\n\tmov\t44(%esp), %ebp\n\tmov\t40(%esp), %edi\n\tmov\t36(%esp), %esi\n\tmov\t32(%esp), %edx\n\tmov\t28(%esp), %ecx\n\tmov\t24(%esp), %ebx\n\tmov\t20(%esp), %eax\t/* syscall number */\n\t/* Jump to kernel, return value comes to eax. */\n\tint\t$0x80\n\t/* Restore preserved registers. */\n\tpop\t%ebx\n\tpop\t%esi\n\tpop\t%edi\n\tpop\t%ebp\n\tret\n\n"
  },
  {
    "path": "src/i386/z_trampo.S",
    "content": "\t.text\n\t.globl\tz_trampo\n\t.type\tz_trampo,@function\nz_trampo:\n\tmov\t4(%esp), %eax\n\tmov\t8(%esp), %ecx\n\tmov\t12(%esp), %edx\n\tmov\t%ecx, %esp\n\tjmp\t*%eax\n\t/* Should not reach. */\n\thlt\n\n"
  },
  {
    "path": "src/loader.c",
    "content": "#include \"z_asm.h\"\n#include \"z_syscalls.h\"\n#include \"z_utils.h\"\n#include \"z_elf.h\"\n\n#define PAGE_SIZE\t4096\n#define ALIGN\t\t(PAGE_SIZE - 1)\n#define ROUND_PG(x)\t(((x) + (ALIGN)) & ~(ALIGN))\n#define TRUNC_PG(x)\t((x) & ~(ALIGN))\n#define PFLAGS(x)\t((((x) & PF_R) ? PROT_READ : 0) | \\\n\t\t\t (((x) & PF_W) ? PROT_WRITE : 0) | \\\n\t\t\t (((x) & PF_X) ? PROT_EXEC : 0))\n#define LOAD_ERR\t((unsigned long)-1)\n\nstatic void z_fini(void)\n{\n\tz_printf(\"Fini at work\\n\");\n}\n\nstatic int check_ehdr(Elf_Ehdr *ehdr)\n{\n\tunsigned char *e_ident = ehdr->e_ident;\n\treturn (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||\n\t\te_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3 ||\n\t    \te_ident[EI_CLASS] != ELFCLASS ||\n\t\te_ident[EI_VERSION] != EV_CURRENT ||\n\t\t(ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)) ? 0 : 1;\n}\n\nstatic unsigned long loadelf_anon(int fd, Elf_Ehdr *ehdr, Elf_Phdr *phdr)\n{\n\tunsigned long minva, maxva;\n\tElf_Phdr *iter;\n\tssize_t sz;\n\tint flags, dyn = ehdr->e_type == ET_DYN;\n\tunsigned char *p, *base, *hint;\n\n\tminva = (unsigned long)-1;\n\tmaxva = 0;\n\t\n\tfor (iter = phdr; iter < &phdr[ehdr->e_phnum]; iter++) {\n\t\tif (iter->p_type != PT_LOAD)\n\t\t\tcontinue;\n\t\tif (iter->p_vaddr < minva)\n\t\t\tminva = iter->p_vaddr;\n\t\tif (iter->p_vaddr + iter->p_memsz > maxva)\n\t\t\tmaxva = iter->p_vaddr + iter->p_memsz;\n\t}\n\n\tminva = TRUNC_PG(minva);\n\tmaxva = ROUND_PG(maxva);\n\n\t/* For dynamic ELF let the kernel chose the address. */\t\n\thint = dyn ? NULL : (void *)minva;\n\tflags = dyn ? 0 : MAP_FIXED;\n\tflags |= (MAP_PRIVATE | MAP_ANONYMOUS);\n\n\t/* Check that we can hold the whole image. */\n\tbase = z_mmap(hint, maxva - minva, PROT_NONE, flags, -1, 0);\n\tif (base == (void *)-1)\n\t\treturn -1;\n\tz_munmap(base, maxva - minva);\n\n\tflags = MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE;\n\t/* Now map each segment separately in precalculated address. */\n\tfor (iter = phdr; iter < &phdr[ehdr->e_phnum]; iter++) {\n\t\tunsigned long off, start;\n\t\tif (iter->p_type != PT_LOAD)\n\t\t\tcontinue;\n\t\toff = iter->p_vaddr & ALIGN;\n\t\tstart = dyn ? (unsigned long)base : 0;\n\t\tstart += TRUNC_PG(iter->p_vaddr);\n\t\tsz = ROUND_PG(iter->p_memsz + off);\n\n\t\tp = z_mmap((void *)start, sz, PROT_WRITE, flags, -1, 0);\n\t\tif (p == (void *)-1)\n\t\t\tgoto err;\n\t\tif (z_lseek(fd, iter->p_offset, SEEK_SET) < 0)\n\t\t\tgoto err;\n\t\tif (z_read(fd, p + off, iter->p_filesz) !=\n\t\t\t\t(ssize_t)iter->p_filesz)\n\t\t\tgoto err;\n\t\tz_mprotect(p, sz, PFLAGS(iter->p_flags));\n\t}\n\n\treturn (unsigned long)base;\nerr:\n\tz_munmap(base, maxva - minva);\n\treturn LOAD_ERR;\n}\n\n#define Z_PROG\t\t0\n#define Z_INTERP\t1\n\nvoid z_entry(unsigned long *sp, void (*fini)(void))\n{\n\tElf_Ehdr ehdrs[2], *ehdr = ehdrs;\n\tElf_Phdr *phdr, *iter;\n\tElf_auxv_t *av;\n\tchar **argv, **env, **p, *elf_interp = NULL;\n\tunsigned long base[2], entry[2];\n\tconst char *file;\n\tssize_t sz;\n\tint argc, fd, i;\n\n\t(void)fini;\n\n\targc = (int)*(sp);\n\targv = (char **)(sp + 1);\n\tenv = p = (char **)&argv[argc + 1];\n\twhile (*p++ != NULL)\n\t\t;\n\tav = (void *)p;\n\n\t(void)env;\n\tif (argc < 2)\n\t\tz_errx(1, \"no input file\");\n\tfile = argv[1];\n\n\tfor (i = 0;; i++, ehdr++) {\n\t\t/* Open file, read and than check ELF header.*/\n\t\tif ((fd = z_open(file, O_RDONLY)) < 0)\n\t\t\tz_errx(1, \"can't open %s\", file);\n\t\tif (z_read(fd, ehdr, sizeof(*ehdr)) != sizeof(*ehdr))\n\t\t\tz_errx(1, \"can't read ELF header %s\", file);\n\t\tif (!check_ehdr(ehdr))\n\t\t\tz_errx(1, \"bogus ELF header %s\", file);\n\n\t\t/* Read the program header. */\n\t\tsz = ehdr->e_phnum * sizeof(Elf_Phdr);\n\t\tphdr = z_alloca(sz);\n\t\tif (z_lseek(fd, ehdr->e_phoff, SEEK_SET) < 0)\n\t\t\tz_errx(1, \"can't lseek to program header %s\", file);\n\t\tif (z_read(fd, phdr, sz) != sz)\n\t\t\tz_errx(1, \"can't read program header %s\", file);\n\t\t/* Time to load ELF. */\n\t\tif ((base[i] = loadelf_anon(fd, ehdr, phdr)) == LOAD_ERR)\n\t\t\tz_errx(1, \"can't load ELF %s\", file);\n\n\t\t/* Set the entry point, if the file is dynamic than add bias. */\n\t\tentry[i] = ehdr->e_entry + (ehdr->e_type == ET_DYN ? base[i] : 0);\n\t\t/* The second round, we've loaded ELF interp. */\n\t\tif (file == elf_interp) {\n\t\t\tz_close(fd);\n\t\t\tbreak;\n\t\t}\n\n\t\tfor (iter = phdr; iter < &phdr[ehdr->e_phnum]; iter++) {\n\t\t\tif (iter->p_type != PT_INTERP)\n\t\t\t\tcontinue;\n\t\t\telf_interp = z_alloca(iter->p_filesz);\n\t\t\tif (z_lseek(fd, iter->p_offset, SEEK_SET) < 0)\n\t\t\t\tz_errx(1, \"can't lseek interp segment\");\n\t\t\tif (z_read(fd, elf_interp, iter->p_filesz) !=\n\t\t\t\t\t(ssize_t)iter->p_filesz)\n\t\t\t\tz_errx(1, \"can't read interp segment\");\n\t\t\tif (elf_interp[iter->p_filesz - 1] != '\\0')\n\t\t\t\tz_errx(1, \"bogus interp path\");\n\t\t\tfile = elf_interp;\n\t\t}\n\n\t\tz_close(fd);\n\t\t/* Looks like the ELF is static -- leave the loop. */\n\t\tif (elf_interp == NULL)\n\t\t\tbreak;\n\t}\n\n\t/* Reassign some vectors that are important for\n\t * the dynamic linker and for lib C. */\n#define AVSET(t, v, expr) case (t): (v)->a_un.a_val = (expr); break\n\twhile (av->a_type != AT_NULL) {\n\t\tswitch (av->a_type) {\n\t\tAVSET(AT_PHDR, av, base[Z_PROG] + ehdrs[Z_PROG].e_phoff);\n\t\tAVSET(AT_PHNUM, av, ehdrs[Z_PROG].e_phnum);\n\t\tAVSET(AT_PHENT, av, ehdrs[Z_PROG].e_phentsize);\n\t\tAVSET(AT_ENTRY, av, entry[Z_PROG]);\n\t\tAVSET(AT_EXECFN, av, (unsigned long)argv[1]);\n\t\tAVSET(AT_BASE, av, elf_interp ?\n\t\t\t\tbase[Z_INTERP] : av->a_un.a_val);\n\t\t}\n\t\t++av;\n\t}\n#undef AVSET\n\t++av;\n\n\t/* Shift argv, env and av. */\n\tz_memcpy(&argv[0], &argv[1],\n\t\t (unsigned long)av - (unsigned long)&argv[1]);\n\t/* SP points to argc. */\n\t(*sp)--;\n\n\tz_trampo((void (*)(void))(elf_interp ?\n\t\t\tentry[Z_INTERP] : entry[Z_PROG]), sp, z_fini);\n\t/* Should not reach. */\n\tz_exit(0);\n}\n\n"
  },
  {
    "path": "src/test.sh",
    "content": "#!/bin/sh\n\nTEST_DIR=/tmp/loadertests\nTEST_SRC=${TEST_DIR}/test.c\nCFLAGS=\"-Wall -Wextra -s ${M32+-m32}\"\n\nset -eu\n\ntrap 'rm -rf $TEST_DIR' EXIT\n\nPATH=.:$PATH\n\ncommand -v loader > /dev/null || { echo >&2 \"error: build the project\"; exit 1; }\n\nmkdir -p $TEST_DIR\n\ncat > $TEST_SRC << EOF\n#include <stdio.h>\n\nint main()\n{\n    printf(\"Hello %s!\\n\", \"world\");\n    return 0;\n}\nEOF\n\nfor opt in \"default@\" \"static@ -static\" \"pie@ -fPIE\" \"static pie@ -static-pie -fPIE\"; do\n    label=${opt%@*}\n    flags=${opt#*@}\n    out=$TEST_DIR/$label\n\n    printf \"%-15s: \" \"$label\" \n    cc $CFLAGS $flags \"$TEST_SRC\" -o \"$out\"\n    loader \"$out\" >/dev/null 2>&1 && echo PASS || echo FAIL\ndone\n\n"
  },
  {
    "path": "src/z_asm.h",
    "content": "#ifndef Z_ASM_H\n#define Z_ASM_H\n\n#define PUBLIC __attribute__((visibility (\"default\")))\n#define PRIVATE __attribute__((visibility (\"hidden\")))\n\nPRIVATE void z_start(void);\nPRIVATE void z_trampo(void (*entry)(void), unsigned long *sp, void (*fini)(void));\nPRIVATE long z_syscall(int n, ...);\n\n#endif /* Z_ASM_H */\n\n"
  },
  {
    "path": "src/z_elf.h",
    "content": "#ifndef Z_ELF_H\n#define Z_ELF_H\n\n#include <elf.h>\n\n#if ELFCLASS == ELFCLASS64\n#  define Elf_Ehdr\tElf64_Ehdr\n#  define Elf_Phdr\tElf64_Phdr\n#  define Elf_auxv_t\tElf64_auxv_t\n#elif ELFCLASS == ELFCLASS32\n#  define Elf_Ehdr\tElf32_Ehdr\n#  define Elf_Phdr\tElf32_Phdr\n#  define Elf_auxv_t\tElf32_auxv_t\n#else\n#  error \"ELFCLASS is not defined\"\n#endif\n\n#endif /* Z_ELF_H */\n\n"
  },
  {
    "path": "src/z_err.c",
    "content": "#include \"z_syscalls.h\"\n#include \"z_utils.h\"\n\nvoid z_errx(int eval, const char *fmt, ...)\n{\n\tva_list ap;\n\tz_fdprintf(2, \"error: \");\n\tva_start(ap, fmt);\n\tz_vfdprintf(2, fmt, ap);\n\tva_end(ap);\n\tz_fdprintf(2, \"\\n\");\n\tz_exit(eval);\n}\n\n"
  },
  {
    "path": "src/z_printf.c",
    "content": "#include <sys/types.h>\n#include <stdarg.h>\n\n#include \"z_syscalls.h\"\n\nstatic int lastfd = -1;\n#define OUTBUFSIZE 128\nstatic char outbuf[OUTBUFSIZE];\nstatic char *outptr;\n\nstatic void kprintn(int, unsigned long, int);\nstatic void kdoprnt(int, const char *, va_list);\nstatic void z_flushbuf(void);\n\nstatic void putcharfd(int, int );\n\nstatic void\nputcharfd(int c, int fd)\n{\n\tchar b = c;\n\tint len;\n\n\tif (fd != lastfd) {\n\t\tz_flushbuf();\n\t\tlastfd = fd;\n\t}\n\t*outptr++ = b;\n\tlen = outptr - outbuf;\n\tif ((len >= OUTBUFSIZE) || (b == '\\n') || (b == '\\r')) {\n\t\tz_flushbuf();\n\t}\n}\n\nstatic void\nz_flushbuf()\n{\n\tint len = outptr - outbuf;\n\tif (len != 0) {\n\t\tif (lastfd != -1)\n\t\t\tz_write(lastfd, outbuf, len);\n\t\toutptr = outbuf;\n\t}\n}\n\nvoid\nz_printf(const char *fmt, ...)\n{\n\tva_list ap;\n\n\tva_start(ap, fmt);\n\tkdoprnt(2, fmt, ap);\n\tva_end(ap);\n}\n\nvoid\nz_vprintf(const char *fmt, va_list ap)\n{\n\tkdoprnt(2, fmt, ap);\n}\n\nvoid\nz_fdprintf(int fd, const char *fmt, ...)\n{\n\tva_list ap;\n\n\tva_start(ap, fmt);\n\tkdoprnt(fd, fmt, ap);\n\tva_end(ap);\n}\n\nvoid\nz_vfdprintf(int fd, const char *fmt, va_list ap)\n{\n\tkdoprnt(fd, fmt, ap);\n}\n\nstatic void\nkdoprnt(int fd, const char *fmt, va_list ap)\n{\n\tunsigned long ul;\n\tint lflag, ch;\n\tchar *p;\n\tstatic int init;\n\n\tif (!init) {\n\t\toutptr = outbuf;\n\t\tinit = 1;\n\t}\n\n\tfor (;;) {\n\t\twhile ((ch = *fmt++) != '%') {\n\t\t\tif (ch == '\\0')\n\t\t\t\treturn;\n\t\t\tputcharfd(ch, fd);\n\t\t}\n\t\tlflag = 0;\nreswitch:\n\t\tswitch (ch = *fmt++) {\n\t\tcase 'l':\n\t\t\tlflag = 1;\n\t\t\tgoto reswitch;\n\t\tcase 'c':\n\t\t\tch = va_arg(ap, int);\n\t\t\tputcharfd(ch & 0x7f, fd);\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tp = va_arg(ap, char *);\n\t\t\twhile ((ch = *p++))\n\t\t\t\tputcharfd(ch, fd);\n\t\t\tbreak;\n\t\tcase 'd':\n\t\t\tul = lflag ? va_arg(ap, long) : va_arg(ap, int);\n\t\t\tif ((long)ul < 0) {\n\t\t\t\tputcharfd('-', fd);\n\t\t\t\tul = -(long)ul;\n\t\t\t}\n\t\t\tkprintn(fd, ul, 10);\n\t\t\tbreak;\n\t\tcase 'o':\n\t\t\tul = lflag ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int);\n\t\t\tkprintn(fd, ul, 8);\n\t\t\tbreak;\n\t\tcase 'u':\n\t\t\tul = lflag ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int);\n\t\t\tkprintn(fd, ul, 10);\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tputcharfd('0', fd);\n\t\t\tputcharfd('x', fd);\n\t\t\tlflag += sizeof(void *)==sizeof(unsigned long)? 1 : 0;\n\t\t\t/* FALLTHRU */\n\t\tcase 'x':\n\t\t\tul = lflag ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int);\n\t\t\tkprintn(fd, ul, 16);\n\t\t\tbreak;\n\t\tcase 'X':\n\t\t{\n\t\t\tint l;\n\n\t\t\tul = lflag ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int);\n\t\t\tif (lflag)\n\t\t\t\tl = (sizeof(unsigned long) * 8) - 4;\n\t\t\telse\n\t\t\t\tl = (sizeof(unsigned int) * 8) - 4;\n\t\t\twhile (l >= 0) {\n\t\t\t\tputcharfd(\"0123456789abcdef\"[(ul >> l) & 0xf], fd);\n\t\t\t\tl -= 4;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tdefault:\n\t\t\tputcharfd('%', fd);\n\t\t\tif (lflag)\n\t\t\t\tputcharfd('l', fd);\n\t\t\tputcharfd(ch, fd);\n\t\t}\n\t}\n\tz_flushbuf();\n}\n\nstatic void\nkprintn(int fd, unsigned long ul, int base)\n{\n\t/* hold a long in base 8 */\n\tchar *p, buf[(sizeof(long) * 8 / 3) + 1];\n\n\tp = buf;\n\tdo {\n\t\t*p++ = \"0123456789abcdef\"[ul % base];\n\t} while (ul /= base);\n\tdo {\n\t\tputcharfd(*--p, fd);\n\t} while (p > buf);\n}\n"
  },
  {
    "path": "src/z_syscalls.c",
    "content": "#include <syscall.h>\n\n#include \"z_asm.h\"\n#include \"z_syscalls.h\"\n\nstatic int errno;\n\nint *z_perrno(void)\n{\n\treturn &errno;\n}\n\nstatic long check_error(long rc)\n{\n\tif (rc < 0 && rc > -4096) {\n\t\terrno = -rc;\n\t\trc = -1;\n\t}\n\treturn rc;\n}\n\n#define SYSCALL(name, ...)  check_error(z_syscall(SYS_##name, __VA_ARGS__))\n#define DEF_SYSCALL1(ret, name, t1, a1) \\\nret z_##name(t1 a1) \\\n{ \\\n\treturn (ret)SYSCALL(name, a1); \\\n}\n#define DEF_SYSCALL2(ret, name, t1, a1, t2, a2) \\\nret z_##name(t1 a1, t2 a2) \\\n{ \\\n\treturn (ret)SYSCALL(name, a1, a2); \\\n}\n#define DEF_SYSCALL3(ret, name, t1, a1, t2, a2, t3, a3) \\\nret z_##name(t1 a1, t2 a2, t3 a3) \\\n{ \\\n\treturn (ret)SYSCALL(name, a1, a2, a3); \\\n}\n\nDEF_SYSCALL3(int, openat, int, dirfd, const char *, filename, int, flags)\nDEF_SYSCALL3(ssize_t, read, int, fd, void *, buf, size_t, count)\nDEF_SYSCALL3(ssize_t, write, int, fd, const void *, buf, size_t, count)\nDEF_SYSCALL1(int, close, int, fd)\nDEF_SYSCALL3(int, lseek, int, fd, off_t, off, int, whence)\nDEF_SYSCALL1(int, exit, int, status)\nDEF_SYSCALL2(int, munmap, void *, addr, size_t, length)\nDEF_SYSCALL3(int, mprotect, void *, addr, size_t, length, int, prot)\n\nint z_open(const char * filename, int flags)\n{\n\treturn z_openat(AT_FDCWD, filename, flags);\n}\n\nvoid *\nz_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)\n{\n\t/* i386 has map (old_mmap) and mmap2, old_map is a legacy single arg\n\t * function, use mmap2 but it needs offset in page units.\n\t * In same time mmap2 does not exist on x86-64.\n\t */\n#ifdef SYS_mmap2\n\treturn (void *)SYSCALL(mmap2, addr, length, prot, flags, fd, offset >> 12);\n#else\n\treturn (void *)SYSCALL(mmap, addr, length, prot, flags, fd, offset);\n#endif\n}\n\n"
  },
  {
    "path": "src/z_syscalls.h",
    "content": "#ifndef Z_SYSCALLS_H\n#define Z_SYSCALLS_H\n\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/mman.h>\n\n#include <fcntl.h>\n#include <unistd.h>\n\n#define z_errno\t(*z_perrno())\n\nint\tz_exit(int status);\nint\tz_open(const char *pathname, int flags);\nint\tz_openat(int dirfd, const char *pathname, int flags);\nint\tz_close(int fd);\nint\tz_lseek(int fd, off_t offset, int whence);\nssize_t\tz_read(int fd, void *buf, size_t count);\nssize_t\tz_write(int fd, const void *buf, size_t count);\nvoid\t*z_mmap(void *addr, size_t length, int prot,\n\t\tint flags, int fd, off_t offset);\nint\tz_munmap(void *addr, size_t length);\nint\tz_mprotect(void *addr, size_t length, int prot);\nint\t*z_perrno(void);\n\n#endif /* Z_SYSCALLS_H */\n"
  },
  {
    "path": "src/z_utils.c",
    "content": "#include <stdlib.h>\n\nvoid *z_memset(void *s, int c, size_t n)\n{\n\tunsigned char *p = s, *e = p + n;\n\twhile (p < e)\n\t\t*p++ = c;\n\treturn s;\n}\n\nvoid *z_memcpy(void *dest, const void *src, size_t n)\n{\n\tunsigned char *d = dest;\n\tconst unsigned char *p = src, *e = p + n;\n\twhile (p < e)\n\t\t*d++ = *p++;\n\treturn dest;\n}\n\n"
  },
  {
    "path": "src/z_utils.h",
    "content": "#ifndef Z_UTILS_H\n#define Z_UTILS_H\n\n#include <stdlib.h>\n#include <stdarg.h>\n#include <alloca.h>\n#include <string.h>\n\n#define z_alloca\t__builtin_alloca\n\nvoid\t*z_memset(void *s, int c, size_t n);\nvoid\t*z_memcpy(void *dest, const void *src, size_t n);\n\nvoid\tz_vprintf(const char *fmt, va_list ap);\nvoid\tz_vfdprintf(int fd, const char *fmt, va_list ap);\nvoid\tz_printf(const char *fmt, ...)\n\t__attribute__ ((format (printf, 1, 2)));\nvoid\tz_fdprintf(int fd, const char *fmt, ...)\n\t__attribute__ ((format (printf, 2, 3)));\nvoid\tz_errx(int eval, const char *fmt, ...)\n\t__attribute__ ((format (printf, 2, 3)));\n\n#ifdef Z_SMALL\n#  define z_errx(eval, fmt, ...) z_exit(eval)\n#  define z_printf(fmt, ...) do {} while(0)\n#  define z_fdprintf(fd, fmt, ...) do {} while(0)\n#endif\n\n#endif /* Z_UTILS_H */\n\n"
  }
]