Redesign cosmocc toolchain

The `cosmocc` compiler is now being distributed as a self-contained
toolchain that's path-agnostic and it no longer requires you clone the
Cosmop repo to use it. The bin/ folder has been deleted from the mono
repo. The `fatcosmocc` command has been renamed to `cosmocc`. MacOS
support now works very well.
This commit is contained in:
Justine Tunney 2023-11-11 14:04:26 -08:00
parent 3802428026
commit 291103ad8d
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
71 changed files with 2437 additions and 1398 deletions

View file

@ -35,6 +35,7 @@
#include "libc/macros.internal.h"
#include "libc/mem/gc.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdckdint.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/msync.h"
@ -51,17 +52,6 @@
#define MRS_TPIDR_EL0 0xd53bd040u
#define MOV_REG(DST, SRC) (0xaa0003e0u | (SRC) << 16 | (DST))
const unsigned char kFatNops[8][8] = {
{}, //
{0x90}, // nop
{0x66, 0x90}, // xchg %ax,%ax
{0x0f, 0x1f, 0x00}, // nopl (%rax)
{0x0f, 0x1f, 0x40, 0x00}, // nopl 0x00(%rax)
{0x0f, 0x1f, 0x44, 0x00, 0x00}, // nopl 0x00(%rax,%rax,1)
{0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00}, // nopw 0x00(%rax,%rax,1)
{0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00}, // nopl 0x00000000(%rax)
};
static int mode;
static int fildes;
static char *symstrs;
@ -138,6 +128,70 @@ static void GetOpts(int argc, char *argv[]) {
}
}
// Official Intel Multibyte No-Operation Instructions. See
// Intel's Six Thousand Page Manual, Volume 2, Table 4-12:
// On "Recommended Multi-Byte Sequence of NOP Instruction"
static const unsigned char kNops[10][10] = {
{}, //
{/***/ /***/ 0x90}, // nop
{0x66, /***/ 0x90}, // xchg %ax,%ax
{/***/ 0x0f, 0x1f, 0000}, // nopl (%rax)
{/***/ 0x0f, 0x1f, 0100, /***/ 0}, // nopl 0x00(%rax)
{/***/ 0x0f, 0x1f, 0104, 0000, 0}, // nopl 0x00(%rax,%rax,1)
{0x66, 0x0f, 0x1f, 0104, 0000, 0}, // nopw 0x00(%rax,%rax,1)
{/***/ 0x0f, 0x1f, 0200, 0000, 0, 0, 0}, // nopl 0x00000000(%rax)
{/***/ 0x0F, 0x1F, 0204, 0000, 0, 0, 0, 0}, // nopl 0x00000000(%rax,%rax,1)
{0x66, 0x0F, 0x1F, 0204, 0000, 0, 0, 0, 0}, // nopw 0x00000000(%rax,%rax,1)
// osz map op modrm sib displacement //
};
/**
* Rewrites leading NOP instructions to have fewer instructions.
*
* For example, the following code:
*
* nop
* nop
* nop
* nop
* nop
* nop
* nop
* nop
* nop
* nop
* nop
* nop
* ret
* nop
* nop
*
* Would be morphed into the following:
*
* nopw 0x00000000(%rax,%rax,1)
* xchg %ax,%ax
* ret
* nop
* nop
*
* @param p points to memory region that shall be modified
* @param e points to end of memory region, i.e. `p + #bytes`
* @return p advanced past last morphed byte
*/
static unsigned char *CoalesceNops(unsigned char *p, const unsigned char *e) {
long n;
for (; p + 1 < e; p += n) {
if (p[0] != 0x90) break;
if (p[1] != 0x90) break;
for (n = 2; p + n < e; ++n) {
if (p[n] != 0x90) break;
if (n == ARRAYLEN(kNops) - 1) break;
}
memcpy(p, kNops[n], n);
}
return p;
}
static void CheckPrivilegedCrossReferences(void) {
unsigned long x;
const char *secname;
@ -202,40 +256,32 @@ static void RewriteTlsCode(void) {
* `STT_FUNC` and `st_size` must have the function's byte length.
*/
static void OptimizePatchableFunctionEntries(void) {
#ifdef __x86_64__
long i, n;
long i;
Elf64_Shdr *shdr;
unsigned char *p, *pe;
for (i = 0; i < symcount; ++i) {
if (!syms[i].st_size) continue;
if (ELF64_ST_TYPE(syms[i].st_info) != STT_FUNC) continue;
if (!(shdr = GetElfSectionHeaderAddress(elf, esize, syms[i].st_shndx))) {
Die("elf header overflow #3");
}
if (shdr->sh_type != SHT_PROGBITS) continue;
if (!(p = GetElfSectionAddress(elf, esize, shdr))) {
Die("elf header overflow #4");
}
if (syms[i].st_value < shdr->sh_addr) {
Die("elf symbol beneath section");
}
if ((syms[i].st_value - shdr->sh_addr > esize ||
(p += syms[i].st_value - shdr->sh_addr) >=
(unsigned char *)elf + esize) ||
(syms[i].st_size >= esize ||
(pe = p + syms[i].st_size) >= (unsigned char *)elf + esize)) {
Die("elf symbol overflow");
};
for (; p + 1 < pe; p += n) {
if (p[0] != 0x90) break;
if (p[1] != 0x90) break;
for (n = 2; p + n < pe && n < ARRAYLEN(kFatNops); ++n) {
if (p[n] != 0x90) break;
unsigned char *p;
Elf64_Addr sym_rva;
if (elf->e_machine == EM_NEXGEN32E) {
for (i = 0; i < symcount; ++i) {
if (!syms[i].st_size) continue;
if (ELF64_ST_TYPE(syms[i].st_info) != STT_FUNC) continue;
if (!(shdr = GetElfSectionHeaderAddress(elf, esize, syms[i].st_shndx))) {
Die("elf header overflow #3");
}
memcpy(p, kFatNops[n], n);
if (shdr->sh_type != SHT_PROGBITS) continue;
if (!(p = GetElfSectionAddress(elf, esize, shdr))) {
Die("elf section overflow");
}
if (ckd_sub(&sym_rva, syms[i].st_value, shdr->sh_addr)) {
Die("elf symbol beneath section");
}
if (sym_rva > esize - shdr->sh_offset || //
(p += sym_rva) >= (unsigned char *)elf + esize || //
syms[i].st_size >= esize - sym_rva) {
Die("elf symbol overflow");
}
CoalesceNops(p, p + syms[i].st_size);
}
}
#endif /* __x86_64__ */
}
/**

View file

@ -16,15 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "ape/relocations.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.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/mem/arraylist2.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/append.h"
@ -44,13 +37,38 @@ struct Visited {
};
char *output;
const char *prog;
struct Interner *visited;
void Visit(const char *);
static wontreturn void Die(const char *reason) {
tinyprint(2, prog, ": ", reason, "\n", NULL);
exit(1);
}
static wontreturn void DieSys(const char *thing) {
perror(thing);
exit(1);
}
static wontreturn void DieOom(void) {
Die("out of memory");
}
static void Appends(char **b, const char *s) {
if (appends(b, s) == -1) DieOom();
}
static void Appendd(char **b, const void *p, size_t n) {
if (appendd(b, p, n) == -1) DieOom();
}
size_t GetFdSize(int fd) {
struct stat st;
CHECK_NE(-1, fstat(fd, &st));
if (fstat(fd, &st)) {
DieSys("fstat");
}
return st.st_size;
}
@ -84,12 +102,11 @@ void Process(const char *p, const char *pe, const char *path, bool isheader) {
continue;
}
}
appendd(&output, p, p2 - p);
Appendd(&output, p, p2 - p);
}
if (noformat) {
appends(&output, "/* clang-format on */\n");
Appends(&output, "/* clang-format on */\n");
}
kprintf("finished%n");
}
void Visit(const char *path) {
@ -105,55 +122,40 @@ void Visit(const char *path) {
if (startswith(path, "libc/isystem/")) return;
isheader = endswith(path, ".h");
if (isheader && isinterned(visited, path)) return;
appends(&output, "\n\f\n/*!BEGIN ");
appends(&output, path);
appends(&output, " */\n\n");
Appends(&output, "\n\f\n/*!BEGIN ");
Appends(&output, path);
Appends(&output, " */\n\n");
intern(visited, path);
if ((fd = open(path, O_RDONLY)) == -1) {
fprintf(stderr, "error: %s: failed to open\n", path);
exit(1);
}
if ((fd = open(path, O_RDONLY)) == -1) DieSys(path);
if ((size = GetFdSize(fd))) {
kprintf("size 1 = %'zu%n", size);
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0)));
map = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) DieSys(path);
Process(map, map + size, path, isheader);
kprintf("size = %'zu%n", size);
CHECK_EQ(0, munmap(map, size), "p=%p z=%'zu path=%s", map, size, path);
if (munmap(map, size)) DieSys(path);
}
CHECK_EQ(0, close(fd));
}
ssize_t WriteAll(int fd, const char *p, size_t n) {
ssize_t rc;
size_t i, got;
for (i = 0; i < n;) {
rc = write(fd, p + i, n - i);
if (rc != -1) {
got = rc;
i += got;
} else if (errno != EINTR) {
return -1;
}
}
return i;
if (close(fd)) DieSys(path);
}
int main(int argc, char *argv[]) {
size_t bytes;
const char *src;
struct GetArgs ga;
ShowCrashReports();
prog = argv[0];
if (!prog) prog = "rollup";
visited = newinterner();
appends(&output, "#ifndef COSMOPOLITAN_H_\n");
appends(&output, "#define COSMOPOLITAN_H_\n");
Appends(&output, "#ifndef COSMOPOLITAN_H_\n");
Appends(&output, "#define COSMOPOLITAN_H_\n");
getargs_init(&ga, argv + 1);
while ((src = getargs_next(&ga))) {
Visit(src);
}
getargs_destroy(&ga);
appends(&output, "\n");
appends(&output, "#endif /* COSMOPOLITAN_H_ */\n");
CHECK_NE(-1, WriteAll(1, output, appendz(output).i));
Appends(&output, "\n");
Appends(&output, "#endif /* COSMOPOLITAN_H_ */\n");
bytes = appendz(output).i;
if (write(1, output, bytes) != bytes) {
DieSys(prog);
}
freeinterner(visited);
return 0;
}