mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-27 14:58:30 +00:00
Add minor improvements and cleanup
This commit is contained in:
parent
9e3e985ae5
commit
feed0d2b0e
163 changed files with 2286 additions and 2245 deletions
|
@ -37,15 +37,6 @@ const aluop_f kBsu[8][4] = {
|
|||
{Shl8, Shl16, Shl32, Shl64}, {Sar8, Sar16, Sar32, Sar64},
|
||||
};
|
||||
|
||||
int64_t AluFlags(uint64_t x, uint32_t af, uint32_t *f, uint32_t of, uint32_t cf,
|
||||
uint32_t sf) {
|
||||
*f &= ~(1u << FLAGS_CF | 1u << FLAGS_ZF | 1u << FLAGS_SF | 1u << FLAGS_OF |
|
||||
1u << FLAGS_AF | 0xFF000000u);
|
||||
*f |= sf << FLAGS_SF | cf << FLAGS_CF | !x << FLAGS_ZF | of << FLAGS_OF |
|
||||
af << FLAGS_AF | (x & 0xFF) << 24;
|
||||
return x;
|
||||
}
|
||||
|
||||
int64_t AluFlags8(uint8_t z, uint32_t af, uint32_t *f, uint32_t of,
|
||||
uint32_t cf) {
|
||||
return AluFlags(z, af, f, of, cf, z >> 7);
|
||||
|
|
|
@ -108,7 +108,6 @@ int64_t Rcl64(uint64_t, uint64_t, uint32_t *);
|
|||
|
||||
uint64_t BsuDoubleShift(int, uint64_t, uint64_t, uint8_t, bool, uint32_t *);
|
||||
|
||||
int64_t AluFlags(uint64_t, uint32_t, uint32_t *, uint32_t, uint32_t, uint32_t);
|
||||
int64_t AluFlags8(uint8_t, uint32_t, uint32_t *, uint32_t, uint32_t);
|
||||
int64_t AluFlags16(uint16_t, uint32_t, uint32_t *, uint32_t, uint32_t);
|
||||
int64_t AluFlags32(uint32_t, uint32_t, uint32_t *, uint32_t, uint32_t);
|
||||
|
|
|
@ -49,16 +49,17 @@ void AppendWide(struct Buffer *b, wint_t wc) {
|
|||
} while (wb);
|
||||
}
|
||||
|
||||
void AppendFmt(struct Buffer *b, const char *fmt, ...) {
|
||||
int size;
|
||||
int AppendFmt(struct Buffer *b, const char *fmt, ...) {
|
||||
int bytes;
|
||||
char *tmp;
|
||||
va_list va;
|
||||
tmp = NULL;
|
||||
va_start(va, fmt);
|
||||
size = vasprintf(&tmp, fmt, va);
|
||||
bytes = vasprintf(&tmp, fmt, va);
|
||||
va_end(va);
|
||||
if (size != -1) AppendData(b, tmp, size);
|
||||
if (bytes != -1) AppendData(b, tmp, bytes);
|
||||
free(tmp);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@ void AppendChar(struct Buffer *, char);
|
|||
void AppendData(struct Buffer *, char *, size_t);
|
||||
void AppendStr(struct Buffer *, const char *);
|
||||
void AppendWide(struct Buffer *, wint_t);
|
||||
void AppendFmt(struct Buffer *, const char *, ...);
|
||||
int AppendFmt(struct Buffer *, const char *, ...);
|
||||
ssize_t WriteBuffer(struct Buffer *, int);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -68,6 +68,10 @@ $(TOOL_BUILD_LIB_A_OBJS): \
|
|||
-fsanitize=address
|
||||
endif
|
||||
|
||||
o/$(MODE)/tool/build/lib/memory-gcc.asm: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-fsanitize=address
|
||||
|
||||
o/$(MODE)/tool/build/lib/ssefloat.o: \
|
||||
TARGET_ARCH += \
|
||||
-msse3
|
||||
|
|
|
@ -31,8 +31,8 @@ void OpCpuid(struct Machine *m, uint32_t rde) {
|
|||
case 0x80000000:
|
||||
ax = 7;
|
||||
bx = 'G' | 'e' << 8 | 'n' << 16 | 'u' << 24;
|
||||
cx = 'i' | 'n' << 8 | 'e' << 16 | 'C' << 24;
|
||||
dx = 'o' | 's' << 8 | 'm' << 16 | 'o' << 24;
|
||||
dx = 'i' | 'n' << 8 | 'e' << 16 | 'C' << 24;
|
||||
cx = 'o' | 's' << 8 | 'm' << 16 | 'o' << 24;
|
||||
break;
|
||||
case 1:
|
||||
cx |= 1 << 0; // sse3
|
||||
|
|
|
@ -89,7 +89,7 @@ static char *DisError(struct Dis *d, char *p) {
|
|||
p = HighStart(p, g_high.comment);
|
||||
*p++ = '#';
|
||||
*p++ = ' ';
|
||||
p = stpcpy(p, indexdoublenulstring(kXedErrorNames, d->xedd->op.error));
|
||||
p = stpcpy(p, IndexDoubleNulString(kXedErrorNames, d->xedd->op.error));
|
||||
p = HighEnd(p);
|
||||
*p = '\0';
|
||||
return p;
|
||||
|
|
|
@ -30,10 +30,10 @@ struct Dis {
|
|||
size_t i, n;
|
||||
struct DisSym {
|
||||
int64_t addr;
|
||||
int rank;
|
||||
int unique;
|
||||
int size;
|
||||
int name;
|
||||
char rank;
|
||||
bool iscode;
|
||||
bool isabs;
|
||||
} * p;
|
||||
|
|
|
@ -156,17 +156,12 @@ static char *DisSymImpl(struct Dis *d, char *p, int64_t x, long sym) {
|
|||
return p;
|
||||
}
|
||||
|
||||
static char *DisSym(struct Dis *d, char *p, int64_t x1, int64_t x2,
|
||||
bool isrelative) {
|
||||
static char *DisSym(struct Dis *d, char *p, int64_t value, int64_t addr) {
|
||||
long sym;
|
||||
if ((sym = DisFindSym(d, x2)) != -1 && d->syms.p[sym].name &&
|
||||
(d->syms.p[sym].isabs ^ isrelative)) {
|
||||
return DisSymImpl(d, p, x2, sym);
|
||||
} else if ((sym = DisFindSym(d, x1)) != -1 && d->syms.p[sym].name &&
|
||||
(d->syms.p[sym].isabs ^ isrelative)) {
|
||||
return DisSymImpl(d, p, x1, sym);
|
||||
if ((sym = DisFindSym(d, addr)) != -1 && d->syms.p[sym].name) {
|
||||
return DisSymImpl(d, p, addr, sym);
|
||||
} else {
|
||||
return DisInt(p, x1);
|
||||
return DisInt(p, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +169,7 @@ static char *DisSymLiteral(struct Dis *d, uint32_t rde, char *p, uint64_t addr,
|
|||
uint64_t ip) {
|
||||
*p++ = '$';
|
||||
p = HighStart(p, g_high.literal);
|
||||
p = DisSym(d, p, addr, addr, false);
|
||||
p = DisSym(d, p, addr, addr);
|
||||
p = HighEnd(p);
|
||||
return p;
|
||||
}
|
||||
|
@ -227,7 +222,7 @@ static char *DisDisp(struct Dis *d, uint32_t rde, char *p) {
|
|||
} else {
|
||||
rela = true;
|
||||
}
|
||||
p = DisSym(d, p, disp, disp, rela);
|
||||
p = DisSym(d, p, disp, disp);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
@ -477,11 +472,11 @@ static char *DisJb(struct Dis *d, uint32_t rde, char *p) {
|
|||
|
||||
static char *DisJvds(struct Dis *d, uint32_t rde, char *p) {
|
||||
return DisSym(d, p, RipRelative(d, d->xedd->op.disp),
|
||||
RipRelative(d, d->xedd->op.disp) - Read64(d->m->cs), true);
|
||||
RipRelative(d, d->xedd->op.disp) - Read64(d->m->cs));
|
||||
}
|
||||
|
||||
static char *DisAbs(struct Dis *d, uint32_t rde, char *p) {
|
||||
return DisSym(d, p, d->xedd->op.disp, d->xedd->op.disp, false);
|
||||
return DisSym(d, p, d->xedd->op.disp, d->xedd->op.disp);
|
||||
}
|
||||
|
||||
static char *DisSw(struct Dis *d, uint32_t rde, char *p) {
|
||||
|
|
|
@ -18,12 +18,12 @@
|
|||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/alg/alg.h"
|
||||
#include "libc/alg/arraylist2.h"
|
||||
#include "libc/elf/elf.h"
|
||||
#include "libc/elf/struct/sym.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "tool/build/lib/dis.h"
|
||||
|
||||
|
@ -46,38 +46,51 @@ static int DisSymCompare(const struct DisSym *a, const struct DisSym *b) {
|
|||
}
|
||||
|
||||
static void DisLoadElfLoads(struct Dis *d, struct Elf *elf) {
|
||||
long i;
|
||||
long i, j, n;
|
||||
int64_t addr;
|
||||
uint64_t size;
|
||||
Elf64_Phdr *phdr;
|
||||
struct DisLoad l;
|
||||
d->loads.i = 0;
|
||||
for (i = 0; i < elf->ehdr->e_phnum; ++i) {
|
||||
j = 0;
|
||||
n = elf->ehdr->e_phnum;
|
||||
if (d->loads.n < n) {
|
||||
d->loads.n = n;
|
||||
d->loads.p = realloc(d->loads.p, d->loads.n * sizeof(*d->loads.p));
|
||||
CHECK_NOTNULL(d->loads.p);
|
||||
}
|
||||
for (i = 0; i < n; ++i) {
|
||||
phdr = getelfsegmentheaderaddress(elf->ehdr, elf->size, i);
|
||||
if (phdr->p_type != PT_LOAD) continue;
|
||||
l.addr = phdr->p_vaddr;
|
||||
l.size = phdr->p_memsz;
|
||||
l.istext = (phdr->p_flags & PF_X) == PF_X;
|
||||
APPEND(&d->loads.p, &d->loads.i, &d->loads.n, &l);
|
||||
d->loads.p[j].addr = phdr->p_vaddr;
|
||||
d->loads.p[j].size = phdr->p_memsz;
|
||||
d->loads.p[j].istext = (phdr->p_flags & PF_X) == PF_X;
|
||||
++j;
|
||||
}
|
||||
d->loads.i = j;
|
||||
}
|
||||
|
||||
static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
|
||||
size_t i, n;
|
||||
size_t i, j, n;
|
||||
int64_t stablen;
|
||||
struct DisSym t;
|
||||
const Elf64_Sym *st, *sym;
|
||||
bool isabs, iscode, isweak, islocal, ishidden, isprotected, isfunc, isobject;
|
||||
d->syms.i = 0;
|
||||
j = 0;
|
||||
if ((d->syms.stab = getelfstringtable(elf->ehdr, elf->size)) &&
|
||||
(st = getelfsymboltable(elf->ehdr, elf->size, &n))) {
|
||||
stablen = (intptr_t)elf->ehdr + elf->size - (intptr_t)d->syms.stab;
|
||||
if (d->syms.n < n) {
|
||||
d->syms.n = n;
|
||||
d->syms.p = realloc(d->syms.p, d->syms.n * sizeof(*d->syms.p));
|
||||
CHECK_NOTNULL(d->syms.p);
|
||||
}
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (!st[i].st_name) continue;
|
||||
if (!(0 <= st[i].st_name && st[i].st_name < stablen)) continue;
|
||||
if (ELF64_ST_TYPE(st[i].st_info) == STT_SECTION) continue;
|
||||
if (ELF64_ST_TYPE(st[i].st_info) == STT_FILE) continue;
|
||||
if (startswith(d->syms.stab + st[i].st_name, "v_")) continue;
|
||||
if (ELF64_ST_TYPE(st[i].st_info) == STT_SECTION ||
|
||||
ELF64_ST_TYPE(st[i].st_info) == STT_FILE || !st[i].st_name ||
|
||||
startswith(d->syms.stab + st[i].st_name, "v_") ||
|
||||
!(0 <= st[i].st_name && st[i].st_name < stablen) || !st[i].st_value ||
|
||||
!(-0x800000000000 <= (int64_t)st[i].st_value &&
|
||||
(int64_t)st[i].st_value < 0x800000000000)) {
|
||||
continue;
|
||||
}
|
||||
isabs = st[i].st_shndx == SHN_ABS;
|
||||
isweak = ELF64_ST_BIND(st[i].st_info) == STB_WEAK;
|
||||
islocal = ELF64_ST_BIND(st[i].st_info) == STB_LOCAL;
|
||||
|
@ -85,19 +98,51 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
|
|||
isprotected = st[i].st_other == STV_PROTECTED;
|
||||
isfunc = ELF64_ST_TYPE(st[i].st_info) == STT_FUNC;
|
||||
isobject = ELF64_ST_TYPE(st[i].st_info) == STT_OBJECT;
|
||||
t.unique = i;
|
||||
t.size = st[i].st_size;
|
||||
t.name = st[i].st_name;
|
||||
t.addr = st[i].st_value;
|
||||
t.rank = -islocal + -isweak + -isabs + isprotected + isobject + isfunc;
|
||||
t.iscode = DisIsText(d, st[i].st_value) ? !isobject : isfunc;
|
||||
t.isabs = isabs;
|
||||
APPEND(&d->syms.p, &d->syms.i, &d->syms.n, &t);
|
||||
d->syms.p[j].unique = i;
|
||||
d->syms.p[j].size = st[i].st_size;
|
||||
d->syms.p[j].name = st[i].st_name;
|
||||
d->syms.p[j].addr = st[i].st_value;
|
||||
d->syms.p[j].rank =
|
||||
-islocal + -isweak + -isabs + isprotected + isobject + isfunc;
|
||||
d->syms.p[j].iscode = DisIsText(d, st[i].st_value) ? !isobject : isfunc;
|
||||
d->syms.p[j].isabs = isabs;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
d->syms.i = j;
|
||||
}
|
||||
|
||||
static void DisSortSyms(struct Dis *d) {
|
||||
qsort(d->syms.p, d->syms.i, sizeof(struct DisSym), (void *)DisSymCompare);
|
||||
}
|
||||
|
||||
static void DisCanonizeSyms(struct Dis *d) {
|
||||
int64_t i, j, a;
|
||||
if (d->syms.i) {
|
||||
i = 1;
|
||||
j = 1;
|
||||
a = d->syms.p[0].addr;
|
||||
do {
|
||||
if (d->syms.p[j].addr > a) {
|
||||
a = d->syms.p[j].addr;
|
||||
if (j > i) {
|
||||
d->syms.p[i] = d->syms.p[j];
|
||||
}
|
||||
++i;
|
||||
}
|
||||
++j;
|
||||
} while (j < d->syms.i);
|
||||
d->syms.p = realloc(d->syms.p, sizeof(*d->syms.p) * i);
|
||||
d->syms.i = i;
|
||||
d->syms.n = i;
|
||||
}
|
||||
for (i = 0; i < d->syms.i; ++i) {
|
||||
DEBUGF("%p-%p %s", d->syms.p[i].addr,
|
||||
d->syms.p[i].addr + (d->syms.p[i].size ? d->syms.p[i].size - 1 : 0),
|
||||
d->syms.stab + d->syms.p[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
bool DisIsProg(struct Dis *d, int64_t addr) {
|
||||
long i;
|
||||
if (g_disisprog_disable) return true;
|
||||
|
@ -122,36 +167,24 @@ bool DisIsText(struct Dis *d, int64_t addr) {
|
|||
}
|
||||
|
||||
long DisFindSym(struct Dis *d, int64_t addr) {
|
||||
size_t i, l, r, m, n;
|
||||
if (d->syms.p) {
|
||||
if (DisIsProg(d, addr)) {
|
||||
l = 0;
|
||||
r = d->syms.i;
|
||||
while (l < r) {
|
||||
m = (l + r) >> 1;
|
||||
if (d->syms.p[m].addr < addr) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
r = m;
|
||||
}
|
||||
}
|
||||
if (d->syms.p[l].addr == addr) {
|
||||
return l;
|
||||
}
|
||||
l = MAX(0, (long)l - 10);
|
||||
for (n = 0, i = l; i < d->syms.i && n < 20; ++i, ++n) {
|
||||
if (addr >= d->syms.p[i].addr &&
|
||||
addr < d->syms.p[i].addr + d->syms.p[i].size) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for (n = 0, i = l; i < d->syms.i && n < 20; ++i, ++n) {
|
||||
if (addr >= d->syms.p[i].addr &&
|
||||
(i + 1 == d->syms.i || addr < d->syms.p[i + 1].addr)) {
|
||||
return i;
|
||||
}
|
||||
long l, r, m, n;
|
||||
if (DisIsProg(d, addr)) {
|
||||
l = 0;
|
||||
r = d->syms.i;
|
||||
while (l < r) {
|
||||
m = (l + r) >> 1;
|
||||
if (d->syms.p[m].addr > addr) {
|
||||
r = m;
|
||||
} else {
|
||||
l = m + 1;
|
||||
}
|
||||
}
|
||||
if (r && (addr == d->syms.p[r - 1].addr ||
|
||||
(addr > d->syms.p[r - 1].addr &&
|
||||
(addr <= d->syms.p[r - 1].addr + d->syms.p[r - 1].size ||
|
||||
!d->syms.p[r - 1].size)))) {
|
||||
return r - 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -170,4 +203,6 @@ void DisLoadElf(struct Dis *d, struct Elf *elf) {
|
|||
if (!elf || !elf->ehdr) return;
|
||||
DisLoadElfLoads(d, elf);
|
||||
DisLoadElfSyms(d, elf);
|
||||
DisSortSyms(d);
|
||||
DisCanonizeSyms(d);
|
||||
}
|
||||
|
|
|
@ -53,3 +53,12 @@ uint64_t ExportFlags(uint64_t flags) {
|
|||
flags |= GetLazyParityBool(flags) << FLAGS_PF;
|
||||
return flags;
|
||||
}
|
||||
|
||||
int64_t AluFlags(uint64_t x, uint32_t af, uint32_t *f, uint32_t of, uint32_t cf,
|
||||
uint32_t sf) {
|
||||
*f &= ~(1u << FLAGS_CF | 1u << FLAGS_ZF | 1u << FLAGS_SF | 1u << FLAGS_OF |
|
||||
1u << FLAGS_AF | 0xFF000000u);
|
||||
*f |= sf << FLAGS_SF | cf << FLAGS_CF | !x << FLAGS_ZF | of << FLAGS_OF |
|
||||
af << FLAGS_AF | (x & 0xFF) << 24;
|
||||
return x;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ COSMOPOLITAN_C_START_
|
|||
bool GetParity(uint8_t);
|
||||
uint64_t ExportFlags(uint64_t);
|
||||
void ImportFlags(struct Machine *, uint64_t);
|
||||
int64_t AluFlags(uint64_t, uint32_t, uint32_t *, uint32_t, uint32_t, uint32_t);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -46,9 +46,6 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize,
|
|||
align = MAX(phdr->p_align, PAGESIZE);
|
||||
CHECK_EQ(1, popcnt(align));
|
||||
CHECK_EQ(0, (phdr->p_vaddr - phdr->p_offset) % align);
|
||||
/*-Type-Offset---VirtAddr-----------PhysAddr-----------FileSiz--MemSiz---Flg-Align----*/
|
||||
/*-LOAD-0x000000-0x0000000000400000-0x0000000000400000-0x0008e4-0x0008e4-R-E-0x200000-*/
|
||||
/*-LOAD-0x000fe0-0x0000000000600fe0-0x0000000000600fe0-0x000030-0x000310-RW--0x200000-*/
|
||||
felf = (int64_t)(intptr_t)code;
|
||||
vstart = ROUNDDOWN(phdr->p_vaddr, align);
|
||||
vbss = ROUNDUP(phdr->p_vaddr + phdr->p_filesz, align);
|
||||
|
@ -56,10 +53,10 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize,
|
|||
fstart = felf + ROUNDDOWN(phdr->p_offset, align);
|
||||
fend = felf + phdr->p_offset + phdr->p_filesz;
|
||||
bsssize = vend - vbss;
|
||||
LOGF("LOADELFLOADSEGMENT"
|
||||
" VSTART %#lx VBSS %#lx VEND %#lx"
|
||||
" FSTART %#lx FEND %#lx BSSSIZE %#lx",
|
||||
vstart, vbss, vend, fstart, fend, bsssize);
|
||||
VERBOSEF("LOADELFLOADSEGMENT"
|
||||
" VSTART %#lx VBSS %#lx VEND %#lx"
|
||||
" FSTART %#lx FEND %#lx BSSSIZE %#lx",
|
||||
vstart, vbss, vend, fstart, fend, bsssize);
|
||||
m->brk = MAX(m->brk, vend);
|
||||
CHECK_GE(vend, vstart);
|
||||
CHECK_GE(fend, fstart);
|
||||
|
@ -69,9 +66,9 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize,
|
|||
CHECK_GE(vend - vstart, fstart - fend);
|
||||
CHECK_LE(phdr->p_filesz, phdr->p_memsz);
|
||||
CHECK_EQ(felf + phdr->p_offset - fstart, phdr->p_vaddr - vstart);
|
||||
CHECK_NE(-1, ReserveVirtual(m, vstart, fend - fstart));
|
||||
CHECK_NE(-1, ReserveVirtual(m, vstart, fend - fstart, 0x0207));
|
||||
VirtualRecv(m, vstart, (void *)fstart, fend - fstart);
|
||||
if (bsssize) CHECK_NE(-1, ReserveVirtual(m, vbss, bsssize));
|
||||
if (bsssize) CHECK_NE(-1, ReserveVirtual(m, vbss, bsssize, 0x0207));
|
||||
if (phdr->p_memsz - phdr->p_filesz > bsssize) {
|
||||
VirtualSet(m, phdr->p_vaddr + phdr->p_filesz, 0,
|
||||
phdr->p_memsz - phdr->p_filesz - bsssize);
|
||||
|
@ -82,7 +79,7 @@ static void LoadElf(struct Machine *m, struct Elf *elf) {
|
|||
unsigned i;
|
||||
Elf64_Phdr *phdr;
|
||||
m->ip = elf->base = elf->ehdr->e_entry;
|
||||
LOGF("LOADELF ENTRY %p", m->ip);
|
||||
VERBOSEF("LOADELF ENTRY %p", m->ip);
|
||||
for (i = 0; i < elf->ehdr->e_phnum; ++i) {
|
||||
phdr = getelfsegmentheaderaddress(elf->ehdr, elf->size, i);
|
||||
switch (phdr->p_type) {
|
||||
|
@ -149,9 +146,9 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars,
|
|||
}
|
||||
CHECK_NE(-1, close(fd));
|
||||
ResetCpu(m);
|
||||
if (m->mode == XED_MACHINE_MODE_REAL) {
|
||||
if ((m->mode & 3) == XED_MODE_REAL) {
|
||||
elf->base = 0x7c00;
|
||||
CHECK_NE(-1, ReserveVirtual(m, 0, BIGPAGESIZE));
|
||||
CHECK_NE(-1, ReserveReal(m, BIGPAGESIZE));
|
||||
m->ip = 0x7c00;
|
||||
Write64(m->cs, 0);
|
||||
Write64(m->dx, 0);
|
||||
|
@ -168,7 +165,8 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars,
|
|||
} else {
|
||||
sp = 0x800000000000;
|
||||
Write64(m->sp, sp);
|
||||
CHECK_NE(-1, ReserveVirtual(m, sp - STACKSIZE, STACKSIZE));
|
||||
m->cr3 = AllocateLinearPage(m);
|
||||
CHECK_NE(-1, ReserveVirtual(m, sp - STACKSIZE, STACKSIZE, 0x0207));
|
||||
LoadArgv(m, prog, args, vars);
|
||||
if (memcmp(elf->map, "\177ELF", 4) == 0) {
|
||||
elf->ehdr = (void *)elf->map;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "tool/build/lib/machine.h"
|
||||
#include "tool/build/lib/memory.h"
|
||||
#include "tool/build/lib/modrm.h"
|
||||
#include "tool/build/lib/op101.h"
|
||||
#include "tool/build/lib/sse.h"
|
||||
#include "tool/build/lib/ssefloat.h"
|
||||
#include "tool/build/lib/ssemov.h"
|
||||
|
@ -883,9 +884,6 @@ static void OpBsubiImm(struct Machine *m, uint32_t rde) {
|
|||
Bsubi(m, rde, m->xedd->op.uimm0);
|
||||
}
|
||||
|
||||
static void OpLgdtMs(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void OpPushImm(struct Machine *m, uint32_t rde) {
|
||||
Push(m, rde, m->xedd->op.uimm0);
|
||||
}
|
||||
|
@ -1328,21 +1326,6 @@ static void Op0ff(struct Machine *m, uint32_t rde) {
|
|||
kOp0ff[ModrmReg(rde)](m, rde);
|
||||
}
|
||||
|
||||
static void Op101(struct Machine *m, uint32_t rde) {
|
||||
if (IsModrmRegister(rde)) {
|
||||
if (ModrmReg(rde) == 0b111 && ModrmRm(rde) == 0b001) {
|
||||
OpRdtscp(m, rde);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (ModrmReg(rde) == 2) {
|
||||
OpLgdtMs(m, rde);
|
||||
return;
|
||||
}
|
||||
}
|
||||
OpUd(m, rde);
|
||||
}
|
||||
|
||||
static void OpDoubleShift(struct Machine *m, uint32_t rde) {
|
||||
uint8_t *p;
|
||||
uint64_t x;
|
||||
|
@ -1400,6 +1383,44 @@ static void OpNop(struct Machine *m, uint32_t rde) {
|
|||
}
|
||||
}
|
||||
|
||||
static void OpMovRqCq(struct Machine *m, uint32_t rde) {
|
||||
switch (ModrmReg(rde)) {
|
||||
case 0:
|
||||
Write64(RegRexbRm(m, rde), m->cr0);
|
||||
break;
|
||||
case 2:
|
||||
Write64(RegRexbRm(m, rde), m->cr2);
|
||||
break;
|
||||
case 3:
|
||||
Write64(RegRexbRm(m, rde), m->cr3);
|
||||
break;
|
||||
case 4:
|
||||
Write64(RegRexbRm(m, rde), m->cr4);
|
||||
break;
|
||||
default:
|
||||
OpUd(m, rde);
|
||||
}
|
||||
}
|
||||
|
||||
static void OpMovCqRq(struct Machine *m, uint32_t rde) {
|
||||
switch (ModrmReg(rde)) {
|
||||
case 0:
|
||||
m->cr0 = Read64(RegRexbRm(m, rde));
|
||||
break;
|
||||
case 2:
|
||||
m->cr2 = Read64(RegRexbRm(m, rde));
|
||||
break;
|
||||
case 3:
|
||||
m->cr3 = Read64(RegRexbRm(m, rde));
|
||||
break;
|
||||
case 4:
|
||||
m->cr4 = Read64(RegRexbRm(m, rde));
|
||||
break;
|
||||
default:
|
||||
OpUd(m, rde);
|
||||
}
|
||||
}
|
||||
|
||||
static const nexgen32e_f kNexgen32e[] = {
|
||||
[0x000] = OpAlubAdd,
|
||||
[0x001] = OpAluw,
|
||||
|
@ -1689,9 +1710,9 @@ static const nexgen32e_f kNexgen32e[] = {
|
|||
[0x11D] = OpHintNopEv,
|
||||
[0x11E] = OpUd,
|
||||
[0x11F] = OpNopEv,
|
||||
[0x120] = OpUd,
|
||||
[0x120] = OpMovRqCq,
|
||||
[0x121] = OpUd,
|
||||
[0x122] = OpUd,
|
||||
[0x122] = OpMovCqRq,
|
||||
[0x123] = OpUd,
|
||||
[0x124] = OpUd,
|
||||
[0x125] = OpUd,
|
||||
|
|
|
@ -131,6 +131,9 @@ struct Machine {
|
|||
};
|
||||
};
|
||||
} sse;
|
||||
uint64_t cr0;
|
||||
uint64_t cr2;
|
||||
uint64_t cr4;
|
||||
struct MachineRealFree {
|
||||
uint64_t i;
|
||||
uint64_t n;
|
||||
|
@ -140,6 +143,15 @@ struct Machine {
|
|||
uint32_t i;
|
||||
void *p[6];
|
||||
} freelist;
|
||||
struct MachineMemstat {
|
||||
int freed;
|
||||
int resizes;
|
||||
int reserved;
|
||||
int committed;
|
||||
int allocated;
|
||||
int reclaimed;
|
||||
int pagetables;
|
||||
} memstat;
|
||||
int64_t brk;
|
||||
int64_t bofram[2];
|
||||
jmp_buf onhalt;
|
||||
|
@ -157,7 +169,9 @@ void ResetTlb(struct Machine *);
|
|||
void LoadInstruction(struct Machine *);
|
||||
void ExecuteInstruction(struct Machine *);
|
||||
long AllocateLinearPage(struct Machine *);
|
||||
int ReserveVirtual(struct Machine *, int64_t, size_t);
|
||||
long AllocateLinearPageRaw(struct Machine *);
|
||||
int ReserveReal(struct Machine *, size_t);
|
||||
int ReserveVirtual(struct Machine *, int64_t, size_t, uint64_t);
|
||||
char *FormatPml4t(struct Machine *) nodiscard;
|
||||
int64_t FindVirtual(struct Machine *, int64_t, size_t);
|
||||
int FreeVirtual(struct Machine *, int64_t, size_t);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
|
@ -44,41 +45,58 @@ void SetWriteAddr(struct Machine *m, int64_t addr, uint32_t size) {
|
|||
}
|
||||
}
|
||||
|
||||
long HandlePageFault(struct Machine *m, uint64_t entry, uint64_t table,
|
||||
unsigned index) {
|
||||
long page;
|
||||
if ((page = AllocateLinearPage(m)) != -1) {
|
||||
--m->memstat.reserved;
|
||||
*(uint64_t *)(m->real.p + table + index * 8) =
|
||||
page | entry & ~0x7ffffffffe00;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
void *FindReal(struct Machine *m, int64_t virt) {
|
||||
uint8_t *host;
|
||||
uint64_t real, pte, *pt;
|
||||
unsigned skew, level, i;
|
||||
if (!(-0x800000000000 <= virt && virt < 0x800000000000)) {
|
||||
long page;
|
||||
uint64_t table, entry;
|
||||
unsigned skew, level, index, i;
|
||||
if ((m->mode & 3) != XED_MODE_REAL) {
|
||||
if (-0x800000000000 <= virt && virt < 0x800000000000) {
|
||||
skew = virt & 0xfff;
|
||||
virt &= -0x1000;
|
||||
for (i = 0; i < ARRAYLEN(m->tlb); ++i) {
|
||||
if (m->tlb[i].virt == virt && m->tlb[i].host) {
|
||||
return m->tlb[i].host + skew;
|
||||
}
|
||||
}
|
||||
level = 39;
|
||||
entry = m->cr3;
|
||||
do {
|
||||
table = entry & 0x7ffffffff000;
|
||||
CHECK_LT(table, m->real.n);
|
||||
index = (virt >> level) & 511;
|
||||
entry = *(uint64_t *)(m->real.p + table + index * 8);
|
||||
if (!(entry & 1)) return NULL;
|
||||
} while ((level -= 9) >= 12);
|
||||
if (!(entry & 0x0e00)) {
|
||||
page = entry & 0x7ffffffff000;
|
||||
CHECK_LT(page, m->real.n);
|
||||
} else if ((page = HandlePageFault(m, entry, table, index)) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
m->tlbindex = (m->tlbindex + 1) & (ARRAYLEN(m->tlb) - 1);
|
||||
m->tlb[m->tlbindex] = m->tlb[0];
|
||||
m->tlb[0].virt = virt;
|
||||
m->tlb[0].host = m->real.p + page;
|
||||
return m->real.p + page + skew;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
} else if (0 <= virt && virt + 0xfff < m->real.n) {
|
||||
return m->real.p + virt;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
skew = virt & 0xfff;
|
||||
virt &= -0x1000;
|
||||
for (i = 0; i < ARRAYLEN(m->tlb); ++i) {
|
||||
if (m->tlb[i].virt == virt && m->tlb[i].host) {
|
||||
return m->tlb[i].host + skew;
|
||||
}
|
||||
}
|
||||
level = 39;
|
||||
real = m->cr3;
|
||||
for (;;) {
|
||||
if (real + 0x1000 > m->real.n) {
|
||||
return NULL;
|
||||
}
|
||||
host = m->real.p + real;
|
||||
if (level < 12) break;
|
||||
pt = (uint64_t *)host;
|
||||
pte = pt[(virt >> level) & 511];
|
||||
if (!(pte & 1)) {
|
||||
return NULL;
|
||||
}
|
||||
real = pte & 0x00007ffffffff000;
|
||||
level -= 9;
|
||||
}
|
||||
m->tlbindex = (m->tlbindex + 1) & (ARRAYLEN(m->tlb) - 1);
|
||||
m->tlb[m->tlbindex] = m->tlb[0];
|
||||
m->tlb[0].host = host;
|
||||
m->tlb[0].virt = virt;
|
||||
return host + skew;
|
||||
}
|
||||
|
||||
void *ResolveAddress(struct Machine *m, int64_t v) {
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
struct Machine *NewMachine(void) {
|
||||
struct Machine *m;
|
||||
m = xmemalignzero(alignof(struct Machine), sizeof(struct Machine));
|
||||
m->mode = XED_MACHINE_MODE_LONG_64;
|
||||
ResetCpu(m);
|
||||
ResetMem(m);
|
||||
return m;
|
||||
|
@ -60,11 +59,20 @@ void FreeMachine(struct Machine *m) {
|
|||
void ResetMem(struct Machine *m) {
|
||||
FreeMachineRealFree(m);
|
||||
ResetTlb(m);
|
||||
memset(&m->memstat, 0, sizeof(m->memstat));
|
||||
m->real.i = 0;
|
||||
m->cr3 = AllocateLinearPage(m);
|
||||
m->cr3 = 0;
|
||||
}
|
||||
|
||||
long AllocateLinearPage(struct Machine *m) {
|
||||
long page;
|
||||
if ((page = AllocateLinearPageRaw(m)) != -1) {
|
||||
memset(m->real.p + page, 0, 0x1000);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
long AllocateLinearPageRaw(struct Machine *m) {
|
||||
uint8_t *p;
|
||||
size_t i, n;
|
||||
struct MachineRealFree *rf;
|
||||
|
@ -79,6 +87,8 @@ long AllocateLinearPage(struct Machine *m) {
|
|||
m->realfree = rf->next;
|
||||
free(rf);
|
||||
}
|
||||
--m->memstat.freed;
|
||||
++m->memstat.reclaimed;
|
||||
} else {
|
||||
i = m->real.i;
|
||||
n = m->real.n;
|
||||
|
@ -94,6 +104,7 @@ long AllocateLinearPage(struct Machine *m) {
|
|||
m->real.p = p;
|
||||
m->real.n = n;
|
||||
ResetTlb(m);
|
||||
++m->memstat.resizes;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
@ -102,8 +113,9 @@ long AllocateLinearPage(struct Machine *m) {
|
|||
DCHECK_EQ(0, n & 0xfff);
|
||||
DCHECK_LE(i + 0x1000, n);
|
||||
m->real.i += 0x1000;
|
||||
++m->memstat.allocated;
|
||||
}
|
||||
memset(m->real.p + i, 0, 0x1000); /* TODO: lazy page clearing */
|
||||
++m->memstat.committed;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
@ -117,24 +129,51 @@ static void MachineWrite64(struct Machine *m, unsigned long i, uint64_t x) {
|
|||
Write64(m->real.p + i, x);
|
||||
}
|
||||
|
||||
int ReserveVirtual(struct Machine *m, int64_t virt, size_t size) {
|
||||
int64_t level, pt, ti, mi, end;
|
||||
for (end = virt + size; virt < end; virt += 0x1000) {
|
||||
for (pt = m->cr3, level = 39; level >= 12; level -= 9) {
|
||||
pt = pt & 0x00007ffffffff000;
|
||||
ti = (virt >> level) & 511;
|
||||
DEBUGF("reserve %p level %d table %p index %ld", virt, level, pt, ti);
|
||||
mi = pt + ti * 8;
|
||||
pt = MachineRead64(m, mi);
|
||||
if (!(pt & 1)) {
|
||||
if ((pt = AllocateLinearPage(m)) == -1) return -1;
|
||||
MachineWrite64(m, mi, pt | 7);
|
||||
}
|
||||
int ReserveReal(struct Machine *m, size_t n) {
|
||||
uint8_t *p;
|
||||
DCHECK_EQ(0, n & 0xfff);
|
||||
if (m->real.n < n) {
|
||||
if ((p = realloc(m->real.p, n))) {
|
||||
m->real.p = p;
|
||||
m->real.n = n;
|
||||
ResetTlb(m);
|
||||
++m->memstat.resizes;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReserveVirtual(struct Machine *m, int64_t virt, size_t size, uint64_t key) {
|
||||
int64_t ti, mi, pt, end, level;
|
||||
for (end = virt + size;;) {
|
||||
for (pt = m->cr3, level = 39; level >= 12; level -= 9) {
|
||||
pt = pt & 0x7ffffffff000;
|
||||
ti = (virt >> level) & 511;
|
||||
mi = (pt & 0x7ffffffff000) + ti * 8;
|
||||
pt = MachineRead64(m, mi);
|
||||
if (level > 12) {
|
||||
if (!(pt & 1)) {
|
||||
if ((pt = AllocateLinearPage(m)) == -1) return -1;
|
||||
MachineWrite64(m, mi, pt | 7);
|
||||
++m->memstat.pagetables;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (;;) {
|
||||
if (!(pt & 1)) {
|
||||
MachineWrite64(m, mi, key);
|
||||
++m->memstat.reserved;
|
||||
}
|
||||
if ((virt += 0x1000) >= end) return 0;
|
||||
if (++ti == 512) break;
|
||||
pt = MachineRead64(m, (mi += 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t FindVirtual(struct Machine *m, int64_t virt, size_t size) {
|
||||
uint64_t i, pt, got;
|
||||
got = 0;
|
||||
|
@ -154,30 +193,39 @@ int64_t FindVirtual(struct Machine *m, int64_t virt, size_t size) {
|
|||
return virt;
|
||||
}
|
||||
|
||||
int FreeVirtual(struct Machine *m, int64_t base, size_t size) {
|
||||
static void AppendRealFree(struct Machine *m, uint64_t real) {
|
||||
struct MachineRealFree *rf;
|
||||
uint64_t i, mi, pt, la, end, virt;
|
||||
for (virt = base, end = virt + size; virt < end;) {
|
||||
if (m->realfree && real == m->realfree->i + m->realfree->n) {
|
||||
m->realfree->n += 0x1000;
|
||||
} else if ((rf = malloc(sizeof(struct MachineRealFree)))) {
|
||||
rf->i = real;
|
||||
rf->n = 0x1000;
|
||||
rf->next = m->realfree;
|
||||
m->realfree = rf;
|
||||
}
|
||||
}
|
||||
|
||||
int FreeVirtual(struct Machine *m, int64_t base, size_t size) {
|
||||
uint64_t i, mi, pt, end, virt;
|
||||
for (virt = base, end = virt + size; virt < end; virt += 1ull << i) {
|
||||
for (pt = m->cr3, i = 39;; i -= 9) {
|
||||
mi = (pt & 0x7ffffffff000) + ((virt >> i) & 511) * 8;
|
||||
pt = MachineRead64(m, mi);
|
||||
if (!(pt & 1)) {
|
||||
break;
|
||||
} else if (i == 12) {
|
||||
MachineWrite64(m, mi, 0);
|
||||
la = pt & 0x7ffffffff000;
|
||||
if (m->realfree && la == m->realfree->i + m->realfree->n) {
|
||||
m->realfree->n += 0x1000;
|
||||
} else if ((rf = malloc(sizeof(struct MachineRealFree)))) {
|
||||
rf->i = la;
|
||||
rf->n = 0x1000;
|
||||
rf->next = m->realfree;
|
||||
m->realfree = rf;
|
||||
++m->memstat.freed;
|
||||
if (pt & 0x0e00) {
|
||||
--m->memstat.reserved;
|
||||
} else {
|
||||
--m->memstat.committed;
|
||||
AppendRealFree(m, pt & 0x7ffffffff000);
|
||||
}
|
||||
MachineWrite64(m, mi, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
virt += 1ull << i;
|
||||
}
|
||||
ResetTlb(m);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -236,6 +236,10 @@ uint8_t *GetModrmRegisterWordPointerWrite(struct Machine *m, uint32_t rde,
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t *GetModrmRegisterWordPointerWrite2(struct Machine *m, uint32_t rde) {
|
||||
return GetModrmRegisterWordPointerWrite(m, rde, 2);
|
||||
}
|
||||
|
||||
uint8_t *GetModrmRegisterWordPointerWrite4(struct Machine *m, uint32_t rde) {
|
||||
return GetModrmRegisterWordPointerWrite(m, rde, 4);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,6 @@ extern const uint8_t kByteReg[32];
|
|||
|
||||
int64_t ComputeAddress(const struct Machine *, uint32_t);
|
||||
struct AddrSeg LoadEffectiveAddress(const struct Machine *, uint32_t);
|
||||
|
||||
void *ComputeReserveAddressRead(struct Machine *, uint32_t, size_t);
|
||||
void *ComputeReserveAddressRead1(struct Machine *, uint32_t);
|
||||
void *ComputeReserveAddressRead4(struct Machine *, uint32_t);
|
||||
|
@ -68,7 +67,6 @@ void *ComputeReserveAddressWrite(struct Machine *, uint32_t, size_t);
|
|||
void *ComputeReserveAddressWrite1(struct Machine *, uint32_t);
|
||||
void *ComputeReserveAddressWrite4(struct Machine *, uint32_t);
|
||||
void *ComputeReserveAddressWrite8(struct Machine *, uint32_t);
|
||||
|
||||
uint8_t *GetModrmRegisterBytePointerRead(struct Machine *, uint32_t);
|
||||
uint8_t *GetModrmRegisterBytePointerWrite(struct Machine *, uint32_t);
|
||||
uint8_t *GetModrmRegisterMmPointerRead(struct Machine *, uint32_t, size_t);
|
||||
|
@ -82,6 +80,7 @@ uint8_t *GetModrmRegisterWordPointerRead8(struct Machine *, uint32_t);
|
|||
uint8_t *GetModrmRegisterWordPointerReadOsz(struct Machine *, uint32_t);
|
||||
uint8_t *GetModrmRegisterWordPointerReadOszRexw(struct Machine *, uint32_t);
|
||||
uint8_t *GetModrmRegisterWordPointerWrite(struct Machine *, uint32_t, size_t);
|
||||
uint8_t *GetModrmRegisterWordPointerWrite2(struct Machine *, uint32_t);
|
||||
uint8_t *GetModrmRegisterWordPointerWrite4(struct Machine *, uint32_t);
|
||||
uint8_t *GetModrmRegisterWordPointerWrite8(struct Machine *, uint32_t);
|
||||
uint8_t *GetModrmRegisterWordPointerWriteOsz(struct Machine *, uint32_t);
|
||||
|
|
160
tool/build/lib/op101.c
Normal file
160
tool/build/lib/op101.c
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*-*- 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 "tool/build/lib/endian.h"
|
||||
#include "tool/build/lib/modrm.h"
|
||||
#include "tool/build/lib/op101.h"
|
||||
#include "tool/build/lib/throw.h"
|
||||
#include "tool/build/lib/time.h"
|
||||
|
||||
static void SgdtMs(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void LgdtMs(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void SidtMs(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void LidtMs(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void Monitor(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void Mwait(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void Swapgs(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void Vmcall(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void Vmlaunch(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void Vmresume(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void Vmxoff(struct Machine *m, uint32_t rde) {
|
||||
}
|
||||
|
||||
static void InvlpgM(struct Machine *m, uint32_t rde) {
|
||||
ResetTlb(m);
|
||||
}
|
||||
|
||||
static void Smsw(struct Machine *m, uint32_t rde, bool ismem) {
|
||||
if (ismem) {
|
||||
Write16(GetModrmRegisterWordPointerWrite2(m, rde), m->cr0);
|
||||
} else if (Rexw(rde)) {
|
||||
Write64(RegRexrReg(m, rde), m->cr0);
|
||||
} else if (!Osz(rde)) {
|
||||
Write64(RegRexrReg(m, rde), m->cr0 & 0xffffffff);
|
||||
} else {
|
||||
Write16(RegRexrReg(m, rde), m->cr0);
|
||||
}
|
||||
}
|
||||
|
||||
static void Lmsw(struct Machine *m, uint32_t rde) {
|
||||
m->cr0 = Read16(GetModrmRegisterWordPointerRead2(m, rde));
|
||||
}
|
||||
|
||||
void Op101(struct Machine *m, uint32_t rde) {
|
||||
bool ismem;
|
||||
ismem = !IsModrmRegister(rde);
|
||||
switch (ModrmReg(rde)) {
|
||||
case 0:
|
||||
if (ismem) {
|
||||
SgdtMs(m, rde);
|
||||
} else {
|
||||
switch (ModrmRm(rde)) {
|
||||
case 1:
|
||||
Vmcall(m, rde);
|
||||
break;
|
||||
case 2:
|
||||
Vmlaunch(m, rde);
|
||||
break;
|
||||
case 3:
|
||||
Vmresume(m, rde);
|
||||
break;
|
||||
case 4:
|
||||
Vmxoff(m, rde);
|
||||
break;
|
||||
default:
|
||||
OpUd(m, rde);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (ismem) {
|
||||
SidtMs(m, rde);
|
||||
} else {
|
||||
switch (ModrmRm(rde)) {
|
||||
case 0:
|
||||
Monitor(m, rde);
|
||||
break;
|
||||
case 1:
|
||||
Mwait(m, rde);
|
||||
break;
|
||||
default:
|
||||
OpUd(m, rde);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (ismem) {
|
||||
LgdtMs(m, rde);
|
||||
} else {
|
||||
OpUd(m, rde);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (ismem) {
|
||||
LidtMs(m, rde);
|
||||
} else {
|
||||
OpUd(m, rde);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
Smsw(m, rde, ismem);
|
||||
break;
|
||||
case 6:
|
||||
Lmsw(m, rde);
|
||||
break;
|
||||
case 7:
|
||||
if (ismem) {
|
||||
InvlpgM(m, rde);
|
||||
} else {
|
||||
switch (ModrmRm(rde)) {
|
||||
case 0:
|
||||
Swapgs(m, rde);
|
||||
break;
|
||||
case 1:
|
||||
OpRdtscp(m, rde);
|
||||
break;
|
||||
default:
|
||||
OpUd(m, rde);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OpUd(m, rde);
|
||||
}
|
||||
}
|
11
tool/build/lib/op101.h
Normal file
11
tool/build/lib/op101.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_OP101_H_
|
||||
#define COSMOPOLITAN_TOOL_BUILD_LIB_OP101_H_
|
||||
#include "tool/build/lib/machine.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void Op101(struct Machine *, uint32_t);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_OP101_H_ */
|
|
@ -5,10 +5,12 @@
|
|||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct Panel {
|
||||
int i;
|
||||
int top, bottom;
|
||||
int left, right;
|
||||
int top;
|
||||
int bottom;
|
||||
int left;
|
||||
int right;
|
||||
struct Buffer *lines;
|
||||
size_t n;
|
||||
};
|
||||
|
||||
ssize_t PrintPanels(int, long, struct Panel *, long, long);
|
||||
|
|
|
@ -70,6 +70,7 @@ char *FormatPml4t(struct Machine *m) {
|
|||
unsigned short i, a[4];
|
||||
struct Pml4tFormater pp = {0};
|
||||
unsigned short range[][2] = {{256, 512}, {0, 256}};
|
||||
if ((m->mode & 3) != XED_MODE_LONG) return strdup("");
|
||||
pd[0] = GetPt(m, m->cr3);
|
||||
for (i = 0; i < ARRAYLEN(range); ++i) {
|
||||
a[0] = range[i][0];
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,36 +2,36 @@
|
|||
#define COSMOPOLITAN_TOOL_BUILD_LIB_PTY_H_
|
||||
#include "tool/build/lib/buffer.h"
|
||||
|
||||
#define kMachinePtyFg 0x0001
|
||||
#define kMachinePtyBg 0x0002
|
||||
#define kMachinePtyBold 0x0004
|
||||
#define kMachinePtyFlip 0x0008
|
||||
#define kMachinePtyFaint 0x0010
|
||||
#define kMachinePtyUnder 0x0020
|
||||
#define kMachinePtyDunder 0x0040
|
||||
#define kMachinePtyTrue 0x0080
|
||||
#define kMachinePtyBlink 0x0100
|
||||
#define kMachinePtyItalic 0x0200
|
||||
#define kMachinePtyFraktur 0x0400
|
||||
#define kMachinePtyStrike 0x0800
|
||||
#define kMachinePtyConceal 0x1000
|
||||
#define kPtyFg 0x0001
|
||||
#define kPtyBg 0x0002
|
||||
#define kPtyBold 0x0004
|
||||
#define kPtyFlip 0x0008
|
||||
#define kPtyFaint 0x0010
|
||||
#define kPtyUnder 0x0020
|
||||
#define kPtyDunder 0x0040
|
||||
#define kPtyTrue 0x0080
|
||||
#define kPtyBlink 0x0100
|
||||
#define kPtyItalic 0x0200
|
||||
#define kPtyFraktur 0x0400
|
||||
#define kPtyStrike 0x0800
|
||||
#define kPtyConceal 0x1000
|
||||
|
||||
#define kMachinePtyBell 0x001
|
||||
#define kMachinePtyRedzone 0x002
|
||||
#define kMachinePtyNocursor 0x004
|
||||
#define kMachinePtyBlinkcursor 0x008
|
||||
#define kMachinePtyNocanon 0x010
|
||||
#define kMachinePtyNoecho 0x020
|
||||
#define kMachinePtyNoopost 0x040
|
||||
#define kMachinePtyLed1 0x080
|
||||
#define kMachinePtyLed2 0x100
|
||||
#define kMachinePtyLed3 0x200
|
||||
#define kMachinePtyLed4 0x400
|
||||
#define kPtyBell 0x001
|
||||
#define kPtyRedzone 0x002
|
||||
#define kPtyNocursor 0x004
|
||||
#define kPtyBlinkcursor 0x008
|
||||
#define kPtyNocanon 0x010
|
||||
#define kPtyNoecho 0x020
|
||||
#define kPtyNoopost 0x040
|
||||
#define kPtyLed1 0x080
|
||||
#define kPtyLed2 0x100
|
||||
#define kPtyLed3 0x200
|
||||
#define kPtyLed4 0x400
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct MachinePty {
|
||||
struct Pty {
|
||||
int y;
|
||||
int x;
|
||||
int yn;
|
||||
|
@ -48,34 +48,34 @@ struct MachinePty {
|
|||
uint32_t *fgs;
|
||||
uint32_t *bgs;
|
||||
wchar_t *xlat;
|
||||
enum MachinePtyState {
|
||||
kMachinePtyAscii,
|
||||
kMachinePtyUtf8,
|
||||
kMachinePtyEsc,
|
||||
kMachinePtyCsi,
|
||||
enum PtyState {
|
||||
kPtyAscii,
|
||||
kPtyUtf8,
|
||||
kPtyEsc,
|
||||
kPtyCsi,
|
||||
} state;
|
||||
struct MachinePtyEsc {
|
||||
struct PtyEsc {
|
||||
unsigned i;
|
||||
char s[64];
|
||||
} esc;
|
||||
struct MachinePtyInput {
|
||||
struct PtyInput {
|
||||
size_t i, n;
|
||||
char *p;
|
||||
} input;
|
||||
};
|
||||
|
||||
void MachinePtyFree(struct MachinePty *);
|
||||
struct MachinePty *MachinePtyNew(void) nodiscard;
|
||||
void MachinePtyResize(struct MachinePty *, int, int);
|
||||
ssize_t MachinePtyRead(struct MachinePty *, void *, size_t);
|
||||
ssize_t MachinePtyWrite(struct MachinePty *, const void *, size_t);
|
||||
ssize_t MachinePtyWriteInput(struct MachinePty *, const void *, size_t);
|
||||
void MachinePtyAppendLine(struct MachinePty *, struct Buffer *, unsigned);
|
||||
void MachinePtyFullReset(struct MachinePty *);
|
||||
void MachinePtyMemmove(struct MachinePty *, long, long, long);
|
||||
void MachinePtyErase(struct MachinePty *, long, long);
|
||||
void MachinePtySetY(struct MachinePty *, int);
|
||||
void MachinePtySetX(struct MachinePty *, int);
|
||||
void FreePty(struct Pty *);
|
||||
struct Pty *NewPty(void) nodiscard;
|
||||
void PtyResize(struct Pty *, int, int);
|
||||
ssize_t PtyRead(struct Pty *, void *, size_t);
|
||||
ssize_t PtyWrite(struct Pty *, const void *, size_t);
|
||||
ssize_t PtyWriteInput(struct Pty *, const void *, size_t);
|
||||
int PtyAppendLine(struct Pty *, struct Buffer *, unsigned);
|
||||
void PtyFullReset(struct Pty *);
|
||||
void PtyMemmove(struct Pty *, long, long, long);
|
||||
void PtyErase(struct Pty *, long, long);
|
||||
void PtySetY(struct Pty *, int);
|
||||
void PtySetX(struct Pty *, int);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/pmovmskb.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "tool/build/lib/address.h"
|
||||
#include "tool/build/lib/endian.h"
|
||||
|
@ -496,23 +497,9 @@ void OpMov0fD6(struct Machine *m, uint32_t rde) {
|
|||
}
|
||||
}
|
||||
|
||||
static uint8_t pmovmskb(uint64_t x) {
|
||||
return (x & 0x0000000000000080) >> 007 | (x & 0x0000000000008000) >> 016 |
|
||||
(x & 0x0000000000800000) >> 025 | (x & 0x0000000080000000) >> 034 |
|
||||
(x & 0x0000008000000000) >> 043 | (x & 0x0000800000000000) >> 052 |
|
||||
(x & 0x0080000000000000) >> 061 | (x & 0x8000000000000000) >> 070;
|
||||
}
|
||||
|
||||
void OpPmovmskbGdqpNqUdq(struct Machine *m, uint32_t rde) {
|
||||
uint64_t bitmask;
|
||||
if (Osz(rde)) {
|
||||
bitmask = pmovmskb(Read64(XmmRexbRm(m, rde) + 8)) << 8 |
|
||||
pmovmskb(Read64(XmmRexbRm(m, rde)));
|
||||
} else {
|
||||
bitmask = pmovmskb(Read64(MmRm(m, rde) + 8)) << 8 |
|
||||
pmovmskb(Read64(MmRm(m, rde)));
|
||||
}
|
||||
Write64(RegRexrReg(m, rde), bitmask);
|
||||
Write64(RegRexrReg(m, rde),
|
||||
pmovmskb(XmmRexbRm(m, rde)) & (Osz(rde) ? 0xffff : 0xff));
|
||||
}
|
||||
|
||||
void OpMaskMovDiXmmRegXmmRm(struct Machine *m, uint32_t rde) {
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "tool/build/lib/stats.h"
|
||||
|
||||
unsigned long ops;
|
||||
unsigned long taken;
|
||||
unsigned long ntaken;
|
||||
unsigned long opcount;
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
extern unsigned long ops;
|
||||
extern unsigned long taken;
|
||||
extern unsigned long ntaken;
|
||||
extern unsigned long opcount;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.h"
|
||||
|
@ -321,11 +322,22 @@ static int XlatMsyncFlags(int x) {
|
|||
return res;
|
||||
}
|
||||
|
||||
static unsigned XlatOpenMode(unsigned flags) {
|
||||
switch (flags & 3) {
|
||||
case 0:
|
||||
return O_RDONLY;
|
||||
case 1:
|
||||
return O_WRONLY;
|
||||
case 2:
|
||||
return O_RDWR;
|
||||
default:
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned XlatOpenFlags(unsigned flags) {
|
||||
unsigned res = 0;
|
||||
if ((flags & 3) == 0) res = O_RDONLY;
|
||||
if ((flags & 3) == 1) res = O_WRONLY;
|
||||
if ((flags & 3) == 3) res = O_RDWR;
|
||||
res = XlatOpenMode(flags);
|
||||
if (flags & 0x80000) res |= O_CLOEXEC;
|
||||
if (flags & 0x400) res |= O_APPEND;
|
||||
if (flags & 0x40) res |= O_CREAT;
|
||||
|
@ -355,6 +367,7 @@ static int XlatFcntlArg(int x) {
|
|||
switch (x) {
|
||||
XLAT(0, 0);
|
||||
XLAT(1, FD_CLOEXEC);
|
||||
XLAT(0x0800, O_NONBLOCK);
|
||||
default:
|
||||
return einval();
|
||||
}
|
||||
|
@ -401,6 +414,14 @@ static int XlatRusage(int x) {
|
|||
}
|
||||
}
|
||||
|
||||
static const char *GetSimulated(void) {
|
||||
if (IsGenuineCosmo()) {
|
||||
return " SIMULATED";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static void VirtualSendRead(struct Machine *m, void *dst, int64_t addr,
|
||||
uint64_t n) {
|
||||
VirtualSend(m, dst, addr, n);
|
||||
|
@ -508,7 +529,7 @@ static int OpMadvise(struct Machine *m, int64_t addr, size_t length,
|
|||
static int64_t OpBrk(struct Machine *m, int64_t addr) {
|
||||
addr = ROUNDUP(addr, PAGESIZE);
|
||||
if (addr > m->brk) {
|
||||
if (ReserveVirtual(m, m->brk, addr - m->brk) != -1) {
|
||||
if (ReserveVirtual(m, m->brk, addr - m->brk, 0x0207) != -1) {
|
||||
m->brk = addr;
|
||||
}
|
||||
} else if (addr < m->brk) {
|
||||
|
@ -519,34 +540,47 @@ static int64_t OpBrk(struct Machine *m, int64_t addr) {
|
|||
return m->brk;
|
||||
}
|
||||
|
||||
static int OpMunmap(struct Machine *m, int64_t virt, uint64_t size) {
|
||||
VERBOSEF("MUNMAP%s %p %,ld", GetSimulated(), virt, size);
|
||||
return FreeVirtual(m, virt, size);
|
||||
}
|
||||
|
||||
static int64_t OpMmap(struct Machine *m, int64_t virt, size_t size, int prot,
|
||||
int flags, int fd, int64_t offset) {
|
||||
void *tmp;
|
||||
LOGF("MMAP%s %p %,ld %#x %#x %d %#lx", IsGenuineCosmo() ? " SIMULATED" : "",
|
||||
virt, size, prot, flags, fd, offset);
|
||||
flags = XlatMapFlags(flags);
|
||||
if (fd != -1 && (fd = XlatFd(m, fd)) == -1) return -1;
|
||||
if (!(flags & MAP_FIXED)) {
|
||||
if (!virt) {
|
||||
if ((virt = FindVirtual(m, m->brk, size)) == -1) return -1;
|
||||
m->brk = virt + size;
|
||||
} else {
|
||||
if ((virt = FindVirtual(m, virt, size)) == -1) return -1;
|
||||
uint64_t key;
|
||||
VERBOSEF("MMAP%s %p %,ld %#x %#x %d %#lx", GetSimulated(), virt, size, prot,
|
||||
flags, fd, offset);
|
||||
if (prot & PROT_READ) {
|
||||
key = 0x0205;
|
||||
if (prot & PROT_WRITE) key |= 2;
|
||||
if (!(prot & PROT_EXEC)) key |= 0x8000000000000000;
|
||||
flags = XlatMapFlags(flags);
|
||||
if (fd != -1 && (fd = XlatFd(m, fd)) == -1) return -1;
|
||||
if (!(flags & MAP_FIXED)) {
|
||||
if (!virt) {
|
||||
if ((virt = FindVirtual(m, m->brk, size)) == -1) return -1;
|
||||
m->brk = virt + size;
|
||||
} else {
|
||||
if ((virt = FindVirtual(m, virt, size)) == -1) return -1;
|
||||
}
|
||||
}
|
||||
if (ReserveVirtual(m, virt, size, key) != -1) {
|
||||
if (fd != -1 && !(flags & MAP_ANONYMOUS)) {
|
||||
/* TODO: lazy file mappings */
|
||||
CHECK_NOTNULL((tmp = malloc(size)));
|
||||
CHECK_EQ(size, pread(fd, tmp, size, offset));
|
||||
VirtualRecvWrite(m, virt, tmp, size);
|
||||
free(tmp);
|
||||
}
|
||||
} else {
|
||||
FreeVirtual(m, virt, size);
|
||||
return -1;
|
||||
}
|
||||
return virt;
|
||||
} else {
|
||||
return FreeVirtual(m, virt, size);
|
||||
}
|
||||
if (ReserveVirtual(m, virt, size) == -1) return -1;
|
||||
if (fd != -1 && !(flags & MAP_ANONYMOUS)) {
|
||||
/* TODO: lazy page loading */
|
||||
CHECK_NOTNULL((tmp = malloc(size)));
|
||||
CHECK_EQ(size, pread(fd, tmp, size, offset));
|
||||
VirtualRecvWrite(m, virt, tmp, size);
|
||||
free(tmp);
|
||||
}
|
||||
return virt;
|
||||
}
|
||||
|
||||
static int OpMunmap(struct Machine *m, int64_t addr, uint64_t size) {
|
||||
return FreeVirtual(m, addr, size);
|
||||
}
|
||||
|
||||
static int OpMsync(struct Machine *m, int64_t virt, size_t size, int flags) {
|
||||
|
@ -574,18 +608,23 @@ static int OpClose(struct Machine *m, int fd) {
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int OpOpenat(struct Machine *m, int dirfd, int64_t path, int flags,
|
||||
static int OpOpenat(struct Machine *m, int dirfd, int64_t pathaddr, int flags,
|
||||
int mode) {
|
||||
int fd, i;
|
||||
const char *path;
|
||||
flags = XlatOpenFlags(flags);
|
||||
if ((dirfd = XlatAfd(m, dirfd)) == -1) return -1;
|
||||
if ((i = MachineFdAdd(&m->fds)) == -1) return -1;
|
||||
if ((fd = openat(dirfd, LoadStr(m, path), flags, mode)) != -1) {
|
||||
path = LoadStr(m, pathaddr);
|
||||
if ((fd = openat(dirfd, path, flags, mode)) != -1) {
|
||||
m->fds.p[i].cb = &kMachineFdCbHost;
|
||||
m->fds.p[i].fd = fd;
|
||||
VERBOSEF("openat(%#x, %`'s, %#x, %#x) → %d [%d]", dirfd, path, flags, mode,
|
||||
i, fd);
|
||||
fd = i;
|
||||
} else {
|
||||
MachineFdRemove(&m->fds, i);
|
||||
VERBOSEF("openat(%#x, %`'s, %#x, %#x) failed", dirfd, path, flags, mode);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
@ -768,9 +807,13 @@ static ssize_t OpWrite(struct Machine *m, int fd, int64_t addr, size_t size) {
|
|||
if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) {
|
||||
if ((rc = m->fds.p[fd].cb->writev(m->fds.p[fd].fd, iv.p, iv.i)) != -1) {
|
||||
SetReadAddr(m, addr, rc);
|
||||
} else {
|
||||
VERBOSEF("write(%d [%d], %p, %zu) failed: %s", fd, m->fds.p[fd].fd,
|
||||
addr, size, strerror(errno));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VERBOSEF("write(%d, %p, %zu) bad fd", fd, addr, size);
|
||||
rc = ebadf();
|
||||
}
|
||||
FreeIovs(&iv);
|
||||
|
@ -1363,7 +1406,7 @@ void OpSyscall(struct Machine *m, uint32_t rde) {
|
|||
SYSCALL(0x177, vmsplice(di, P(si), dx, r0));
|
||||
CASE(0xE7, HaltMachine(m, di | 0x100));
|
||||
default:
|
||||
LOGF("missing syscall 0x%03x", ax);
|
||||
VERBOSEF("missing syscall 0x%03x", ax);
|
||||
ax = enosys();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/nexgen32e/vendor.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "tool/build/lib/address.h"
|
||||
|
@ -34,7 +35,7 @@ static bool IsHaltingInitialized(struct Machine *m) {
|
|||
|
||||
void HaltMachine(struct Machine *m, int code) {
|
||||
CHECK(IsHaltingInitialized(m));
|
||||
longjmp(m->onhalt, code);
|
||||
gclongjmp(m->onhalt, code);
|
||||
}
|
||||
|
||||
void ThrowDivideError(struct Machine *m) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue