Add x86_64-linux-gnu emulator

I wanted a tiny scriptable meltdown proof way to run userspace programs
and visualize how program execution impacts memory. It helps to explain
how things like Actually Portable Executable works. It can show you how
the GCC generated code is going about manipulating matrices and more. I
didn't feel fully comfortable with Qemu and Bochs because I'm not smart
enough to understand them. I wanted something like gVisor but with much
stronger levels of assurances. I wanted a single binary that'll run, on
all major operating systems with an embedded GPL barrier ZIP filesystem
that is tiny enough to transpile to JavaScript and run in browsers too.

https://justine.storage.googleapis.com/emulator625.mp4
This commit is contained in:
Justine Tunney 2020-08-25 04:23:25 -07:00
parent 467504308a
commit f4f4caab0e
1052 changed files with 65667 additions and 7825 deletions

View file

@ -25,15 +25,18 @@
#include "libc/zip.h"
#include "libc/zipos/zipos.h"
ssize_t __zipos_find(const struct ZiposUri *name) {
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
size_t i, cf;
assert(ZIP_CDIR_MAGIC(__zip_end) == kZipCdirHdrMagic);
for (i = 0, cf = ZIP_CDIR_OFFSET(__zip_end); i < ZIP_CDIR_RECORDS(__zip_end);
++i, cf += ZIP_CFILE_HDRSIZE(&_base[0] + cf)) {
assert(ZIP_CFILE_MAGIC(&_base[0] + cf) == kZipCfileHdrMagic);
if (name->len == ZIP_CFILE_NAMESIZE(&_base[0] + cf) &&
memcmp(name->path, ZIP_CFILE_NAME(&_base[0] + cf), name->len) == 0) {
return cf;
if ((zipos = __zipos_get())) {
assert(ZIP_CDIR_MAGIC(zipos->cdir) == kZipCdirHdrMagic);
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
i < ZIP_CDIR_RECORDS(zipos->cdir);
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
assert(ZIP_CFILE_MAGIC(zipos->map + cf) == kZipCfileHdrMagic);
if (name->len == ZIP_CFILE_NAMESIZE(zipos->map + cf) &&
memcmp(name->path, ZIP_CFILE_NAME(zipos->map + cf), name->len) == 0) {
return cf;
}
}
}
return -1;

View file

@ -26,5 +26,5 @@
* @asyncsignalsafe
*/
int __zipos_fstat(const struct ZiposHandle *h, struct stat *st) {
return __zipos_stat_impl(h->cfile, st);
return __zipos_stat_impl(__zipos_get(), h->cfile, st);
}

65
libc/zipos/get.c Normal file
View file

@ -0,0 +1,65 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/limits.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/zip.h"
#include "libc/zipos/zipos.h"
/**
* Returns pointer to zip central directory of current executable.
*/
struct Zipos *__zipos_get(void) {
static struct Zipos zipos;
static bool once;
const char *exe;
size_t mapsize;
uint8_t *cdir;
void *map;
int fd;
if (!once) {
if (ZIP_CDIR_MAGIC(__zip_end) == kZipCdirHdrMagic) {
zipos.map = _base;
zipos.cdir = __zip_end;
} else {
exe = (const char *)getauxval(AT_EXECFN);
if ((fd = open(exe, O_RDONLY)) != -1) {
if ((mapsize = getfiledescriptorsize(fd)) != SIZE_MAX &&
(map = mmap(NULL, mapsize, PROT_READ, MAP_SHARED, fd, 0)) !=
MAP_FAILED) {
if ((cdir = zipfindcentraldir(map, mapsize))) {
zipos.map = map;
zipos.cdir = cdir;
} else {
munmap(map, mapsize);
}
}
close(fd);
}
}
once = true;
}
return zipos.cdir ? &zipos : NULL;
}

View file

@ -23,10 +23,10 @@
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/stat.h"
#include "libc/conv/sizemultiply.h"
#include "libc/dce.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/crc32.h"
#include "libc/runtime/rbx.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
@ -83,37 +83,39 @@ static int __zipos_inflate_tiny(struct ZiposHandle *h, uint8_t *data,
return undeflate(h->mem, h->size, data, size, &ds) != -1 ? 0 : eio();
}
static int __zipos_load(size_t cf, unsigned flags, int mode) {
static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
int mode) {
int fd;
size_t lf;
struct ZiposHandle *h;
lf = ZIP_CFILE_OFFSET(&_base[0] + cf);
assert(ZIP_LFILE_MAGIC(&_base[0] + lf) == kZipLfileHdrMagic);
assert(ZIP_LFILE_COMPRESSIONMETHOD(&_base[0] + lf) == kZipCompressionNone ||
ZIP_LFILE_COMPRESSIONMETHOD(&_base[0] + lf) == kZipCompressionDeflate);
lf = ZIP_CFILE_OFFSET(zipos->map + cf);
assert(ZIP_LFILE_MAGIC(zipos->map + lf) == kZipLfileHdrMagic);
assert(ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) == kZipCompressionNone ||
ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) ==
kZipCompressionDeflate);
if ((fd = createfd()) == -1) return -1;
if (!(h = calloc(1, sizeof(*h)))) return -1;
h->cfile = cf;
if ((h->size = ZIP_LFILE_UNCOMPRESSEDSIZE(&_base[0] + lf))) {
if (ZIP_LFILE_COMPRESSIONMETHOD(&_base[0] + lf)) {
assert(ZIP_LFILE_COMPRESSEDSIZE(&_base[0] + lf));
if ((h->size = ZIP_LFILE_UNCOMPRESSEDSIZE(zipos->map + lf))) {
if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
assert(ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf));
h->mapsize = ROUNDUP(h->size + FRAMESIZE, FRAMESIZE);
if ((h->map = mapanon(h->mapsize)) != MAP_FAILED) {
h->mem = h->map + FRAMESIZE / 2;
if ((IsTiny() ? __zipos_inflate_tiny : __zipos_inflate_fast)(
h, ZIP_LFILE_CONTENT(&_base[0] + lf),
ZIP_LFILE_COMPRESSEDSIZE(&_base[0] + lf)) == -1) {
h, ZIP_LFILE_CONTENT(zipos->map + lf),
ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)) == -1) {
fd = -1;
}
} else {
fd = -1;
}
} else {
h->mem = ZIP_LFILE_CONTENT(&_base[0] + lf);
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
}
}
if (!IsTiny() && fd != -1 &&
crc32_z(0, h->mem, h->size) != ZIP_LFILE_CRC32(&_base[0] + lf)) {
crc32_z(0, h->mem, h->size) != ZIP_LFILE_CRC32(zipos->map + lf)) {
fd = eio();
}
if (fd != -1) {
@ -136,12 +138,17 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
int fd;
ssize_t cf;
sigset_t oldmask;
struct Zipos *zipos;
assert((flags & O_ACCMODE) == O_RDONLY);
sigprocmask(SIG_BLOCK, &kSigsetFull, &oldmask);
if ((cf = __zipos_find(name)) != -1) {
fd = __zipos_load(cf, flags, mode);
if ((zipos = __zipos_get())) {
if ((cf = __zipos_find(zipos, name)) != -1) {
fd = __zipos_load(zipos, cf, flags, mode);
} else {
fd = enoent();
}
} else {
fd = enoent();
fd = enoexec();
}
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return fd;

View file

@ -22,17 +22,23 @@
#include "libc/calls/struct/stat.h"
#include "libc/runtime/rbx.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#include "libc/zip.h"
#include "libc/zipos/zipos.h"
int __zipos_stat_impl(size_t cf, struct stat *st) {
memset(st, 0, sizeof(*st));
if (ZIP_CFILE_FILEATTRCOMPAT(&_base[0] + cf) == kZipOsUnix) {
st->st_mode = ZIP_CFILE_EXTERNALATTRIBUTES(&_base[0] + cf) >> 16;
int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) {
if (zipos && st) {
memset(st, 0, sizeof(*st));
if (ZIP_CFILE_FILEATTRCOMPAT(zipos->map + cf) == kZipOsUnix) {
st->st_mode = ZIP_CFILE_EXTERNALATTRIBUTES(zipos->map + cf) >> 16;
} else {
st->st_mode = 0100644;
}
st->st_size = ZIP_CFILE_UNCOMPRESSEDSIZE(zipos->map + cf);
st->st_blocks =
roundup(ZIP_CFILE_COMPRESSEDSIZE(zipos->map + cf), 512) / 512;
return 0;
} else {
st->st_mode = 0100644;
return einval();
}
st->st_size = ZIP_CFILE_UNCOMPRESSEDSIZE(&_base[0] + cf);
st->st_blocks = roundup(ZIP_CFILE_COMPRESSEDSIZE(&_base[0] + cf), 512) / 512;
return 0;
}

View file

@ -28,9 +28,14 @@
*/
int __zipos_stat(const struct ZiposUri *name, struct stat *st) {
ssize_t cf;
if ((cf = __zipos_find(name)) != -1) {
return __zipos_stat_impl(cf, st);
struct Zipos *zipos;
if ((zipos = __zipos_get())) {
if ((cf = __zipos_find(zipos, name)) != -1) {
return __zipos_stat_impl(zipos, cf, st);
} else {
return enoent();
}
} else {
return enoent();
return enoexec();
}
}

View file

@ -17,42 +17,13 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "ape/relocations.h"
#include "libc/zip.h"
#include "libc/macros.h"
/ ZIP Central Directory.
.section .piro.data.sort.zip.3,"a",@progbits
.hidden __zip_start
.globl __zip_start
.type __zip_start,@object
.align kZipCdirAlign
__zip_start:
.previous/*
...
decentralized content
...
*/.section .piro.data.sort.zip.5,"a",@progbits
.align kZipCdirAlign
__zip_end:
.long kZipCdirHdrMagic # magic
.short 0 # disk
.short 0 # starting disk
.short v_zip_records # records on disk
.short v_zip_records # records
.long v_zip_cdirsize # size of central directory
.long RVA(__zip_start) # central directory offset
.short v_zip_commentsize # comment size
.endobj __zip_end,globl,hidden
.weak v_zip_records
.weak v_zip_cdirsize
.weak v_zip_commentsize
.previous
.yoink __zip_start
.yoink __zip_end
.yoink __zipos_close
.yoink __zipos_fstat
.yoink __zipos_open
.yoink __zipos_parseuri
.yoink __zipos_read
.yoink __zipos_stat
.source __FILE__

View file

@ -6,6 +6,11 @@ COSMOPOLITAN_C_START_
struct stat;
struct iovec;
struct Zipos {
uint8_t *map;
uint8_t *cdir;
};
struct ZiposUri {
const char *path;
size_t len;
@ -22,13 +27,14 @@ struct ZiposHandle {
extern const char kZiposSchemePrefix[4];
struct Zipos *__zipos_get(void);
ssize_t __zipos_parseuri(const char *, struct ZiposUri *);
ssize_t __zipos_find(const struct ZiposUri *);
ssize_t __zipos_find(struct Zipos *, const struct ZiposUri *);
int __zipos_close(struct ZiposHandle *);
int __zipos_open(const struct ZiposUri *, unsigned, int);
int __zipos_stat(const struct ZiposUri *, struct stat *);
int __zipos_fstat(const struct ZiposHandle *, struct stat *);
int __zipos_stat_impl(size_t, struct stat *);
int __zipos_stat_impl(struct Zipos *, size_t, struct stat *);
ssize_t __zipos_read(struct ZiposHandle *, const struct iovec *, size_t,
ssize_t);
ssize_t __zipos_write(struct ZiposHandle *, const struct iovec *, size_t,

View file

@ -4,13 +4,17 @@
PKGS += LIBC_ZIPOS
LIBC_ZIPOS_ARTIFACTS += LIBC_ZIPOS_A
LIBC_ZIPOS = $(LIBC_ZIPOS_A_DEPS) $(LIBC_ZIPOS_A)
LIBC_ZIPOS_A = o/$(MODE)/libc/zipos/zipos.a
LIBC_ZIPOS_A_FILES := $(wildcard libc/zipos/*)
LIBC_ZIPOS_A_HDRS = $(filter %.h,$(LIBC_ZIPOS_A_FILES))
LIBC_ZIPOS_A_SRCS_S = $(filter %.S,$(LIBC_ZIPOS_A_FILES))
LIBC_ZIPOS_A_SRCS_C = $(filter %.c,$(LIBC_ZIPOS_A_FILES))
LIBC_ZIPOS = \
$(LIBC_ZIPOS_A_DEPS) \
$(LIBC_ZIPOS_A) \
o/$(MODE)/libc/zipos/zipos.o
LIBC_ZIPOS_A_SRCS = \
$(LIBC_ZIPOS_A_SRCS_S) \
$(LIBC_ZIPOS_A_SRCS_C)