Initial import

This commit is contained in:
Justine Tunney 2020-06-15 07:18:57 -07:00
commit c91b3c5006
14915 changed files with 590219 additions and 0 deletions

View file

@ -0,0 +1,58 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += TOOL_BUILD_LIB
TOOL_BUILD_LIB_ARTIFACTS += TOOL_BUILD_LIB_A
TOOL_BUILD_LIB = $(TOOL_BUILD_LIB_A_DEPS) $(TOOL_BUILD_LIB_A)
TOOL_BUILD_LIB_A = o/$(MODE)/tool/build/lib/buildlib.a
TOOL_BUILD_LIB_A_FILES := $(wildcard tool/build/lib/*)
TOOL_BUILD_LIB_A_HDRS = $(filter %.h,$(TOOL_BUILD_LIB_A_FILES))
TOOL_BUILD_LIB_A_SRCS_S = $(filter %.S,$(TOOL_BUILD_LIB_A_FILES))
TOOL_BUILD_LIB_A_SRCS_C = $(filter %.c,$(TOOL_BUILD_LIB_A_FILES))
TOOL_BUILD_LIB_A_CHECKS = $(TOOL_BUILD_LIB_A).pkg
TOOL_BUILD_LIB_A_SRCS = \
$(TOOL_BUILD_LIB_A_SRCS_S) \
$(TOOL_BUILD_LIB_A_SRCS_C)
TOOL_BUILD_LIB_A_OBJS = \
$(TOOL_BUILD_LIB_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(TOOL_BUILD_LIB_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(TOOL_BUILD_LIB_A_SRCS_C:%.c=o/$(MODE)/%.o)
TOOL_BUILD_LIB_A_DIRECTDEPS = \
LIBC_CALLS \
LIBC_CALLS_HEFTY \
LIBC_FMT \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STUBS \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_LOG \
LIBC_X
TOOL_BUILD_LIB_A_DEPS := \
$(call uniq,$(foreach x,$(TOOL_BUILD_LIB_A_DIRECTDEPS),$($(x))))
$(TOOL_BUILD_LIB_A): \
tool/build/lib/ \
$(TOOL_BUILD_LIB_A).pkg \
$(TOOL_BUILD_LIB_A_OBJS)
$(TOOL_BUILD_LIB_A).pkg: \
$(TOOL_BUILD_LIB_A_OBJS) \
$(foreach x,$(TOOL_BUILD_LIB_A_DIRECTDEPS),$($(x)_A).pkg)
TOOL_BUILD_LIB_LIBS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)))
TOOL_BUILD_LIB_SRCS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_SRCS))
TOOL_BUILD_LIB_HDRS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_HDRS))
TOOL_BUILD_LIB_BINS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_BINS))
TOOL_BUILD_LIB_CHECKS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_CHECKS))
TOOL_BUILD_LIB_OBJS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_OBJS))
TOOL_BUILD_LIB_TESTS = $(foreach x,$(TOOL_BUILD_LIB_ARTIFACTS),$($(x)_TESTS))
.PHONY: o/$(MODE)/tool/build/lib
o/$(MODE)/tool/build/lib: $(TOOL_BUILD_LIB_CHECKS)

259
tool/build/lib/elfwriter.c Normal file
View file

@ -0,0 +1,259 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/alg/arraylist.h"
#include "libc/assert.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/mappings.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/mremap.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/prot.h"
#include "libc/x/x.h"
#include "tool/build/lib/elfwriter.h"
#include "tool/build/lib/interner.h"
static const Elf64_Ehdr kObjHeader = {
.e_ident = {ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS64, ELFDATA2LSB, 1,
ELFOSABI_NONE},
.e_type = ET_REL,
.e_machine = EM_NEXGEN32E,
.e_version = 1,
.e_ehsize = sizeof(Elf64_Ehdr),
.e_shentsize = sizeof(Elf64_Shdr)};
static size_t AppendSection(struct ElfWriter *elf, const char *name,
int sh_type, int sh_flags) {
ssize_t section =
append(elf->shdrs,
(&(Elf64_Shdr){.sh_type = sh_type,
.sh_flags = sh_flags,
.sh_entsize = elf->entsize,
.sh_addralign = elf->addralign,
.sh_offset = sh_type != SHT_NULL ? elf->wrote : 0,
.sh_name = intern(elf->shstrtab, name)}));
CHECK_NE(-1, section);
return section;
}
static size_t FinishSection(struct ElfWriter *elf) {
size_t section = elf->shdrs->i - 1;
elf->shdrs->p[section].sh_size =
elf->wrote - elf->shdrs->p[section].sh_offset;
return section;
}
static struct ElfWriterSymRef AppendSymbol(struct ElfWriter *elf,
const char *name, int st_info,
int st_other, size_t st_value,
size_t st_size, size_t st_shndx,
enum ElfWriterSymOrder slg) {
ssize_t sym =
append(elf->syms[slg], (&(Elf64_Sym){.st_info = st_info,
.st_size = st_size,
.st_value = st_value,
.st_other = st_other,
.st_name = intern(elf->strtab, name),
.st_shndx = st_shndx}));
CHECK_NE(-1, sym);
return (struct ElfWriterSymRef){.slg = slg, .sym = sym};
}
static void MakeRelaSection(struct ElfWriter *elf, size_t section) {
size_t shdr, size;
size = (elf->relas->i - elf->relas->j) * sizeof(Elf64_Rela);
elfwriter_align(elf, alignof(Elf64_Rela), sizeof(Elf64_Rela));
shdr = elfwriter_startsection(
elf,
gc(xasprintf("%s%s", ".rela",
&elf->shstrtab->p[elf->shdrs->p[section].sh_name])),
SHT_RELA, SHF_INFO_LINK);
elf->shdrs->p[shdr].sh_info = section;
elfwriter_reserve(elf, size);
elfwriter_commit(elf, size);
FinishSection(elf);
elf->relas->j = elf->relas->i;
}
static void WriteRelaSections(struct ElfWriter *elf, size_t symtab) {
uint32_t sym;
size_t i, j, k;
Elf64_Rela *rela;
for (j = 0, i = 0; i < elf->shdrs->i; ++i) {
if (elf->shdrs->p[i].sh_type == SHT_RELA) {
elf->shdrs->p[i].sh_link = symtab;
for (rela = (Elf64_Rela *)((char *)elf->map + elf->shdrs->p[i].sh_offset);
rela <
(Elf64_Rela *)((char *)elf->map + (elf->shdrs->p[i].sh_offset +
elf->shdrs->p[i].sh_size));
rela++, j++) {
sym = elf->relas->p[j].symkey.sym;
for (k = 0; k < elf->relas->p[j].symkey.slg; ++k) {
sym += elf->syms[k]->i;
}
rela->r_offset = elf->relas->p[j].offset;
rela->r_info = ELF64_R_INFO(sym, elf->relas->p[j].type);
rela->r_addend = elf->relas->p[j].addend;
}
}
}
assert(j == elf->relas->i);
}
static size_t FlushStrtab(struct ElfWriter *elf, const char *name,
struct Interner *strtab) {
size_t size = strtab->i * sizeof(strtab->p[0]);
elfwriter_align(elf, 1, 0);
AppendSection(elf, ".strtab", SHT_STRTAB, 0);
mempcpy(elfwriter_reserve(elf, size), strtab->p, size);
elfwriter_commit(elf, size);
return FinishSection(elf);
}
static void FlushTables(struct ElfWriter *elf) {
size_t i, size, symtab;
elfwriter_align(elf, alignof(Elf64_Sym), sizeof(Elf64_Sym));
symtab = AppendSection(elf, ".symtab", SHT_SYMTAB, 0);
for (i = 0; i < ARRAYLEN(elf->syms); ++i) {
size = elf->syms[i]->i * sizeof(Elf64_Sym);
memcpy(elfwriter_reserve(elf, size), elf->syms[i]->p, size);
elfwriter_commit(elf, size);
}
FinishSection(elf);
elf->shdrs->p[symtab].sh_link = FlushStrtab(elf, ".strtab", elf->strtab);
elf->ehdr->e_shstrndx = FlushStrtab(elf, ".shstrtab", elf->shstrtab);
WriteRelaSections(elf, symtab);
size = elf->shdrs->i * sizeof(elf->shdrs->p[0]);
elfwriter_align(elf, alignof(elf->shdrs->p[0]), sizeof(elf->shdrs->p[0]));
elf->ehdr->e_shoff = elf->wrote;
elf->ehdr->e_shnum = elf->shdrs->i;
elf->shdrs->p[symtab].sh_info =
elf->syms[kElfWriterSymSection]->i + elf->syms[kElfWriterSymLocal]->i;
mempcpy(elfwriter_reserve(elf, size), elf->shdrs->p, size);
elfwriter_commit(elf, size);
}
struct ElfWriter *elfwriter_open(const char *path, int mode) {
struct ElfWriter *elf;
CHECK_NOTNULL((elf = calloc(1, sizeof(struct ElfWriter))));
CHECK_NOTNULL((elf->path = strdup(path)));
CHECK_NE(-1, asprintf(&elf->tmppath, "%s.%d", elf->path, getpid()));
CHECK_NE(-1, (elf->fd = open(elf->tmppath,
O_CREAT | O_TRUNC | O_RDWR | O_EXCL, mode)));
CHECK_NE(-1, ftruncate(elf->fd, (elf->mapsize = FRAMESIZE)));
CHECK_NE(MAP_FAILED, (elf->map = mmap((void *)(intptr_t)kFixedMappingsStart,
elf->mapsize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED, elf->fd, 0)));
elf->ehdr = memcpy(elf->map, &kObjHeader, (elf->wrote = sizeof(kObjHeader)));
elf->strtab = newinterner();
elf->shstrtab = newinterner();
return elf;
}
void elfwriter_close(struct ElfWriter *elf) {
size_t i;
FlushTables(elf);
CHECK_NE(-1, munmap(elf->map, elf->mapsize));
CHECK_NE(-1, ftruncate(elf->fd, elf->wrote));
CHECK_NE(-1, close(elf->fd));
CHECK_NE(-1, rename(elf->tmppath, elf->path));
freeinterner(elf->shstrtab);
freeinterner(elf->strtab);
free(elf->shdrs->p);
free(elf->relas->p);
for (i = 0; i < ARRAYLEN(elf->syms); ++i) free(elf->syms[i]->p);
free(elf);
}
void elfwriter_align(struct ElfWriter *elf, size_t addralign, size_t entsize) {
elf->entsize = entsize;
elf->addralign = addralign;
elf->wrote = roundup(elf->wrote, addralign);
}
size_t elfwriter_startsection(struct ElfWriter *elf, const char *name,
int sh_type, int sh_flags) {
size_t shdr = AppendSection(elf, name, sh_type, sh_flags);
AppendSymbol(elf, "",
sh_type != SHT_NULL ? ELF64_ST_INFO(STB_LOCAL, STT_SECTION)
: ELF64_ST_INFO(STB_LOCAL, STT_NOTYPE),
STV_DEFAULT, 0, 0, shdr, kElfWriterSymSection);
return shdr;
}
void *elfwriter_reserve(struct ElfWriter *elf, size_t size) {
size_t need, greed;
need = elf->wrote + size;
greed = elf->mapsize;
if (need > greed) {
do {
greed = greed + (greed >> 1);
} while (need > greed);
greed = roundup(greed, FRAMESIZE);
CHECK_NE(-1, ftruncate(elf->fd, greed));
CHECK_NE(MAP_FAILED, mmap((char *)elf->map + elf->mapsize,
greed - elf->mapsize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED, elf->fd, elf->mapsize));
elf->mapsize = greed;
}
return (char *)elf->map + elf->wrote;
}
void elfwriter_commit(struct ElfWriter *elf, size_t size) {
elf->wrote += size;
}
void elfwriter_finishsection(struct ElfWriter *elf) {
size_t section = FinishSection(elf);
if (elf->relas->j < elf->relas->i) MakeRelaSection(elf, section);
}
struct ElfWriterSymRef elfwriter_appendsym(struct ElfWriter *elf,
const char *name, int st_info,
int st_other, size_t st_value,
size_t st_size) {
return AppendSymbol(
elf, name, st_info, st_other, st_value, st_size, elf->shdrs->i - 1,
ELF64_ST_BIND(st_info) == STB_LOCAL ? kElfWriterSymLocal
: kElfWriterSymGlobal);
}
struct ElfWriterSymRef elfwriter_linksym(struct ElfWriter *elf,
const char *name, int st_info,
int st_other) {
return AppendSymbol(elf, name, st_info, st_other, 0, 0, 0,
kElfWriterSymGlobal);
}
void elfwriter_appendrela(struct ElfWriter *elf, uint64_t r_offset,
struct ElfWriterSymRef symkey, uint32_t type,
int64_t r_addend) {
CHECK_NE(-1,
append(elf->relas, (&(struct ElfWriterRela){.type = type,
.symkey = symkey,
.offset = r_offset,
.addend = r_addend})));
}

View file

@ -0,0 +1,67 @@
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_ELFWRITER_H_
#define COSMOPOLITAN_TOOL_BUILD_LIB_ELFWRITER_H_
#include "libc/elf/struct/ehdr.h"
#include "libc/elf/struct/rela.h"
#include "libc/elf/struct/shdr.h"
#include "libc/elf/struct/sym.h"
#include "tool/build/lib/interner.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct ElfWriter {
char *path;
char *tmppath;
int fd;
void *map;
size_t mapsize;
size_t wrote;
size_t entsize;
size_t addralign;
struct Elf64_Ehdr *ehdr;
struct {
size_t i, n;
Elf64_Shdr *p;
} shdrs[1];
struct ElfWriterSyms {
size_t i, n;
Elf64_Sym *p;
} syms[3][1];
struct {
size_t i, j, n;
struct ElfWriterRela {
uint64_t offset;
struct ElfWriterSymRef {
enum ElfWriterSymOrder {
kElfWriterSymSection,
kElfWriterSymLocal,
kElfWriterSymGlobal
} slg;
uint32_t sym;
} symkey;
uint32_t type;
int64_t addend;
} * p;
} relas[1];
struct Interner *strtab;
struct Interner *shstrtab;
};
struct ElfWriter *elfwriter_open(const char *, int) nodiscard;
void elfwriter_cargoculting(struct ElfWriter *);
void elfwriter_close(struct ElfWriter *);
void elfwriter_align(struct ElfWriter *, size_t, size_t);
size_t elfwriter_startsection(struct ElfWriter *, const char *, int, int);
void *elfwriter_reserve(struct ElfWriter *, size_t);
void elfwriter_commit(struct ElfWriter *, size_t);
void elfwriter_finishsection(struct ElfWriter *);
void elfwriter_appendrela(struct ElfWriter *, uint64_t, struct ElfWriterSymRef,
uint32_t, int64_t);
struct ElfWriterSymRef elfwriter_linksym(struct ElfWriter *, const char *, int,
int);
struct ElfWriterSymRef elfwriter_appendsym(struct ElfWriter *, const char *,
int, int, size_t, size_t);
void elfwriter_yoink(struct ElfWriter *, const char *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_ELFWRITER_H_ */

View file

@ -0,0 +1,31 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/elf/def.h"
#include "tool/build/lib/elfwriter.h"
void elfwriter_cargoculting(struct ElfWriter *elf) {
elfwriter_startsection(elf, "", SHT_NULL, 0);
elfwriter_startsection(elf, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
elfwriter_finishsection(elf);
elfwriter_startsection(elf, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
elfwriter_finishsection(elf);
elfwriter_startsection(elf, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
elfwriter_finishsection(elf);
}

View file

@ -0,0 +1,33 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/str/str.h"
#include "tool/build/lib/elfwriter.h"
void elfwriter_yoink(struct ElfWriter *elf, const char *symbol) {
unsigned char *p;
struct ElfWriterSymRef sym;
const unsigned char nopl[8] = "\x0f\x1f\x04\x25\x00\x00\x00\x00";
p = elfwriter_reserve(elf, 8);
memcpy(p, nopl, sizeof(nopl));
sym = elfwriter_linksym(elf, symbol, ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT),
STV_HIDDEN);
elfwriter_appendrela(elf, sizeof(nopl) - 4, sym, R_X86_64_32, 0);
elfwriter_commit(elf, sizeof(nopl));
}

134
tool/build/lib/interner.c Normal file
View file

@ -0,0 +1,134 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/alg/arraylist.h"
#include "libc/bits/safemacros.h"
#include "libc/mem/mem.h"
#include "libc/str/knuthmultiplicativehash.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
#include "tool/build/lib/interner.h"
#define kInitialItems 16
struct InternerObject {
struct Interner pool;
size_t i, n;
struct InternerHash {
unsigned hash; /* 0 means empty */
unsigned index;
} * p;
};
static void rehash(struct InternerObject *it) {
size_t i, j, n, step;
struct InternerHash *p;
n = it->n;
p = it->p;
it->p = xcalloc((it->n <<= 1), sizeof(struct InternerHash));
for (i = 0; i < n; ++i) {
if (!p[i].hash) continue;
step = 0;
do {
j = (p[i].hash + step * (step + 1) / 2) & (it->n - 1);
step++;
} while (it->p[j].hash);
memcpy(&it->p[j], &p[i], sizeof(p[i]));
}
free_s(&p);
}
/**
* Creates new interner.
*/
struct Interner *newinterner(void) {
struct InternerObject *it;
it = xcalloc(1, sizeof(*it));
it->p = xcalloc((it->n = kInitialItems), sizeof(*it->p));
it->pool.p = xcalloc((it->pool.n = kInitialItems), sizeof(*it->pool.p));
return &it->pool;
}
/**
* Destroys interner.
*/
void freeinterner(struct Interner *t) {
struct InternerObject *it = (struct InternerObject *)t;
if (it) {
free(it->pool.p);
free(it->p);
free(it);
}
}
/**
* Returns number of unique items interned.
*/
size_t interncount(const struct Interner *t) {
struct InternerObject *it = (struct InternerObject *)t;
return it->i;
}
/**
* Interns object.
*
* @return index into 𝑡𝑝 holding equal item
* @note use consistent size w/ non-string items
*/
size_t internobj(struct Interner *t, const void *data, size_t size) {
struct InternerObject *it = (struct InternerObject *)t;
unsigned hash;
size_t i, step;
unsigned char *item;
step = 0;
item = data;
hash = max(1, KnuthMultiplicativeHash32(data, size));
do {
/* it is written that triangle probe halts iff i<n/2 && popcount(n)==1 */
i = (hash + step * (step + 1) / 2) & (it->n - 1);
if (it->p[i].hash == hash && it->p[i].index + size <= it->pool.n &&
memcmp(item, &it->pool.p[it->p[i].index], size) == 0) {
return it->p[i].index;
}
step++;
} while (it->p[i].hash);
if (++it->i == (it->n >> 1)) {
rehash(it);
step = 0;
do {
i = (hash + step * (step + 1) / 2) & (it->n - 1);
step++;
} while (it->p[i].hash);
}
it->p[i].hash = hash;
return (it->p[i].index = concat(&it->pool, item, size));
}
/**
* Interns string.
*
* The NUL-terminated string 𝑠 is concatenated to the relocatable
* double-NUL terminated string list 𝑡𝑝 with de-duplication and
* preservation of insertion order.
*
* @return index into 𝑡𝑝 holding string equal to 𝑠
*/
size_t intern(struct Interner *t, const char *s) {
return internobj(t, s, strlen(s) + 1);
}

20
tool/build/lib/interner.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_INTERNER_H_
#define COSMOPOLITAN_TOOL_BUILD_LIB_INTERNER_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct Interner {
size_t i; /* byte size of 𝑝 */
size_t n; /* byte capacity of 𝑝 */
char *p; /* will relocate */
};
struct Interner *newinterner(void) returnsnonnull paramsnonnull();
void freeinterner(struct Interner *);
size_t interncount(const struct Interner *) paramsnonnull();
size_t internobj(struct Interner *, const void *, size_t) paramsnonnull();
size_t intern(struct Interner *, const char *) paramsnonnull();
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_INTERNER_H_ */

123
tool/build/lib/persist.c Normal file
View file

@ -0,0 +1,123 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/iovec.h"
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/runtime/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) {
struct iovec *iov;
int i, n, fd, iovlen;
const char *tmp, *pad;
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)));
tmp = gc(xasprintf("%s.%d.%s", path, getpid(), "tmp"));
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 = open(tmp, O_CREAT | O_WRONLY | O_EXCL, 0644)), "%s", tmp);
CHECK_EQ(filesize, writev(fd, iov, iovlen));
CHECK_NE(-1, close(fd));
CHECK_NE(-1, rename(tmp, path), "%s", path);
}

22
tool/build/lib/persist.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_PERSIST_H_
#define COSMOPOLITAN_TOOL_BUILD_LIB_PERSIST_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct ObjectParam {
size_t size;
void *p;
uint32_t *magic;
int32_t *abi;
struct ObjectArrayParam {
size_t len;
size_t size;
void *pp;
} * arrays;
};
void PersistObject(const char *, size_t, const struct ObjectParam *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_PERSIST_H_ */