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

@ -31,4 +31,6 @@
* @param path is NUL-terminated UTF-8 path
* @return pointer inside path or path itself
*/
char *basename(const char *path) { return basename_n(path, strlen(path)); }
textstartup char *basename(const char *path) {
return basename_n(path, strlen(path));
}

View file

@ -31,7 +31,7 @@
* @param size is byte length of path
* @return pointer inside path or path itself
*/
char *basename_n(const char *path, size_t size) {
textstartup char *basename_n(const char *path, size_t size) {
size_t i, l;
if (size) {
if (isslash(path[size - 1])) {

View file

@ -1,5 +1,8 @@
#ifndef COSMOPOLITAN_LIBC_CONV_CONV_H_
#define COSMOPOLITAN_LIBC_CONV_CONV_H_
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/nt/struct/filetime.h"
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § conversion
@ -23,6 +26,7 @@ long labs(long) libcesque pureconst;
long long llabs(long long) libcesque pureconst;
char *ltpcpy(char *, long) paramsnonnull() libcesque nocallback;
int llog10(unsigned long) libcesque pureconst;
int unsleb128(const void *, size_t, int64_t *);
int atoi(const char *) paramsnonnull() libcesque nosideeffect;
long atol(const char *) paramsnonnull() libcesque nosideeffect;
long long atoll(const char *) paramsnonnull() libcesque nosideeffect;
@ -38,18 +42,12 @@ long wcstol(const wchar_t *, wchar_t **, int);
long strtol(const char *, char **, int)
paramsnonnull((1)) libcesque nosideeffect;
intmax_t __imaxabs(intmax_t) asm("imaxabs") libcesque pureconst;
#define imaxabs(x) __imaxabs(x)
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § conversion » time
*/
struct NtFileTime;
struct timespec;
struct timeval;
void filetimetotimespec(struct timespec *, struct NtFileTime) paramsnonnull();
struct timespec filetimetotimespec(struct NtFileTime);
struct NtFileTime timespectofiletime(struct timespec);
struct NtFileTime timetofiletime(int64_t) nothrow pureconst;
int64_t filetimetotime(struct NtFileTime) nothrow pureconst;
void filetimetotimeval(struct timeval *, struct NtFileTime) nothrow;
@ -104,6 +102,11 @@ double RoundDecimalPlaces(double, double, double(double));
#define lldiv(num, den) ((lldiv_t){(num) / (den), (num) % (den)})
#endif
#ifndef __STRICT_ANSI__
intmax_t __imaxabs(intmax_t) asm("imaxabs") libcesque pureconst;
#define imaxabs(x) __imaxabs(x)
#endif /* !ANSI */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CONV_CONV_H_ */

View file

@ -38,7 +38,8 @@ LIBC_CONV_A_DIRECTDEPS = \
LIBC_STUBS \
LIBC_NEXGEN32E \
LIBC_TINYMATH \
LIBC_SYSV
LIBC_SYSV \
THIRD_PARTY_COMPILER_RT
LIBC_CONV_A_DEPS := \
$(call uniq,$(foreach x,$(LIBC_CONV_A_DIRECTDEPS),$($(x))))
@ -51,12 +52,10 @@ $(LIBC_CONV_A).pkg: \
$(LIBC_CONV_A_OBJS) \
$(foreach x,$(LIBC_CONV_A_DIRECTDEPS),$($(x)_A).pkg)
#o/$(MODE)/libc/conv/strtoimax.o: CC = clang-10
#o/$(MODE)/libc/conv/strtoumax.o: CC = clang-10
o/$(MODE)/libc/conv/itoa64radix10.o \
o/$(MODE)/libc/conv/itoa64radix10.greg.o \
o/$(MODE)/libc/conv/timetofiletime.o \
o/$(MODE)/libc/conv/filetimetotime.o \
o/$(MODE)/libc/conv/timespectofiletime.o \
o/$(MODE)/libc/conv/filetimetotimespec.o \
o/$(MODE)/libc/conv/filetimetotimeval.o: \
OVERRIDE_COPTS += \

View file

@ -24,9 +24,11 @@
/**
* Converts Windows COBOL timestamp to UNIX epoch in nanoseconds.
*/
void filetimetotimespec(struct timespec *ts, struct NtFileTime ft) {
uint64_t t = (uint64_t)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
uint64_t x = t - MODERNITYSECONDS * HECTONANOSECONDS;
ts->tv_sec = x / HECTONANOSECONDS;
ts->tv_nsec = x % HECTONANOSECONDS * 100;
struct timespec filetimetotimespec(struct NtFileTime ft) {
uint64_t x;
x = ft.dwHighDateTime;
x <<= 32;
x |= ft.dwLowDateTime;
x -= MODERNITYSECONDS;
return (struct timespec){x / HECTONANOSECONDS, x % HECTONANOSECONDS * 100};
}

View file

@ -23,13 +23,17 @@ COSMOPOLITAN_C_START_
*/
size_t int128toarray_radix10(int128_t, char *);
size_t uint128toarray_radix10(uint128_t, char *);
size_t int64toarray_radix10(int64_t, char *);
size_t uint64toarray_radix10(uint64_t, char *);
size_t int64toarray(int64_t, char *, int);
size_t uint64toarray(uint64_t, char *, int);
size_t uint64toarray_radix16(uint64_t, char[hasatleast 17]);
size_t uint64toarray_fixed16(uint64_t, char[hasatleast 17], uint8_t);
#ifndef __STRICT_ANSI__
size_t int128toarray_radix10(int128_t, char *);
size_t uint128toarray_radix10(uint128_t, char *);
#endif
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -20,13 +20,21 @@
#include "libc/alg/reverse.h"
#include "libc/conv/conv.h"
#include "libc/conv/itoa.h"
#include "libc/limits.h"
uint128_t __udivmodti4(uint128_t, uint128_t, uint128_t *);
/**
* Converts unsigned 128-bit integer to string.
* @param a needs at least 40 bytes
* @return bytes written w/o nul
*/
noinline size_t uint128toarray_radix10(uint128_t i, char *a) {
size_t j;
unsigned rem;
uint128_t rem;
j = 0;
do {
i = div10(i, &rem);
i = __udivmodti4(i, 10, &rem);
a[j++] = rem + '0';
} while (i > 0);
a[j] = '\0';
@ -34,10 +42,21 @@ noinline size_t uint128toarray_radix10(uint128_t i, char *a) {
return j;
}
/**
* Converts signed 128-bit integer to string.
* @param a needs at least 41 bytes
* @return bytes written w/o nul
*/
size_t int128toarray_radix10(int128_t i, char *a) {
if (i < 0) {
*a++ = '-';
i = -i;
if (i != INT128_MIN) {
*a++ = '-';
return 1 + uint128toarray_radix10(-i, a);
} else {
memcpy(a, "-170141183460469231731687303715884105728", 41);
return 40;
}
} else {
return uint128toarray_radix10(i, a);
}
return uint128toarray_radix10(i, a);
}

View file

@ -0,0 +1,38 @@
/*-*- 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/alg/reverse.h"
#include "libc/assert.h"
#include "libc/conv/itoa.h"
size_t uint64toarray_fixed16(uint64_t i, char a[hasatleast 17], uint8_t b) {
size_t j;
assert(b <= 64);
assert(b % 4 == 0);
j = 0;
if (b) {
do {
a[j++] = "0123456789abcdef"[i & 15];
i >>= 4;
} while (b -= 4);
}
a[j] = '\0';
reverse(a, j);
return j;
}

View file

@ -20,7 +20,13 @@
#include "libc/alg/reverse.h"
#include "libc/conv/conv.h"
#include "libc/conv/itoa.h"
#include "libc/limits.h"
/**
* Converts unsigned 64-bit integer to string.
* @param a needs at least 21 bytes
* @return bytes written w/o nul
*/
noinline size_t uint64toarray_radix10(uint64_t i, char *a) {
size_t j;
j = 0;
@ -33,10 +39,21 @@ noinline size_t uint64toarray_radix10(uint64_t i, char *a) {
return j;
}
/**
* Converts signed 64-bit integer to string.
* @param a needs at least 21 bytes
* @return bytes written w/o nul
*/
size_t int64toarray_radix10(int64_t i, char *a) {
if (i < 0) {
*a++ = '-';
i = -i;
if (i != INT64_MIN) {
*a++ = '-';
return 1 + uint64toarray_radix10(-i, a);
} else {
memcpy(a, "-9223372036854775808", 21);
return 20;
}
} else {
return uint64toarray_radix10(i, a);
}
return uint64toarray_radix10(i, a);
}

View file

@ -22,11 +22,9 @@
size_t uint64toarray_radix16(uint64_t i, char a[hasatleast 17]) {
size_t j;
unsigned char d;
j = 0;
do {
d = i % 16;
a[j++] = d < 10 ? d + '0' : d + 'a';
a[j++] = "0123456789abcdef"[i % 16];
i /= 16;
} while (i > 0);
a[j] = '\0';

View file

@ -76,6 +76,10 @@ intmax_t strtoimax(const char *s, char **endptr, int base) {
} else {
base = 10;
}
} else if (*s == '0') {
++s;
if (base == 2 && *s == 'b' && *s == 'B') ++s;
if (base == 16 && *s == 'x' && *s == 'X') ++s;
}
for (;;) {

View file

@ -28,7 +28,10 @@
*/
uintmax_t strtoumax(const char *s, char **endptr, int base) {
const unsigned char *p = (const unsigned char *)s;
uintmax_t res = 0;
unsigned diglet;
uintmax_t res;
res = 0;
while (isspace(*p)) {
p++;
@ -49,10 +52,14 @@ uintmax_t strtoumax(const char *s, char **endptr, int base) {
} else {
base = 10;
}
} else if (*s == '0') {
++s;
if (base == 2 && *s == 'b' && *s == 'B') ++s;
if (base == 16 && *s == 'x' && *s == 'X') ++s;
}
for (;;) {
unsigned diglet = kBase36[*p];
diglet = kBase36[*p];
if (!diglet || diglet > base) break;
p++;
res *= base;

View file

@ -0,0 +1,34 @@
/*-*- 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/conv/conv.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/nt/struct/filetime.h"
/**
* Converts UNIX nanosecond timestamp to Windows COBOL timestamp.
*/
struct NtFileTime timespectofiletime(struct timespec ts) {
uint64_t x;
x = MODERNITYSECONDS;
x += ts.tv_sec * HECTONANOSECONDS;
x += div100int64(ts.tv_nsec);
return (struct NtFileTime){x, x >> 32};
}

45
libc/conv/unsleb128.c Normal file
View file

@ -0,0 +1,45 @@
/*-*- 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/conv/conv.h"
/**
* Decodes a GNU-style varint from a buffer.
*
* The GNU Assembler is able to encode numbers this way, since it's used
* by the DWARF debug format.
*/
int unsleb128(const void *buf, size_t size, int64_t *out) {
int b;
int64_t r, w;
unsigned char c;
const unsigned char *p, *pe;
pe = (p = buf) + size;
r = b = 0;
do {
if (size && p == pe) return -1;
c = *p++;
w = c & 0x7f;
r |= w << b;
b += 7;
} while (c & 0x80);
if (c & 0x40) r |= -1ull << b;
if (out) *out = r;
return p - (const unsigned char *)buf;
}