mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-03 01:38:30 +00:00
Validate privileged code relationships
- Work towards improving non-optimized build support - Introduce MODE=zero which is -O0 without ASAN/UBSAN - Use system GCC when ~/.cosmo.mk has USE_SYSTEM_TOOLCHAIN=1 - Have package.com check .privileged code doesn't call non-privileged
This commit is contained in:
parent
01fd655097
commit
daf4454a06
82 changed files with 808 additions and 850 deletions
|
@ -48,17 +48,17 @@ o/$(MODE)/tool/build/emubin/%.bin.dbg: \
|
|||
$(TOOL_BUILD_EMUBIN_A) \
|
||||
o/$(MODE)/tool/build/emubin/%.o \
|
||||
$(TOOL_BUILD_EMUBIN_A).pkg
|
||||
@$(ELFLINK) -e emucrt -z max-page-size=0x10
|
||||
@$(ELFLINK) -e emucrt -z common-page-size=0x10 -z max-page-size=0x10
|
||||
|
||||
o/tiny/tool/build/emubin/spiral.bin.dbg: \
|
||||
$(TOOL_BUILD_EMUBIN_DEPS) \
|
||||
o/tiny/tool/build/emubin/spiral.real.o
|
||||
@$(ELFLINK) -z max-page-size=0x10 -T tool/build/emucrt/real.lds
|
||||
@$(ELFLINK) -z common-page-size=0x10 -z max-page-size=0x10 -T tool/build/emucrt/real.lds
|
||||
|
||||
o/tiny/tool/build/emubin/mdatest.bin.dbg: \
|
||||
$(TOOL_BUILD_EMUBIN_DEPS) \
|
||||
o/tiny/tool/build/emubin/mdatest.real.o
|
||||
@$(ELFLINK) -z max-page-size=0x10 -T tool/build/emucrt/real.lds
|
||||
@$(ELFLINK) -z common-page-size=0x10 -z max-page-size=0x10 -T tool/build/emucrt/real.lds
|
||||
|
||||
$(TOOL_BUILD_EMUBIN_OBJS): private \
|
||||
CFLAGS += \
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/dce.h"
|
||||
|
@ -26,9 +27,11 @@
|
|||
#include "libc/elf/struct/sym.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
@ -41,20 +44,21 @@
|
|||
* @fileoverview GCC Codegen Fixer-Upper.
|
||||
*/
|
||||
|
||||
#define GETOPTS "h"
|
||||
#define GETOPTS "ch"
|
||||
|
||||
#define USAGE \
|
||||
"\
|
||||
Usage: fixupobj.com [-h] ARGS...\n\
|
||||
-?\n\
|
||||
-h show help\n\
|
||||
-c check-only mode\n\
|
||||
"
|
||||
|
||||
#define COSMO_TLS_REG 28
|
||||
#define MRS_TPIDR_EL0 0xd53bd040u
|
||||
#define MOV_REG(DST, SRC) (0xaa0003e0u | (SRC) << 16 | (DST))
|
||||
|
||||
static const unsigned char kFatNops[8][8] = {
|
||||
const unsigned char kFatNops[8][8] = {
|
||||
{}, //
|
||||
{0x90}, // nop
|
||||
{0x66, 0x90}, // xchg %ax,%ax
|
||||
|
@ -65,31 +69,43 @@ static const unsigned char kFatNops[8][8] = {
|
|||
{0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00}, // nopl 0x00000000(%rax)
|
||||
};
|
||||
|
||||
void Write(const char *s, ...) {
|
||||
int mode;
|
||||
char *symstrs;
|
||||
char *secstrs;
|
||||
ssize_t esize;
|
||||
Elf64_Sym *syms;
|
||||
const char *epath;
|
||||
Elf64_Xword symcount;
|
||||
const Elf64_Ehdr *elf;
|
||||
|
||||
void Print(int fd, const char *s, ...) {
|
||||
va_list va;
|
||||
char buf[2048];
|
||||
va_start(va, s);
|
||||
buf[0] = 0;
|
||||
do {
|
||||
write(2, s, strlen(s));
|
||||
strlcat(buf, s, sizeof(buf));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
strlcat(buf, "\n", sizeof(buf));
|
||||
write(fd, buf, strlen(buf));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
wontreturn void SysExit(int rc, const char *call, const char *thing) {
|
||||
int err;
|
||||
char ibuf[12];
|
||||
const char *estr;
|
||||
err = errno;
|
||||
FormatInt32(ibuf, err);
|
||||
estr = _strerdoc(err);
|
||||
if (!estr) estr = "EUNKNOWN";
|
||||
Write(thing, ": ", call, "() failed: ", estr, " (", ibuf, ")\n", NULL);
|
||||
exit(rc);
|
||||
wontreturn void SysExit(const char *func) {
|
||||
const char *errstr;
|
||||
if (!(errstr = _strerdoc(errno))) errstr = "EUNKNOWN";
|
||||
Print(2, epath, ": ", func, " failed with ", errstr, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
mode = O_RDWR;
|
||||
while ((opt = getopt(argc, argv, GETOPTS)) != -1) {
|
||||
switch (opt) {
|
||||
case 'c':
|
||||
mode = O_RDONLY;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
write(1, USAGE, sizeof(USAGE) - 1);
|
||||
|
@ -101,17 +117,56 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
Elf64_Shdr *FindElfSectionByName(const char *name) {
|
||||
long i;
|
||||
Elf64_Shdr *shdr;
|
||||
for (i = 0; i < elf->e_shnum; ++i) {
|
||||
shdr = GetElfSectionHeaderAddress(elf, esize, i);
|
||||
if (!strcmp(GetElfString(elf, esize, secstrs, shdr->sh_name), name)) {
|
||||
return shdr;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CheckPrivilegedCrossReferences(void) {
|
||||
long i, x;
|
||||
Elf64_Shdr *shdr;
|
||||
const char *secname;
|
||||
Elf64_Rela *rela, *erela;
|
||||
if (!(shdr = FindElfSectionByName(".rela.privileged"))) return;
|
||||
rela = GetElfSectionAddress(elf, esize, shdr);
|
||||
erela = rela + shdr->sh_size / sizeof(*rela);
|
||||
for (; rela < erela; ++rela) {
|
||||
if (!ELF64_R_TYPE(rela->r_info)) continue;
|
||||
if (!(x = ELF64_R_SYM(rela->r_info))) continue;
|
||||
if (syms[x].st_shndx == SHN_ABS) continue;
|
||||
if (!syms[x].st_shndx) continue;
|
||||
shdr = GetElfSectionHeaderAddress(elf, esize, syms[x].st_shndx);
|
||||
if (~shdr->sh_flags & SHF_EXECINSTR) continue; // data reference
|
||||
secname = GetElfString(elf, esize, secstrs, shdr->sh_name);
|
||||
if (strcmp(".privileged", secname)) {
|
||||
Print(2, epath,
|
||||
": code in .privileged section "
|
||||
"references symbol '",
|
||||
GetElfString(elf, esize, symstrs, syms[x].st_name),
|
||||
"' in unprivileged code section '", secname, "'\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Modify ARM64 code to use x28 for TLS rather than tpidr_el0.
|
||||
void RewriteTlsCode(Elf64_Ehdr *elf, size_t elfsize) {
|
||||
void RewriteTlsCode(void) {
|
||||
int i, dest;
|
||||
Elf64_Shdr *shdr;
|
||||
uint32_t *p, *pe;
|
||||
for (i = 0; i < elf->e_shnum; ++i) {
|
||||
shdr = GetElfSectionHeaderAddress(elf, elfsize, i);
|
||||
shdr = GetElfSectionHeaderAddress(elf, esize, i);
|
||||
if (shdr->sh_type == SHT_PROGBITS && //
|
||||
(shdr->sh_flags & SHF_ALLOC) && //
|
||||
(shdr->sh_flags & SHF_EXECINSTR) && //
|
||||
(p = GetElfSectionAddress(elf, elfsize, shdr))) {
|
||||
(p = GetElfSectionAddress(elf, esize, shdr))) {
|
||||
for (pe = p + shdr->sh_size / 4; p <= pe; ++p) {
|
||||
if ((*p & -32) == MRS_TPIDR_EL0) {
|
||||
*p = MOV_REG(*p & 31, COSMO_TLS_REG);
|
||||
|
@ -131,19 +186,16 @@ void RewriteTlsCode(Elf64_Ehdr *elf, size_t elfsize) {
|
|||
* In order for this to work, the function symbol must be declared as
|
||||
* `STT_FUNC` and `st_size` must have the function's byte length.
|
||||
*/
|
||||
void OptimizePatchableFunctionEntries(Elf64_Ehdr *elf, size_t elfsize) {
|
||||
void OptimizePatchableFunctionEntries(void) {
|
||||
#ifdef __x86_64__
|
||||
long i, n;
|
||||
int nopcount;
|
||||
Elf64_Sym *syms;
|
||||
Elf64_Shdr *shdr;
|
||||
Elf64_Xword symcount;
|
||||
unsigned char *p, *pe;
|
||||
CHECK_NOTNULL((syms = GetElfSymbolTable(elf, elfsize, &symcount)));
|
||||
for (i = 0; i < symcount; ++i) {
|
||||
if (ELF64_ST_TYPE(syms[i].st_info) == STT_FUNC && syms[i].st_size) {
|
||||
shdr = GetElfSectionHeaderAddress(elf, elfsize, syms[i].st_shndx);
|
||||
p = GetElfSectionAddress(elf, elfsize, shdr);
|
||||
shdr = GetElfSectionHeaderAddress(elf, esize, syms[i].st_shndx);
|
||||
p = GetElfSectionAddress(elf, esize, shdr);
|
||||
p += syms[i].st_value;
|
||||
pe = p + syms[i].st_size;
|
||||
for (; p + 1 < pe; p += n) {
|
||||
|
@ -159,27 +211,22 @@ void OptimizePatchableFunctionEntries(Elf64_Ehdr *elf, size_t elfsize) {
|
|||
#endif /* __x86_64__ */
|
||||
}
|
||||
|
||||
void OptimizeRelocations(Elf64_Ehdr *elf, size_t elfsize) {
|
||||
char *strs;
|
||||
void OptimizeRelocations(void) {
|
||||
Elf64_Half i;
|
||||
Elf64_Sym *syms;
|
||||
Elf64_Rela *rela;
|
||||
Elf64_Xword symcount;
|
||||
unsigned char *code, *p;
|
||||
Elf64_Shdr *shdr, *shdrcode;
|
||||
CHECK_NOTNULL((strs = GetElfStringTable(elf, elfsize)));
|
||||
CHECK_NOTNULL((syms = GetElfSymbolTable(elf, elfsize, &symcount)));
|
||||
for (i = 0; i < elf->e_shnum; ++i) {
|
||||
shdr = GetElfSectionHeaderAddress(elf, elfsize, i);
|
||||
shdr = GetElfSectionHeaderAddress(elf, esize, i);
|
||||
if (shdr->sh_type == SHT_RELA) {
|
||||
CHECK_EQ(sizeof(struct Elf64_Rela), shdr->sh_entsize);
|
||||
CHECK_NOTNULL(
|
||||
(shdrcode = GetElfSectionHeaderAddress(elf, elfsize, shdr->sh_info)));
|
||||
(shdrcode = GetElfSectionHeaderAddress(elf, esize, shdr->sh_info)));
|
||||
if (!(shdrcode->sh_flags & SHF_EXECINSTR)) continue;
|
||||
CHECK_NOTNULL((code = GetElfSectionAddress(elf, elfsize, shdrcode)));
|
||||
for (rela = GetElfSectionAddress(elf, elfsize, shdr);
|
||||
CHECK_NOTNULL((code = GetElfSectionAddress(elf, esize, shdrcode)));
|
||||
for (rela = GetElfSectionAddress(elf, esize, shdr);
|
||||
((uintptr_t)rela + shdr->sh_entsize <=
|
||||
MIN((uintptr_t)elf + elfsize,
|
||||
MIN((uintptr_t)elf + esize,
|
||||
(uintptr_t)elf + shdr->sh_offset + shdr->sh_size));
|
||||
++rela) {
|
||||
|
||||
|
@ -189,7 +236,7 @@ void OptimizeRelocations(Elf64_Ehdr *elf, size_t elfsize) {
|
|||
* Then libc/runtime/ftrace.greg.c morphs it back at runtime.
|
||||
*/
|
||||
if (ELF64_R_TYPE(rela->r_info) == R_X86_64_GOTPCRELX &&
|
||||
strcmp(GetElfString(elf, elfsize, strs,
|
||||
strcmp(GetElfString(elf, esize, symstrs,
|
||||
syms[ELF64_R_SYM(rela->r_info)].st_name),
|
||||
"mcount") == 0) {
|
||||
rela->r_info = R_X86_64_NONE;
|
||||
|
@ -207,7 +254,7 @@ void OptimizeRelocations(Elf64_Ehdr *elf, size_t elfsize) {
|
|||
*/
|
||||
if ((ELF64_R_TYPE(rela->r_info) == R_X86_64_PC32 ||
|
||||
ELF64_R_TYPE(rela->r_info) == R_X86_64_PLT32) &&
|
||||
strcmp(GetElfString(elf, elfsize, strs,
|
||||
strcmp(GetElfString(elf, esize, symstrs,
|
||||
syms[ELF64_R_SYM(rela->r_info)].st_name),
|
||||
"mcount") == 0) {
|
||||
rela->r_info = R_X86_64_NONE;
|
||||
|
@ -223,45 +270,65 @@ void OptimizeRelocations(Elf64_Ehdr *elf, size_t elfsize) {
|
|||
}
|
||||
}
|
||||
|
||||
void RewriteObject(const char *path) {
|
||||
void FixupObject(void) {
|
||||
int fd;
|
||||
struct stat st;
|
||||
Elf64_Ehdr *elf;
|
||||
if ((fd = open(path, O_RDWR)) == -1) {
|
||||
SysExit(__COUNTER__ + 1, "open", path);
|
||||
if ((fd = open(epath, mode)) == -1) {
|
||||
SysExit("open");
|
||||
}
|
||||
if (fstat(fd, &st) == -1) {
|
||||
SysExit(__COUNTER__ + 1, "fstat", path);
|
||||
if ((esize = lseek(fd, 0, SEEK_END)) == -1) {
|
||||
SysExit("lseek");
|
||||
}
|
||||
if (st.st_size >= 64) {
|
||||
if ((elf = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
|
||||
0)) == MAP_FAILED) {
|
||||
SysExit(__COUNTER__ + 1, "mmap", path);
|
||||
if (esize) {
|
||||
if ((elf = mmap(0, esize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
|
||||
MAP_FAILED) {
|
||||
SysExit("mmap");
|
||||
}
|
||||
if (elf->e_machine == EM_NEXGEN32E) {
|
||||
OptimizeRelocations(elf, st.st_size);
|
||||
OptimizePatchableFunctionEntries(elf, st.st_size);
|
||||
if (!IsElf64Binary(elf, esize)) {
|
||||
Print(2, epath, ": not an elf64 binary\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
if (elf->e_machine == EM_AARCH64) {
|
||||
RewriteTlsCode(elf, st.st_size);
|
||||
if (!(syms = GetElfSymbolTable(elf, esize, &symcount))) {
|
||||
Print(2, epath, ": missing elf symbol table\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
if (msync(elf, st.st_size, MS_ASYNC | MS_INVALIDATE)) {
|
||||
SysExit(__COUNTER__ + 1, "msync", path);
|
||||
if (!(secstrs = GetElfSectionNameStringTable(elf, esize))) {
|
||||
Print(2, epath, ": missing elf section string table\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
if (munmap(elf, st.st_size)) {
|
||||
SysExit(__COUNTER__ + 1, "munmap", path);
|
||||
if (!(symstrs = GetElfStringTable(elf, esize))) {
|
||||
Print(2, epath, ": missing elf symbol string table\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
CheckPrivilegedCrossReferences();
|
||||
if (mode == O_RDWR) {
|
||||
if (elf->e_machine == EM_NEXGEN32E) {
|
||||
OptimizeRelocations();
|
||||
OptimizePatchableFunctionEntries();
|
||||
}
|
||||
if (elf->e_machine == EM_AARCH64) {
|
||||
RewriteTlsCode();
|
||||
}
|
||||
if (msync(elf, esize, MS_ASYNC | MS_INVALIDATE)) {
|
||||
SysExit("msync");
|
||||
}
|
||||
}
|
||||
if (munmap(elf, esize)) {
|
||||
SysExit("munmap");
|
||||
}
|
||||
}
|
||||
if (close(fd)) {
|
||||
SysExit(__COUNTER__ + 1, "close", path);
|
||||
SysExit("close");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, opt;
|
||||
if (IsModeDbg()) ShowCrashReports();
|
||||
if (!IsOptimized()) {
|
||||
ShowCrashReports();
|
||||
}
|
||||
GetOpts(argc, argv);
|
||||
for (i = optind; i < argc; ++i) {
|
||||
RewriteObject(argv[i]);
|
||||
epath = argv[i];
|
||||
FixupObject();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
/*-*- 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 │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/intrin/bsr.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "tool/build/lib/persist.h"
|
||||
|
||||
static bool IsWithin(size_t sz1, void *vp1, size_t sz2, void *vp2) {
|
||||
char *p1 = vp1, *p2 = vp2;
|
||||
return p1 >= p2 && p1 + sz1 <= p2 + sz2;
|
||||
}
|
||||
|
||||
static bool IsOverlapping(void *vx1, void *vy1, void *vx2, void *vy2) {
|
||||
char *x1 = vx1, *y1 = vy1, *x2 = vx2, *y2 = vy2;
|
||||
return (x1 >= x2 && x1 <= y2) || (y1 >= x2 && y1 <= y2);
|
||||
}
|
||||
|
||||
static bool IsOverlappingIov(struct iovec *a, struct iovec *b) {
|
||||
char *ap = a->iov_base, *bp = b->iov_base;
|
||||
return IsOverlapping(ap, ap + a->iov_len, bp, bp + b->iov_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes struct w/ dynamic arrays to mappable file, e.g.
|
||||
*
|
||||
* PersistObject(path, 64, &(struct ObjectParam){
|
||||
* sizeof(*o), o, &o->magic, &o->abi,
|
||||
* &(struct ObjectArrayParam){
|
||||
* {&o->a1.i, sizeof(o->a1.p[0]), &o->a1.p},
|
||||
* {&o->a2.i, sizeof(o->a2.p[0]), &o->a2.p},
|
||||
* {0},
|
||||
* }});
|
||||
*
|
||||
* @param obj->magic needs to be unique for struct
|
||||
* @param obj->abi monotonically tracks breaking changes
|
||||
* @param obj->arrays needs sentinel with item size of zero
|
||||
* @note non-recursive i.e. array elements can't have pointers
|
||||
* @see MapObject()
|
||||
*/
|
||||
void PersistObject(const char *path, size_t align,
|
||||
const struct ObjectParam *obj) {
|
||||
const char *pad;
|
||||
struct iovec *iov;
|
||||
int i, n, fd, iovlen;
|
||||
long len, size, bytes, filesize;
|
||||
unsigned char *hdr, *p1, *p2, **pp;
|
||||
intptr_t arrayptroffset, arraydataoffset;
|
||||
filesize = 0;
|
||||
DCHECK_GE(align, 1);
|
||||
CHECK_GT(*obj->magic, 0);
|
||||
CHECK_GT(*obj->abi, 0);
|
||||
CHECK(IsWithin(sizeof(*obj->magic), obj->magic, obj->size, obj->p));
|
||||
CHECK(IsWithin(sizeof(*obj->abi), obj->abi, obj->size, obj->p));
|
||||
for (n = i = 0; obj->arrays[i].size; ++i) ++n;
|
||||
iovlen = (n + 1) * 2;
|
||||
pad = _gc(xcalloc(align, 1));
|
||||
hdr = _gc(xmalloc(obj->size));
|
||||
iov = _gc(xcalloc(iovlen, sizeof(*iov)));
|
||||
bytes = obj->size;
|
||||
iov[0].iov_base = memcpy(hdr, obj->p, obj->size);
|
||||
iov[0].iov_len = bytes;
|
||||
iov[1].iov_base = pad;
|
||||
iov[1].iov_len = ROUNDUP(bytes, align) - bytes;
|
||||
filesize += ROUNDUP(bytes, align);
|
||||
for (i = 0; i < n; ++i) {
|
||||
pp = obj->arrays[i].pp;
|
||||
len = obj->arrays[i].len;
|
||||
size = obj->arrays[i].size;
|
||||
if (!*pp || !len) continue;
|
||||
p1 = obj->p;
|
||||
p2 = obj->arrays[i].pp;
|
||||
arrayptroffset = p2 - p1;
|
||||
arraydataoffset = filesize;
|
||||
CHECK((!len || _bsrl(len) + _bsrl(size) < 31),
|
||||
"path=%s i=%d len=%,lu size=%,lu", path, i, len, size);
|
||||
CHECK(IsWithin(sizeof(void *), pp, obj->size, obj->p));
|
||||
CHECK(!IsOverlapping(pp, pp + sizeof(void *), obj->magic,
|
||||
obj->magic + sizeof(*obj->magic)));
|
||||
CHECK(!IsOverlapping(pp, pp + sizeof(void *), obj->abi,
|
||||
obj->abi + sizeof(*obj->abi)));
|
||||
memcpy(hdr + arrayptroffset, &arraydataoffset, sizeof(intptr_t));
|
||||
CHECK_LT(filesize + arraydataoffset, 0x7ffff000);
|
||||
bytes = len * size;
|
||||
iov[(i + 1) * 2 + 0].iov_base = *pp;
|
||||
iov[(i + 1) * 2 + 0].iov_len = bytes;
|
||||
iov[(i + 1) * 2 + 1].iov_base = pad;
|
||||
iov[(i + 1) * 2 + 1].iov_len = ROUNDUP(bytes, align) - bytes;
|
||||
filesize += ROUNDUP(bytes, align);
|
||||
CHECK(!IsOverlappingIov(&iov[(i + 0) * 2], &iov[(i + 1) * 2]),
|
||||
"iov[%d]={%#p,%#x}, iov[%d]={%#p,%#x} path=%s", (i + 0) * 2,
|
||||
iov[(i + 0) * 2].iov_base, iov[(i + 0) * 2].iov_len, (i + 1) * 2,
|
||||
iov[(i + 1) * 2].iov_base, iov[(i + 1) * 2].iov_len, path);
|
||||
}
|
||||
CHECK_NE(-1, (fd = creat(path, 0644)), "%s", path);
|
||||
CHECK_EQ(filesize, writev(fd, iov, iovlen));
|
||||
CHECK_NE(-1, close(fd));
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_PERSIST_H_
|
||||
#define COSMOPOLITAN_TOOL_BUILD_LIB_PERSIST_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct ObjectArrayParam {
|
||||
size_t len;
|
||||
size_t size;
|
||||
void *pp;
|
||||
};
|
||||
|
||||
struct ObjectParam {
|
||||
size_t size;
|
||||
void *p;
|
||||
uint32_t *magic;
|
||||
int32_t *abi;
|
||||
struct ObjectArrayParam * arrays;
|
||||
};
|
||||
|
||||
void PersistObject(const char *, size_t, const struct ObjectParam *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_PERSIST_H_ */
|
|
@ -17,14 +17,18 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/elf/elf.h"
|
||||
#include "libc/elf/struct/rela.h"
|
||||
#include "libc/elf/struct/shdr.h"
|
||||
#include "libc/elf/struct/sym.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bswap.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/mem/arraylist.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -36,7 +40,8 @@
|
|||
#include "third_party/getopt/getopt.h"
|
||||
#include "third_party/xed/x86.h"
|
||||
#include "tool/build/lib/getargs.h"
|
||||
#include "tool/build/lib/persist.h"
|
||||
|
||||
STATIC_YOINK("realloc");
|
||||
|
||||
/**
|
||||
* @fileoverview Build Package Script.
|
||||
|
@ -67,7 +72,21 @@
|
|||
*/
|
||||
|
||||
#define PACKAGE_MAGIC bswap_32(0xBEEFBEEFu)
|
||||
#define PACKAGE_ABI 1
|
||||
#define PACKAGE_ABI 2
|
||||
|
||||
struct ObjectArrayParam {
|
||||
size_t len;
|
||||
size_t size;
|
||||
void *pp;
|
||||
};
|
||||
|
||||
struct ObjectParam {
|
||||
size_t size;
|
||||
void *p;
|
||||
uint32_t *magic;
|
||||
int32_t *abi;
|
||||
struct ObjectArrayParam *arrays;
|
||||
};
|
||||
|
||||
struct Packages {
|
||||
size_t i, n;
|
||||
|
@ -77,7 +96,7 @@ struct Packages {
|
|||
uint32_t path; // pkg->strings.p[path]
|
||||
int64_t fd; // not persisted
|
||||
void *addr; // not persisted
|
||||
size_t size; // not persisted
|
||||
ssize_t size; // not persisted
|
||||
struct Strings {
|
||||
size_t i, n;
|
||||
char *p; // persisted as pkg+RVA
|
||||
|
@ -86,28 +105,29 @@ struct Packages {
|
|||
size_t i, n;
|
||||
struct Object {
|
||||
uint32_t path; // pkg->strings.p[path]
|
||||
unsigned mode; // not persisted
|
||||
struct Elf64_Ehdr *elf; // not persisted
|
||||
size_t size; // not persisted
|
||||
char *strs; // not persisted
|
||||
Elf64_Sym *syms; // not persisted
|
||||
Elf64_Xword symcount; // not persisted
|
||||
struct Sections {
|
||||
size_t i, n;
|
||||
struct Section {
|
||||
enum SectionKind {
|
||||
kUndef,
|
||||
kText,
|
||||
kData,
|
||||
kPiroRelo,
|
||||
kPiroData,
|
||||
kPiroBss,
|
||||
kBss,
|
||||
} kind;
|
||||
} * p;
|
||||
} sections; // not persisted
|
||||
} * p; // persisted as pkg+RVA
|
||||
int section_offset;
|
||||
int section_count;
|
||||
} * p;
|
||||
} objects;
|
||||
struct Sections {
|
||||
size_t i, n;
|
||||
struct Section {
|
||||
int name;
|
||||
enum SectionKind {
|
||||
kUndef,
|
||||
kText,
|
||||
kPrivilegedText,
|
||||
kData,
|
||||
kBss,
|
||||
kOther,
|
||||
} kind;
|
||||
} * p;
|
||||
} sections;
|
||||
struct Symbols {
|
||||
size_t i, n;
|
||||
struct Symbol {
|
||||
|
@ -115,80 +135,223 @@ struct Packages {
|
|||
enum SectionKind kind : 8;
|
||||
uint8_t bind_ : 4;
|
||||
uint8_t type : 4;
|
||||
uint16_t object; // pkg->objects.p[object]
|
||||
} * p; // persisted as pkg+RVA
|
||||
} symbols, undefs; // TODO(jart): hash undefs?
|
||||
} * *p; // persisted across multiple files
|
||||
uint16_t object; // pkg->objects.p[object]
|
||||
uint16_t section; // pkg->sections.p[section]
|
||||
} * p; // persisted as pkg+RVA
|
||||
} symbols, undefs; // TODO(jart): hash undefs?
|
||||
} * *p; // persisted across multiple files
|
||||
};
|
||||
|
||||
int CompareSymbolName(const struct Symbol *a, const struct Symbol *b,
|
||||
const char *tab) {
|
||||
struct Relas {
|
||||
size_t i, n;
|
||||
struct Strings s;
|
||||
struct Rela {
|
||||
const char *symbol_name;
|
||||
const char *object_path;
|
||||
} * p;
|
||||
} prtu;
|
||||
|
||||
nullterminated() static void Print(int fd, const char *s, ...) {
|
||||
va_list va;
|
||||
char buf[2048];
|
||||
va_start(va, s);
|
||||
buf[0] = 0;
|
||||
do {
|
||||
strlcat(buf, s, sizeof(buf));
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
write(fd, buf, strlen(buf));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static wontreturn void SysExit(const char *path, const char *func) {
|
||||
const char *errstr;
|
||||
if (!(errstr = _strerdoc(errno))) errstr = "EUNKNOWN";
|
||||
Print(2, path, ": ", func, " failed with ", errstr, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int CompareSymbolName(const struct Symbol *a, const struct Symbol *b,
|
||||
const char *tab) {
|
||||
return strcmp(tab + a->name, tab + b->name);
|
||||
}
|
||||
|
||||
struct Package *LoadPackage(const char *path) {
|
||||
static void PrintSymbols(struct Package *pkg, struct Symbols *syms,
|
||||
const char *name) {
|
||||
int i;
|
||||
kprintf(" - %s=%d\n", name, syms->i);
|
||||
for (i = 0; i < syms->i; ++i) {
|
||||
kprintf(" - id=%d\n", i);
|
||||
kprintf(" name=%d [%s]\n", syms->p[i].name,
|
||||
pkg->strings.p + syms->p[i].name);
|
||||
kprintf(" kind=%d\n", syms->p[i].kind);
|
||||
kprintf(" bind=%d\n", syms->p[i].bind_);
|
||||
kprintf(" type=%d\n", syms->p[i].type);
|
||||
kprintf(" object=%d [%s]\n", syms->p[i].object,
|
||||
pkg->strings.p + pkg->objects.p[syms->p[i].object].path);
|
||||
kprintf(" section=%d [%s]\n", syms->p[i].section,
|
||||
syms->p[i].section == SHN_ABS
|
||||
? "SHN_ABS"
|
||||
: pkg->strings.p + pkg->sections.p[syms->p[i].section].name);
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintObject(struct Package *pkg, struct Object *obj) {
|
||||
int i, o;
|
||||
kprintf(" path=%d [%s]\n", obj->path, pkg->strings.p + obj->path);
|
||||
kprintf(" sections=%d\n", obj->section_count);
|
||||
for (i = 0; i < obj->section_count; ++i) {
|
||||
o = obj->section_offset;
|
||||
kprintf(" - id=%d %p (%d+%d)\n", i, pkg->sections.p, o, i);
|
||||
kprintf(" name=%d [%s]\n", pkg->sections.p[o + i].name,
|
||||
pkg->strings.p + pkg->sections.p[o + i].name);
|
||||
kprintf(" kind=%d\n", pkg->sections.p[o + i].kind);
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintPackage(struct Package *pkg) {
|
||||
int i, j, o;
|
||||
kprintf("- %s\n", pkg->strings.p + pkg->path);
|
||||
kprintf(" objects=%d\n", pkg->objects.i);
|
||||
for (i = 0; i < pkg->objects.i; ++i) {
|
||||
kprintf(" - id=%d\n", i);
|
||||
PrintObject(pkg, pkg->objects.p + i);
|
||||
}
|
||||
PrintSymbols(pkg, &pkg->symbols, "symbols");
|
||||
PrintSymbols(pkg, &pkg->undefs, "undefs");
|
||||
}
|
||||
|
||||
static void PrintPackages(struct Package *p, int n) {
|
||||
int i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
PrintPackage(p + i);
|
||||
}
|
||||
}
|
||||
|
||||
static struct Package *LoadPackage(const char *path) {
|
||||
int fd;
|
||||
ssize_t i;
|
||||
struct stat st;
|
||||
ssize_t i, size;
|
||||
struct Package *pkg;
|
||||
CHECK(fileexists(path), "%s: %s: %s\n", "error", path, "not found");
|
||||
CHECK_NE(-1, (fd = open(path, O_RDONLY)), "%s", path);
|
||||
CHECK_NE(-1, fstat(fd, &st));
|
||||
CHECK_NE(MAP_FAILED,
|
||||
(pkg = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
|
||||
fd, 0)),
|
||||
"path=%s", path);
|
||||
CHECK_NE(-1, close(fd));
|
||||
CHECK_EQ(PACKAGE_MAGIC, pkg->magic, "corrupt package: %`'s", path);
|
||||
pkg->strings.p = (char *)((intptr_t)pkg->strings.p + (intptr_t)pkg);
|
||||
pkg->objects.p = (struct Object *)((intptr_t)pkg->objects.p + (intptr_t)pkg);
|
||||
pkg->symbols.p = (struct Symbol *)((intptr_t)pkg->symbols.p + (intptr_t)pkg);
|
||||
if ((fd = open(path, O_RDONLY)) == -1) {
|
||||
SysExit(path, "open");
|
||||
}
|
||||
if ((size = lseek(fd, 0, SEEK_END)) == -1) {
|
||||
SysExit(path, "lseek");
|
||||
}
|
||||
if ((pkg = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) ==
|
||||
MAP_FAILED) {
|
||||
SysExit(path, "mmap");
|
||||
}
|
||||
close(fd);
|
||||
if (pkg->magic != PACKAGE_MAGIC) {
|
||||
Print(2, path, ": not a cosmo .pkg file\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
if (pkg->abi < PACKAGE_ABI) {
|
||||
Print(2, path, ": package has old abi try running make clean\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
pkg->strings.p = (void *)((uintptr_t)pkg->strings.p + (uintptr_t)pkg);
|
||||
pkg->objects.p = (void *)((uintptr_t)pkg->objects.p + (uintptr_t)pkg);
|
||||
pkg->symbols.p = (void *)((uintptr_t)pkg->symbols.p + (uintptr_t)pkg);
|
||||
pkg->sections.p = (void *)((uintptr_t)pkg->sections.p + (uintptr_t)pkg);
|
||||
pkg->addr = pkg;
|
||||
pkg->size = st.st_size;
|
||||
CHECK_NE(-1, mprotect(pkg, st.st_size, PROT_READ));
|
||||
pkg->size = size;
|
||||
if (mprotect(pkg, size, PROT_READ)) {
|
||||
SysExit(path, "mprotect");
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
|
||||
void AddDependency(struct Packages *deps, const char *path) {
|
||||
static void AddDependency(struct Packages *deps, const char *path) {
|
||||
struct Package *pkg;
|
||||
pkg = LoadPackage(path);
|
||||
CHECK_NE(-1, append(deps, &pkg));
|
||||
append(deps, &pkg);
|
||||
}
|
||||
|
||||
void WritePackage(struct Package *pkg) {
|
||||
CHECK_NE(0, PACKAGE_MAGIC);
|
||||
static void WritePackage(struct Package *pkg) {
|
||||
int fd;
|
||||
size_t n;
|
||||
int64_t o;
|
||||
const char *path;
|
||||
pkg->magic = PACKAGE_MAGIC;
|
||||
pkg->abi = PACKAGE_ABI;
|
||||
DEBUGF("%s has %,ld objects, %,ld symbols, and a %,ld byte string table",
|
||||
&pkg->strings.p[pkg->path], pkg->objects.i, pkg->symbols.i,
|
||||
pkg->strings.i);
|
||||
PersistObject(
|
||||
&pkg->strings.p[pkg->path], 64,
|
||||
&(struct ObjectParam){
|
||||
sizeof(struct Package),
|
||||
pkg,
|
||||
&pkg->magic,
|
||||
&pkg->abi,
|
||||
(struct ObjectArrayParam[]){
|
||||
{pkg->strings.i, sizeof(pkg->strings.p[0]), &pkg->strings.p},
|
||||
{pkg->objects.i, sizeof(pkg->objects.p[0]), &pkg->objects.p},
|
||||
{pkg->symbols.i, sizeof(pkg->symbols.p[0]), &pkg->symbols.p},
|
||||
{0},
|
||||
},
|
||||
});
|
||||
path = pkg->strings.p + pkg->path;
|
||||
if ((fd = creat(path, 0644)) == -1) {
|
||||
SysExit(path, "creat");
|
||||
}
|
||||
o = sizeof(*pkg);
|
||||
// write objects
|
||||
n = pkg->objects.i * sizeof(*pkg->objects.p);
|
||||
if (pwrite(fd, pkg->objects.p, n, o) != n) {
|
||||
SysExit(path, "pwrite");
|
||||
}
|
||||
pkg->objects.p = (void *)o;
|
||||
o += n;
|
||||
// write symbols
|
||||
n = pkg->symbols.i * sizeof(*pkg->symbols.p);
|
||||
if (pwrite(fd, pkg->symbols.p, n, o) != n) {
|
||||
SysExit(path, "pwrite");
|
||||
}
|
||||
pkg->symbols.p = (void *)o;
|
||||
o += n;
|
||||
// write sections
|
||||
n = pkg->sections.i * sizeof(*pkg->sections.p);
|
||||
if (pwrite(fd, pkg->sections.p, n, o) != n) {
|
||||
SysExit(path, "pwrite");
|
||||
}
|
||||
pkg->sections.p = (void *)o;
|
||||
o += n;
|
||||
// write strings
|
||||
n = pkg->strings.i * sizeof(*pkg->strings.p);
|
||||
pwrite(fd, pkg->strings.p, n, o);
|
||||
pkg->strings.p = (void *)o;
|
||||
// write header
|
||||
if (pwrite(fd, pkg, sizeof(*pkg), 0) != sizeof(*pkg)) {
|
||||
SysExit(path, "pwrite");
|
||||
}
|
||||
// we're done
|
||||
if (close(fd) == -1) {
|
||||
SysExit(path, "close");
|
||||
}
|
||||
}
|
||||
|
||||
void GetOpts(struct Package *pkg, struct Packages *deps, int argc,
|
||||
char *argv[]) {
|
||||
static wontreturn void PrintUsage(int fd, int exitcode) {
|
||||
Print(fd, "\
|
||||
NAME\n\
|
||||
\n\
|
||||
Cosmopolitan Monorepo Packager\n\
|
||||
\n\
|
||||
SYNOPSIS\n\
|
||||
\n\
|
||||
",
|
||||
program_invocation_name, " [FLAGS] OBJECT...\n\
|
||||
\n\
|
||||
DESCRIPTION\n\
|
||||
\n\
|
||||
This program verifies the well-formedness of symbolic references\n\
|
||||
and package dependencies in the cosmopolitan monolithic repository.\n\
|
||||
Validation happens incrementally and is granular to static libraries.\n\
|
||||
Each .a file should have its own .pkg file too, created by this tool.\n\
|
||||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-h show this help\n\
|
||||
-o PATH package output path\n\
|
||||
-d PATH package dependency path [repeatable]\n\
|
||||
\n\
|
||||
",
|
||||
NULL);
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
static void GetOpts(struct Package *pkg, struct Packages *deps, int argc,
|
||||
char *argv[]) {
|
||||
long i, si, opt;
|
||||
const char *arg;
|
||||
struct GetArgs ga;
|
||||
pkg->path = -1;
|
||||
while ((opt = getopt(argc, argv, "vho:d:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "ho:d:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
__log_level = kLogDebug;
|
||||
break;
|
||||
case 'o':
|
||||
pkg->path = concat(&pkg->strings, optarg, strlen(optarg) + 1);
|
||||
break;
|
||||
|
@ -196,72 +359,74 @@ void GetOpts(struct Package *pkg, struct Packages *deps, int argc,
|
|||
AddDependency(deps, optarg);
|
||||
break;
|
||||
case 'h':
|
||||
exit(0);
|
||||
PrintUsage(1, 0);
|
||||
default:
|
||||
fprintf(stderr, "%s: %s [%s %s] [%s %s] %s\n", "Usage",
|
||||
program_invocation_name, "-o", "OUTPACKAGE", "-d", "DEPPACKAGE",
|
||||
"OBJECT...");
|
||||
exit(1);
|
||||
PrintUsage(2, 1);
|
||||
}
|
||||
}
|
||||
CHECK_NE(-1, pkg->path, "no packages passed to package.com");
|
||||
CHECK_LT(optind, argc,
|
||||
"no objects passed to package.com; "
|
||||
"is your foo.mk $(FOO_OBJS) glob broken?");
|
||||
if (pkg->path == -1) {
|
||||
Print(2, "error: no packages passed to package.com\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
if (optind == argc) {
|
||||
Print(2,
|
||||
"no objects passed to package.com; is your foo.mk $(FOO_OBJS) glob "
|
||||
"broken?\n",
|
||||
NULL);
|
||||
exit(1);
|
||||
}
|
||||
getargs_init(&ga, argv + optind);
|
||||
while ((arg = getargs_next(&ga))) {
|
||||
CHECK_NE(-1, (si = concat(&pkg->strings, arg, strlen(arg) + 1)));
|
||||
CHECK_NE(-1, append(&pkg->objects, (&(struct Object){si})));
|
||||
struct Object obj = {0};
|
||||
obj.path = concat(&pkg->strings, arg, strlen(arg) + 1);
|
||||
append(&pkg->objects, &obj);
|
||||
}
|
||||
getargs_destroy(&ga);
|
||||
}
|
||||
|
||||
void IndexSections(struct Object *obj) {
|
||||
size_t i;
|
||||
static void IndexSections(struct Package *pkg, struct Object *obj) {
|
||||
int i;
|
||||
const char *name;
|
||||
const uint8_t *code;
|
||||
struct Section sect;
|
||||
const Elf64_Shdr *shdr;
|
||||
struct XedDecodedInst xedd;
|
||||
obj->section_offset = pkg->sections.i;
|
||||
for (i = 0; i < obj->elf->e_shnum; ++i) {
|
||||
bzero(§, sizeof(sect));
|
||||
CHECK_NOTNULL((shdr = GetElfSectionHeaderAddress(obj->elf, obj->size, i)));
|
||||
if (shdr->sh_type != SHT_NULL) {
|
||||
CHECK_NOTNULL((name = GetElfSectionName(obj->elf, obj->size, shdr)));
|
||||
if (_startswith(name, ".sort.")) name += 5;
|
||||
if ((strcmp(name, ".piro.relo") == 0 ||
|
||||
_startswith(name, ".piro.relo.")) ||
|
||||
(strcmp(name, ".data.rel.ro") == 0 ||
|
||||
_startswith(name, ".data.rel.ro."))) {
|
||||
sect.kind = kPiroRelo;
|
||||
} else if (strcmp(name, ".piro.data") == 0 ||
|
||||
_startswith(name, ".piro.data.")) {
|
||||
sect.kind = kPiroData;
|
||||
} else if (strcmp(name, ".piro.bss") == 0 ||
|
||||
_startswith(name, ".piro.bss.")) {
|
||||
sect.kind = kPiroBss;
|
||||
} else if (strcmp(name, ".data") == 0 || _startswith(name, ".data.")) {
|
||||
sect.kind = kData;
|
||||
} else if (strcmp(name, ".bss") == 0 || _startswith(name, ".bss.")) {
|
||||
sect.kind = kBss;
|
||||
} else {
|
||||
shdr = GetElfSectionHeaderAddress(obj->elf, obj->size, i);
|
||||
name = GetElfSectionName(obj->elf, obj->size, shdr);
|
||||
if (shdr->sh_type == SHT_NULL) {
|
||||
sect.kind = kUndef;
|
||||
} else if (shdr->sh_type == SHT_NOBITS) {
|
||||
sect.kind = kBss;
|
||||
} else if (shdr->sh_type == SHT_PROGBITS &&
|
||||
!(shdr->sh_flags & SHF_EXECINSTR)) {
|
||||
sect.kind = kData;
|
||||
} else if (shdr->sh_type == SHT_PROGBITS &&
|
||||
(shdr->sh_flags & SHF_EXECINSTR)) {
|
||||
if (strcmp(name, ".privileged")) {
|
||||
sect.kind = kText;
|
||||
} else {
|
||||
sect.kind = kPrivilegedText;
|
||||
}
|
||||
} else {
|
||||
sect.kind = kUndef; /* should always and only be section #0 */
|
||||
sect.kind = kOther;
|
||||
}
|
||||
CHECK_NE(-1, append(&obj->sections, §));
|
||||
sect.name = concat(&pkg->strings, name, strlen(name) + 1);
|
||||
append(&pkg->sections, §);
|
||||
++obj->section_count;
|
||||
}
|
||||
}
|
||||
|
||||
enum SectionKind ClassifySection(struct Object *obj, uint8_t type,
|
||||
Elf64_Section shndx) {
|
||||
static enum SectionKind ClassifySection(struct Package *pkg, struct Object *obj,
|
||||
uint8_t type, Elf64_Section shndx) {
|
||||
if (shndx == SHN_ABS) return kOther;
|
||||
if (type == STT_COMMON) return kBss;
|
||||
if (!obj->sections.i) return kText;
|
||||
return obj->sections.p[min(max(0, shndx), obj->sections.i - 1)].kind;
|
||||
return pkg->sections.p[obj->section_offset + shndx].kind;
|
||||
}
|
||||
|
||||
void LoadSymbols(struct Package *pkg, uint32_t object) {
|
||||
static void LoadSymbols(struct Package *pkg, uint32_t object) {
|
||||
Elf64_Xword i;
|
||||
const char *name;
|
||||
struct Object *obj;
|
||||
|
@ -269,57 +434,104 @@ void LoadSymbols(struct Package *pkg, uint32_t object) {
|
|||
obj = &pkg->objects.p[object];
|
||||
symbol.object = object;
|
||||
for (i = 0; i < obj->symcount; ++i) {
|
||||
symbol.section = obj->section_offset + obj->syms[i].st_shndx;
|
||||
symbol.bind_ = ELF64_ST_BIND(obj->syms[i].st_info);
|
||||
symbol.type = ELF64_ST_TYPE(obj->syms[i].st_info);
|
||||
if (symbol.bind_ != STB_LOCAL &&
|
||||
(symbol.type == STT_OBJECT || symbol.type == STT_FUNC ||
|
||||
symbol.type == STT_COMMON || symbol.type == STT_NOTYPE)) {
|
||||
name = GetElfString(obj->elf, obj->size, obj->strs, obj->syms[i].st_name);
|
||||
DEBUGF("%s", name);
|
||||
if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") != 0) {
|
||||
symbol.kind = ClassifySection(obj, symbol.type, obj->syms[i].st_shndx);
|
||||
CHECK_NE(-1,
|
||||
(symbol.name = concat(&pkg->strings, name, strlen(name) + 1)));
|
||||
CHECK_NE(-1,
|
||||
append(symbol.kind != kUndef ? &pkg->symbols : &pkg->undefs,
|
||||
&symbol));
|
||||
if (strcmp(name, "_GLOBAL_OFFSET_TABLE_")) {
|
||||
symbol.kind =
|
||||
ClassifySection(pkg, obj, symbol.type, obj->syms[i].st_shndx);
|
||||
symbol.name = concat(&pkg->strings, name, strlen(name) + 1);
|
||||
append(symbol.kind != kUndef ? &pkg->symbols : &pkg->undefs, &symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenObject(struct Package *pkg, struct Object *obj, int mode, int prot,
|
||||
int flags) {
|
||||
static Elf64_Shdr *FindElfSectionByName(Elf64_Ehdr *elf, size_t esize,
|
||||
const char *name) {
|
||||
long i;
|
||||
Elf64_Shdr *shdr;
|
||||
for (i = 0; i < elf->e_shnum; ++i) {
|
||||
shdr = GetElfSectionHeaderAddress(elf, esize, i);
|
||||
if (!strcmp(GetElfSectionName(elf, esize, shdr), name)) {
|
||||
return shdr;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void LoadPriviligedRefsToUndefs(struct Package *pkg,
|
||||
struct Object *obj) {
|
||||
long x;
|
||||
struct Rela r;
|
||||
const char *s;
|
||||
Elf64_Shdr *shdr;
|
||||
Elf64_Rela *rela, *erela;
|
||||
if ((shdr = FindElfSectionByName(obj->elf, obj->size, ".rela.privileged"))) {
|
||||
rela = GetElfSectionAddress(obj->elf, obj->size, shdr);
|
||||
erela = rela + shdr->sh_size / sizeof(*rela);
|
||||
for (; rela < erela; ++rela) {
|
||||
if (!ELF64_R_TYPE(rela->r_info)) continue;
|
||||
if (!(x = ELF64_R_SYM(rela->r_info))) continue;
|
||||
if (obj->syms[x].st_shndx) continue; // symbol is defined
|
||||
if (ELF64_ST_BIND(obj->syms[x].st_info) != STB_WEAK &&
|
||||
ELF64_ST_BIND(obj->syms[x].st_info) != STB_GLOBAL) {
|
||||
Print(2, "warning: undefined symbol not global\n", NULL);
|
||||
continue;
|
||||
}
|
||||
r.symbol_name = strdup(
|
||||
GetElfString(obj->elf, obj->size, obj->strs, obj->syms[x].st_name));
|
||||
r.object_path = strdup(pkg->strings.p + obj->path);
|
||||
append(&prtu, &r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OpenObject(struct Package *pkg, struct Object *obj, int oid) {
|
||||
int fd;
|
||||
struct stat st;
|
||||
CHECK_NE(-1, (fd = open(&pkg->strings.p[obj->path], (obj->mode = mode))),
|
||||
"path=%`'s", &pkg->strings.p[obj->path]);
|
||||
CHECK_NE(-1, fstat(fd, &st));
|
||||
CHECK_NE(
|
||||
MAP_FAILED,
|
||||
(obj->elf = mmap(NULL, (obj->size = st.st_size), prot, flags, fd, 0)),
|
||||
"path=%`'s", &pkg->strings.p[obj->path]);
|
||||
CHECK_NE(-1, close(fd));
|
||||
CHECK(IsElf64Binary(obj->elf, obj->size), "path=%`'s",
|
||||
&pkg->strings.p[obj->path]);
|
||||
CHECK_NOTNULL((obj->strs = GetElfStringTable(obj->elf, obj->size)), "on %s",
|
||||
&pkg->strings.p[obj->path]);
|
||||
CHECK_NOTNULL(
|
||||
(obj->syms = GetElfSymbolTable(obj->elf, obj->size, &obj->symcount)));
|
||||
CHECK_NE(0, obj->symcount);
|
||||
IndexSections(obj);
|
||||
const char *path;
|
||||
path = pkg->strings.p + obj->path;
|
||||
if ((fd = open(path, O_RDONLY)) == -1) {
|
||||
SysExit(path, "open");
|
||||
}
|
||||
if ((obj->size = lseek(fd, 0, SEEK_END)) == -1) {
|
||||
SysExit(path, "lseek");
|
||||
}
|
||||
if ((obj->elf = mmap(0, obj->size, PROT_READ, MAP_SHARED, fd, 0)) ==
|
||||
MAP_FAILED) {
|
||||
SysExit(path, "mmap");
|
||||
}
|
||||
close(fd);
|
||||
if (!IsElf64Binary(obj->elf, obj->size)) {
|
||||
Print(2, path, ": not an elf64 binary\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
if (!(obj->strs = GetElfStringTable(obj->elf, obj->size))) {
|
||||
Print(2, path, ": missing elf string table\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
if (!(obj->syms = GetElfSymbolTable(obj->elf, obj->size, &obj->symcount))) {
|
||||
Print(2, path, ": missing elf symbol table\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
IndexSections(pkg, obj);
|
||||
LoadPriviligedRefsToUndefs(pkg, obj);
|
||||
}
|
||||
|
||||
void CloseObject(struct Object *obj) {
|
||||
CHECK_NE(-1, munmap(obj->elf, obj->size));
|
||||
static void CloseObject(struct Object *obj) {
|
||||
if (munmap(obj->elf, obj->size)) notpossible;
|
||||
}
|
||||
|
||||
void LoadObjects(struct Package *pkg) {
|
||||
static void LoadObjects(struct Package *pkg) {
|
||||
size_t i;
|
||||
struct Object *obj;
|
||||
for (i = 0; i < pkg->objects.i; ++i) {
|
||||
obj = pkg->objects.p + i;
|
||||
OpenObject(pkg, obj, O_RDONLY, PROT_READ, MAP_SHARED);
|
||||
OpenObject(pkg, obj, i);
|
||||
LoadSymbols(pkg, i);
|
||||
CloseObject(obj);
|
||||
}
|
||||
|
@ -327,7 +539,7 @@ void LoadObjects(struct Package *pkg) {
|
|||
(void *)CompareSymbolName, pkg->strings.p);
|
||||
}
|
||||
|
||||
struct Symbol *BisectSymbol(struct Package *pkg, const char *name) {
|
||||
static struct Symbol *BisectSymbol(struct Package *pkg, const char *name) {
|
||||
int c;
|
||||
long m, l, r;
|
||||
l = 0;
|
||||
|
@ -346,9 +558,9 @@ struct Symbol *BisectSymbol(struct Package *pkg, const char *name) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool FindSymbol(const char *name, struct Package *pkg,
|
||||
struct Packages *directdeps, struct Package **out_pkg,
|
||||
struct Symbol **out_sym) {
|
||||
static bool FindSymbol(const char *name, struct Package *pkg,
|
||||
struct Packages *directdeps, struct Package **out_pkg,
|
||||
struct Symbol **out_sym) {
|
||||
size_t i, j;
|
||||
struct Symbol *sym;
|
||||
if ((sym = BisectSymbol(pkg, name))) {
|
||||
|
@ -366,7 +578,7 @@ bool FindSymbol(const char *name, struct Package *pkg,
|
|||
return false;
|
||||
}
|
||||
|
||||
void CheckStrictDeps(struct Package *pkg, struct Packages *deps) {
|
||||
static void CheckStrictDeps(struct Package *pkg, struct Packages *deps) {
|
||||
size_t i, j;
|
||||
struct Package *dep;
|
||||
struct Symbol *undef;
|
||||
|
@ -374,15 +586,13 @@ void CheckStrictDeps(struct Package *pkg, struct Packages *deps) {
|
|||
undef = &pkg->undefs.p[i];
|
||||
if (undef->bind_ == STB_WEAK) continue;
|
||||
if (!FindSymbol(pkg->strings.p + undef->name, pkg, deps, NULL, NULL)) {
|
||||
fprintf(stderr, "%s: %`'s (%s) %s %s\n", "error",
|
||||
pkg->strings.p + undef->name,
|
||||
pkg->strings.p + pkg->objects.p[undef->object].path,
|
||||
"not defined by direct deps of", pkg->strings.p + pkg->path);
|
||||
Print(2, pkg->strings.p + pkg->path, ": undefined symbol '",
|
||||
pkg->strings.p + undef->name, "' (",
|
||||
pkg->strings.p + pkg->objects.p[undef->object].path,
|
||||
") not defined by direct dependencies:\n", NULL);
|
||||
for (j = 0; j < deps->i; ++j) {
|
||||
dep = deps->p[j];
|
||||
fputc('\t', stderr);
|
||||
fputs(dep->strings.p + dep->path, stderr);
|
||||
fputc('\n', stderr);
|
||||
Print(2, "\t", dep->strings.p + dep->path, "\n", NULL);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
@ -391,43 +601,53 @@ void CheckStrictDeps(struct Package *pkg, struct Packages *deps) {
|
|||
bzero(&pkg->undefs, sizeof(pkg->undefs));
|
||||
}
|
||||
|
||||
forceinline bool IsRipRelativeModrm(uint8_t modrm) {
|
||||
return (modrm & 0b11000111) == 0b00000101;
|
||||
static void CheckYourPrivilege(struct Package *pkg, struct Packages *deps) {
|
||||
int i, j, o, f;
|
||||
const char *name;
|
||||
struct Symbol *sym;
|
||||
struct Package *dep;
|
||||
for (f = i = 0; i < prtu.i; ++i) {
|
||||
name = prtu.p[i].symbol_name;
|
||||
if (FindSymbol(name, pkg, deps, &dep, &sym) &&
|
||||
dep->sections.p[sym->section].kind == kText) {
|
||||
Print(2, prtu.p[i].object_path,
|
||||
": privileged code referenced unprivileged symbol '", name,
|
||||
"' in section '",
|
||||
dep->strings.p + dep->sections.p[sym->section].name, "'\n", NULL);
|
||||
++f;
|
||||
}
|
||||
}
|
||||
if (f) exit(1);
|
||||
}
|
||||
|
||||
forceinline uint8_t ChangeRipToRbx(uint8_t modrm) {
|
||||
return (modrm & 0b00111000) | 0b10000011;
|
||||
static bool IsSymbolDirectlyReachable(struct Package *pkg,
|
||||
struct Packages *deps,
|
||||
const char *symbol) {
|
||||
return FindSymbol(symbol, pkg, deps, 0, 0);
|
||||
}
|
||||
|
||||
bool IsSymbolDirectlyReachable(struct Package *pkg, struct Packages *deps,
|
||||
const char *symbol) {
|
||||
return FindSymbol(symbol, pkg, deps, NULL, NULL);
|
||||
}
|
||||
|
||||
void Package(int argc, char *argv[], struct Package *pkg,
|
||||
struct Packages *deps) {
|
||||
static void Package(int argc, char *argv[], struct Package *pkg,
|
||||
struct Packages *deps) {
|
||||
size_t i, j;
|
||||
GetOpts(pkg, deps, argc, argv);
|
||||
LoadObjects(pkg);
|
||||
CheckStrictDeps(pkg, deps);
|
||||
CheckYourPrivilege(pkg, deps);
|
||||
WritePackage(pkg);
|
||||
for (i = 0; i < deps->i; ++i) {
|
||||
CHECK_NE(-1, munmap(deps->p[i]->addr, deps->p[i]->size));
|
||||
if (munmap(deps->p[i]->addr, deps->p[i]->size)) notpossible;
|
||||
}
|
||||
for (i = 0; i < pkg->objects.i; ++i) {
|
||||
free(pkg->objects.p[i].sections.p);
|
||||
}
|
||||
free(pkg->strings.p);
|
||||
free(pkg->objects.p);
|
||||
free(pkg->symbols.p);
|
||||
free(deps->p);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct Package pkg;
|
||||
struct Packages deps;
|
||||
if (argc == 2 && !strcmp(argv[1], "-n")) exit(0);
|
||||
if (IsModeDbg()) ShowCrashReports();
|
||||
if (argc == 2 && !strcmp(argv[1], "-n")) {
|
||||
exit(0);
|
||||
}
|
||||
if (!IsOptimized()) {
|
||||
ShowCrashReports();
|
||||
}
|
||||
bzero(&pkg, sizeof(pkg));
|
||||
bzero(&deps, sizeof(deps));
|
||||
Package(argc, argv, &pkg, &deps);
|
||||
|
|
|
@ -1052,7 +1052,9 @@ wontreturn void StraceMain(int argc, char *argv[]) {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
if (IsModeDbg()) ShowCrashReports();
|
||||
if (!IsOptimized()) {
|
||||
ShowCrashReports();
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
kprintf("Usage: %s PROGRAM [ARGS...]%n", argv[0]);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/ioctl.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/termios.h"
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
(require 'ld-script)
|
||||
(require 'make-mode)
|
||||
|
||||
(setq cosmo-dbg-mode "zero")
|
||||
(setq c-doc-comment-style 'javadown)
|
||||
|
||||
(add-to-list 'auto-mode-alist '("\\.x$" . c-mode)) ;; -aux-info
|
||||
|
@ -162,13 +163,13 @@
|
|||
(cond ((eq arg 1) "tiny")
|
||||
((eq arg 2) "opt")
|
||||
((eq arg 3) "rel")
|
||||
((eq arg 4) "dbg")
|
||||
((eq arg 4) cosmo-dbg-mode)
|
||||
((eq arg 5) "")
|
||||
((eq arg 6) "llvm")
|
||||
((eq arg 7) "aarch64")
|
||||
((eq arg 8) "aarch64-tiny")
|
||||
(default default)
|
||||
((cosmo-intest) "dbg")
|
||||
((cosmo-intest) cosmo-dbg-mode)
|
||||
(t "fastbuild")))
|
||||
|
||||
(defun cosmo--make-suffix (arg)
|
||||
|
@ -684,7 +685,7 @@
|
|||
(let* ((this (or (buffer-file-name) dired-directory))
|
||||
(root (locate-dominating-file this "Makefile")))
|
||||
(when root
|
||||
(let* ((mode (cosmo--make-mode arg "dbg"))
|
||||
(let* ((mode (cosmo--make-mode arg cosmo-dbg-mode))
|
||||
(name (file-relative-name this root))
|
||||
(next (file-name-sans-extension name))
|
||||
(exec (format "o/%s/%s.com.dbg" mode next))
|
||||
|
|
|
@ -249,6 +249,7 @@ o/$(MODE)/tool/net/redbean-demo.com: \
|
|||
|
||||
o/$(MODE)/tool/net/redbean-static.com.dbg: \
|
||||
$(TOOL_NET_DEPS) \
|
||||
$(TOOL_NET_REDBEAN_LUA_MODULES) \
|
||||
o/$(MODE)/tool/net/redbean-static.o \
|
||||
o/$(MODE)/tool/net/net.pkg \
|
||||
$(CRT) \
|
||||
|
@ -300,6 +301,7 @@ o/$(MODE)/tool/net/redbean-unsecure.com: \
|
|||
|
||||
o/$(MODE)/tool/net/redbean-original.com.dbg: \
|
||||
$(TOOL_NET_DEPS) \
|
||||
$(TOOL_NET_REDBEAN_LUA_MODULES) \
|
||||
o/$(MODE)/tool/net/redbean-original.o \
|
||||
o/$(MODE)/tool/net/net.pkg \
|
||||
$(CRT) \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue