#define CTRL_FD 3
#define READY_FD 4
#define GO_FD 5
#define UFFD_SLOT 6
#define SYS_read 0
#define SYS_write 1
#define SYS_mmap 9
#define SYS_ioctl 16
#define SYS_dup2 33
#define SYS_exit 60
#define SYS_rt_sigreturn 15
#define SYS_userfaultfd 323
#define SYS_lseek 8
#define SYS_fstat 5
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define PROT_EXEC 0x4
#define MAP_PRIVATE 0x2
#define MAP_ANONYMOUS 0x20
#define MAP_FIXED 0x10
#define O_CLOEXEC 02000000
#define O_NONBLOCK 04000
#define UFFD_USER_MODE_ONLY 1
#define SEEK_END 2
#define SEEK_SET 0
typedef unsigned long u64;
typedef unsigned int u32;
typedef long i64;
static i64 sc6(long n, u64 a, u64 b, u64 c, u64 d, u64 e, u64 f) {
i64 r;
register u64 r10 __asm__("r10") = d;
register u64 r8 __asm__("r8") = e;
register u64 r9 __asm__("r9") = f;
__asm__ volatile("syscall" : "=a"(r)
: "a"(n), "D"(a), "S"(b), "d"(c), "r"(r10), "r"(r8), "r"(r9)
: "rcx", "r11", "memory");
return r;
}
#define SC0(n) sc6(n,0,0,0,0,0,0)
#define SC1(n,a) sc6(n,(u64)(a),0,0,0,0,0)
#define SC2(n,a,b) sc6(n,(u64)(a),(u64)(b),0,0,0,0)
#define SC3(n,a,b,c) sc6(n,(u64)(a),(u64)(b),(u64)(c),0,0,0)
#define SC6(n,a,b,c,d,e,f) sc6(n,(u64)(a),(u64)(b),(u64)(c),(u64)(d),(u64)(e),(u64)(f))
static void die(int code) { SC1(SYS_exit, code); for(;;){} }
#define UFFDIO_API 0xc018aa3fUL
#define UFFDIO_REGISTER 0xc020aa00UL
#define UFFD_API 0xAAUL
#define UFFDIO_REGISTER_MODE_MISSING 1UL
struct uffdio_api { u64 api, features, ioctls; };
struct uffdio_register { u64 start, len, mode, ioctls; };
struct blob_header {
u32 magic, version, n_regions, n_fds;
u64 regs_off; u32 regs_len, _pad; u64 anon_data_off;
};
struct blob_region {
u64 start, end; u32 prot; unsigned char src, _p0[3]; u64 file_off, data_off;
};
#define BLOB_MAGIC 0x534c5242u
#define BLOB_VERSION 1u
#define NO_DATA 0xFFFFFFFFFFFFFFFFUL
enum { R8=0,R9,R10,R11,R12,R13,R14,R15,RDI,RSI,RBP,RBX,RDX,RAX,RCX,RSP,RIP,
EFL,CSGSFS,ERR,TRAPNO,OLDMASK,CR2 };
struct sigctx { u64 gregs[23]; u64 fpstate; u64 reserved[8]; };
struct uctx {
u64 uc_flags;
u64 uc_link;
u64 ss_sp; u32 ss_flags; u32 _pad; u64 ss_size;
struct sigctx mc;
u64 uc_sigmask[16];
};
enum { UR_R15=0,UR_R14,UR_R13,UR_R12,UR_RBP,UR_RBX,UR_R11,UR_R10,UR_R9,UR_R8,
UR_RAX,UR_RCX,UR_RDX,UR_RSI,UR_RDI,UR_ORIG_RAX,UR_RIP,UR_CS,UR_EFLAGS,
UR_RSP,UR_SS,UR_FS_BASE,UR_GS_BASE,UR_DS,UR_ES,UR_FS,UR_GS };
__attribute__((used, noinline))
static void _start_c(void) {
i64 sz = SC3(SYS_lseek, CTRL_FD, 0, SEEK_END);
if (sz <= 0) die(2);
SC3(SYS_lseek, CTRL_FD, 0, SEEK_SET);
void *blob = (void*)SC6(SYS_mmap, 0, sz, PROT_READ, MAP_PRIVATE, CTRL_FD, 0);
if ((i64)blob < 0) die(2);
struct blob_header *h = (struct blob_header*)blob;
if (h->magic != BLOB_MAGIC || h->version != BLOB_VERSION) die(3);
struct blob_region *regs_tbl =
(struct blob_region*)((char*)blob + sizeof(struct blob_header));
unsigned char *anon = (unsigned char*)blob + h->anon_data_off;
u64 *gp = (u64*)((char*)blob + h->regs_off);
for (u32 i = 0; i < h->n_regions; i++) {
struct blob_region *r = ®s_tbl[i];
u64 len = r->end - r->start;
i64 p = SC6(SYS_mmap, r->start, len, r->prot,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
if ((u64)p != r->start) die(4);
}
i64 uffd = SC1(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd < 0) uffd = SC1(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY);
if (uffd < 0) die(5);
struct uffdio_api api = { UFFD_API, 0, 0 };
if (SC3(SYS_ioctl, uffd, UFFDIO_API, &api) < 0) die(6);
for (u32 i = 0; i < h->n_regions; i++) {
struct blob_region *r = ®s_tbl[i];
if (r->data_off == NO_DATA) continue;
struct uffdio_register reg = {
r->start, r->end - r->start, UFFDIO_REGISTER_MODE_MISSING, 0 };
if (SC3(SYS_ioctl, uffd, UFFDIO_REGISTER, ®) < 0) die(7);
}
(void)anon;
if (SC2(SYS_dup2, uffd, UFFD_SLOT) != UFFD_SLOT) die(8);
u64 one = 1;
if (SC3(SYS_write, READY_FD, &one, 8) != 8) die(9);
u64 got = 0;
if (SC3(SYS_read, GO_FD, &got, 8) != 8) die(10);
struct uctx uc;
for (unsigned k = 0; k < sizeof(uc); k++) ((char*)&uc)[k] = 0;
struct sigctx *m = &uc.mc;
m->gregs[R8] = gp[UR_R8]; m->gregs[R9] = gp[UR_R9];
m->gregs[R10] = gp[UR_R10]; m->gregs[R11] = gp[UR_R11];
m->gregs[R12] = gp[UR_R12]; m->gregs[R13] = gp[UR_R13];
m->gregs[R14] = gp[UR_R14]; m->gregs[R15] = gp[UR_R15];
m->gregs[RDI] = gp[UR_RDI]; m->gregs[RSI] = gp[UR_RSI];
m->gregs[RBP] = gp[UR_RBP]; m->gregs[RBX] = gp[UR_RBX];
m->gregs[RDX] = gp[UR_RDX]; m->gregs[RAX] = gp[UR_RAX];
m->gregs[RCX] = gp[UR_RCX]; m->gregs[RSP] = gp[UR_RSP];
m->gregs[RIP] = gp[UR_RIP]; m->gregs[EFL] = gp[UR_EFLAGS];
m->gregs[CSGSFS] = (gp[UR_CS] & 0xffff)
| ((gp[UR_GS] & 0xffff) << 16)
| ((gp[UR_FS] & 0xffff) << 32)
| ((gp[UR_SS] & 0xffff) << 48);
m->fpstate = 0;
register u64 rax __asm__("rax") = SYS_rt_sigreturn;
__asm__ volatile(
"mov %0, %%rsp\n\t"
"syscall\n\t"
:
: "r"(&uc), "r"(rax)
: "memory");
die(11);
}
__asm__(
".global _start\n"
"_start:\n"
" xor %rbp, %rbp\n"
" and $-16, %rsp\n"
" call _start_c\n"
" hlt\n"
);