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

82
tool/build/build.mk Normal file
View file

@ -0,0 +1,82 @@
#-*-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
TOOL_BUILD_FILES := $(wildcard tool/build/*)
TOOL_BUILD_SRCS = $(filter %.c,$(TOOL_BUILD_FILES))
TOOL_BUILD_HDRS = $(filter %.h,$(TOOL_BUILD_FILES))
TOOL_BUILD_COMS = $(TOOL_BUILD_OBJS:%.o=%.com)
TOOL_BUILD_BINS = $(TOOL_BUILD_COMS) $(TOOL_BUILD_COMS:%=%.dbg)
TOOL_BUILD_OBJS = \
$(TOOL_BUILD_SRCS:%=o/$(MODE)/%.zip.o) \
$(TOOL_BUILD_SRCS:%.c=o/$(MODE)/%.o)
TOOL_BUILD_LINK = \
$(TOOL_BUILD_DEPS) \
o/$(MODE)/tool/build/%.o \
$(CRT) \
$(APE)
TOOL_BUILD_DIRECTDEPS = \
DSP_CORE \
DSP_SCALE \
LIBC_ALG \
LIBC_CALLS \
LIBC_CALLS_HEFTY \
LIBC_CONV \
LIBC_DNS \
LIBC_ELF \
LIBC_FMT \
LIBC_LOG \
LIBC_TINYMATH \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_RAND \
LIBC_STUBS \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
LIBC_TIME \
LIBC_UNICODE \
LIBC_X \
TOOL_BUILD_LIB \
THIRD_PARTY_DTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_XED \
THIRD_PARTY_ZLIB \
THIRD_PARTY_STB
TOOL_BUILD_DEPS := \
$(call uniq,$(foreach x,$(TOOL_BUILD_DIRECTDEPS),$($(x))))
o/$(MODE)/tool/build/build.pkg: \
$(TOOL_BUILD_OBJS) \
$(foreach x,$(TOOL_BUILD_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/tool/build/%.com.dbg: \
$(TOOL_BUILD_DEPS) \
o/$(MODE)/tool/build/build.pkg \
o/$(MODE)/tool/build/%.o \
$(CRT) \
$(APE)
-@$(APELINK)
o/$(MODE)/tool/build/mkdeps.o: tool/build/mkdeps.c
-@ACTION=OBJECTIFY.c build/compile $(OBJECTIFY.c) $(OUTPUT_OPTION) $<
o/$(MODE)/tool/build/generatematrix.o \
o/$(MODE)/tool/build/mkdeps.o: \
OVERRIDE_COPTS += \
-O2
.PHONY: o/$(MODE)/tool/build
o/$(MODE)/tool/build: \
o/$(MODE)/tool/build/lib \
$(TOOL_BUILD_BINS) \
$(TOOL_BUILD_CHECKS)

230
tool/build/coefficients.c Normal file
View file

@ -0,0 +1,230 @@
/*-*- 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 "dsp/core/half.h"
#include "dsp/core/q.h"
#include "libc/conv/conv.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/x/x.h"
#include "third_party/dtoa/dtoa.h"
#include "third_party/getopt/getopt.h"
#define USAGE \
" [FLAGS] [INT|FLOAT...]\n\
\n\
Example:\n\
\n\
coefficients -n -- 1 4 6 4 1 # Gaussian Blur\n\
coefficients -L16 -H235 -- .299 .587 .114 # BT.601 RGBY\n\
\n\
Flags:\n\
-v verbose\n\
-n normalize\n\
-m FLEX explicit Q(bits)\n\
-L FLEX low data value [default 0]\n\
-H FLEX high data value [default 255]\n\
-? shows this information\n\
\n"
static struct Flags {
bool n;
long L, H, m;
} flags_ = {
.L = 0,
.H = 255,
};
static noreturn void PrintUsage(int rc, FILE *f) {
fprintf(f, "Usage: %s%s", program_invocation_name, USAGE);
exit(rc);
}
static void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "?nvrL:H:m:")) != -1) {
switch (opt) {
case 'v':
g_loglevel++;
break;
case 'n':
flags_.n = true;
break;
case 'L':
flags_.L = strtol(optarg, NULL, 0);
break;
case 'H':
flags_.H = strtol(optarg, NULL, 0);
break;
case 'm':
flags_.m = strtol(optarg, NULL, 0);
break;
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
static void *Normalize(int n, double A[static 8]) {
int i;
double sum, rnorm;
for (sum = i = 0; i < n; ++i) {
sum += A[i];
}
if (fabs(sum - 1) > DBL_MIN * n) {
rnorm = 1 / sum;
for (i = 0; i < n; ++i) {
A[i] *= rnorm;
}
}
return A;
}
static void GetLimits(int n, const long I[static 8], long m, long L, long H,
long res[2][2]) {
int i, j[8];
long x, p[2] = {L, H};
DCHECK(0 < n && n <= 8);
memset(res, 0, sizeof(long) * 2 * 2);
for (j[0] = 0; j[0] < ARRAYLEN(p); ++j[0]) {
for (j[1] = 0; j[1] < ARRAYLEN(p); ++j[1]) {
for (j[2] = 0; j[2] < ARRAYLEN(p); ++j[2]) {
for (j[3] = 0; j[3] < ARRAYLEN(p); ++j[3]) {
for (j[4] = 0; j[4] < ARRAYLEN(p); ++j[4]) {
for (j[5] = 0; j[5] < ARRAYLEN(p); ++j[5]) {
for (j[6] = 0; j[6] < ARRAYLEN(p); ++j[6]) {
for (j[7] = 0; j[7] < ARRAYLEN(p); ++j[7]) {
x = 0;
for (i = 0; i < ARRAYLEN(j); ++i) {
x += p[j[i]] * I[i];
if (x < res[0][0]) res[0][0] = x;
if (x > res[0][1]) res[0][1] = x;
}
x += 1l << (m - 1);
if (x < res[0][0]) res[0][0] = x;
if (x > res[0][1]) res[0][1] = x;
x >>= m;
if (x < res[1][0]) res[1][0] = x;
if (x > res[1][1]) res[1][1] = x;
}
}
}
}
}
}
}
}
}
static const char *GetFittingMachineWord(long L, long H) {
if (-128 <= L && H <= 127) return "int8";
if (0 <= L && H <= 255) return "uint8";
if (-0x8000 <= L && H <= 0x7fff) return "int16";
if (~0x7fffffff <= L && H <= 0x7fffffff) return "int32";
if (0 <= L && H <= 0xffffffff) return "uint32";
return "INT64";
}
static char *DescribeMachineWord(long L, long H) {
return xasprintf("%s[%,ld..%,ld]", GetFittingMachineWord(L, H), L, H);
}
static void ShowBetterCoefficients(int n, double C[static 8], long L, long H) {
long err, I[8], lim[2][2];
char buf[32], kAlphabet[] = "abcdefgh";
int i, j, m, emitted, count, indices[8];
CHECK_LT(L, H);
DCHECK(0 < n && n <= 8);
for (m = 2; m < 40; ++m) {
memset(I, 0, sizeof(I));
if (C[6] || C[7]) {
err = GetIntegerCoefficients8(I, C, m, L, H);
} else {
err = GetIntegerCoefficients(I, C, m, L, H);
}
GetLimits(n, I, m, L, H, lim);
for (count = i = 0; i < n; ++i) {
if (I[i]) {
indices[count++] = i;
}
}
if (count) {
emitted = 0;
if (m) emitted += printf("(");
for (i = 0; i < count; ++i) {
if (i) emitted += printf(" + ");
if (I[indices[i]] != 1) {
emitted += printf("%ld*%c", I[indices[i]], kAlphabet[indices[i]]);
} else {
emitted += printf("%c", kAlphabet[indices[i]]);
}
}
if (m) {
if (m > 1) {
emitted += printf(" + %ld", 1l << (m - 1));
}
emitted += printf(")>>%d", m);
}
printf("%*s", MAX(0, 80 - emitted), " ");
printf("/* %s %s %s ε=%,ld */\n", gc(DescribeMachineWord(L, H)),
gc(DescribeMachineWord(lim[0][0], lim[0][1])),
gc(DescribeMachineWord(lim[1][0], lim[1][1])), err);
}
}
}
static int ReadIdealCoefficients(double C[static 8], int sn, char *S[sn]) {
int i, n;
if ((n = MIN(8, sn)) > 0) {
C[0] = C[1] = C[2] = C[3] = C[4] = C[5] = 0;
for (i = 0; i < n; ++i) {
C[i] = strtod(S[i], NULL);
}
}
return n;
}
int ToolBuildCoefficients(int argc, char *argv[]) {
int n;
double C[8];
GetOpts(argc, argv);
setvbuf(stdout, malloc(PAGESIZE), _IOLBF, PAGESIZE);
if ((n = ReadIdealCoefficients(C, argc - optind, argv + optind))) {
if (flags_.n) Normalize(n, C);
ShowBetterCoefficients(n, C, flags_.L, flags_.H);
}
return 0;
}
int main(int argc, char *argv[]) {
g_loglevel = kLogWarn;
showcrashreports();
return ToolBuildCoefficients(argc, argv);
}

267
tool/build/img2code.c Normal file
View file

@ -0,0 +1,267 @@
/*-*- 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 "dsp/scale/scale.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/conv/conv.h"
#include "libc/fmt/bing.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/testlib/testlib.h"
#include "third_party/dtoa/dtoa.h"
#include "third_party/getopt/getopt.h"
#include "third_party/stb/stb_image.h"
#include "third_party/xed/x86.h"
#define USAGE \
" [FLAGS] [PATH]\n\
\n\
Example:\n\
\n\
img2code -cw79 -p2 foo.png\n\
\n\
Flags:\n\
-c compress range\n\
-o PATH out\n\
-r FLEX ratio\n\
-w FLEX new width\n\
-h FLEX new height\n\
-p FLEX pixel y ratio\n\
-? shows this information\n\
\n"
static struct Flags {
const char *o;
double r, p;
int w, h;
bool c;
} flags_ = {
.o = "-",
.p = 1,
};
static noreturn void PrintUsage(int rc, FILE *f) {
fprintf(f, "Usage: %s%s", program_invocation_name, USAGE);
exit(rc);
}
static void GetOpts(int *argc, char *argv[]) {
int opt;
if (*argc == 2 &&
(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) {
PrintUsage(EXIT_SUCCESS, stdout);
}
while ((opt = getopt(*argc, argv, "?co:r:w:h:p:")) != -1) {
switch (opt) {
case 'c':
flags_.c = true;
break;
case 'o':
flags_.o = optarg;
break;
case 'r':
flags_.r = strtod(optarg, NULL);
break;
case 'p':
flags_.p = strtod(optarg, NULL);
break;
case 'w':
flags_.w = strtol(optarg, NULL, 0);
break;
case 'h':
flags_.h = strtol(optarg, NULL, 0);
break;
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
if (optind == *argc) {
argv[(*argc)++] = "-";
}
}
static void GetOutputGeometry(long syn, long sxn, long *out_dyn, long *out_dxn,
double *out_ry, double *out_rx) {
double ry, rx;
long dyn, dxn;
if (!flags_.h && !flags_.w) {
if (flags_.r) {
ry = flags_.r * flags_.p;
rx = flags_.r;
} else {
ry = flags_.p;
rx = 1;
}
dyn = round(syn / ry);
dxn = round(sxn / rx);
} else if (flags_.w && !flags_.h) {
if (flags_.r) {
rx = 1. * sxn / flags_.w;
ry = flags_.r * flags_.p;
dxn = flags_.w;
dyn = round(syn / ry);
} else {
rx = 1. * sxn / flags_.w;
ry = flags_.p * rx;
dxn = flags_.w;
dyn = round(syn / ry);
}
} else if (flags_.h && !flags_.w) {
if (flags_.r) {
rx = flags_.r;
ry = flags_.p * syn / flags_.h;
dxn = flags_.w;
dyn = round(syn / ry);
} else {
ry = flags_.p * syn / flags_.h;
rx = ry;
dyn = flags_.h;
dxn = round(syn / rx);
}
} else {
ry = flags_.p;
rx = 1;
dyn = round(flags_.h / ry);
dxn = flags_.w;
}
*out_dyn = dyn;
*out_dxn = dxn;
*out_ry = ry;
*out_rx = rx;
}
static void *Deinterlace(long dcn, long dyn, long dxn,
unsigned char dst[dcn][dyn][dxn], long syw, long sxw,
long scw, const unsigned char src[syw][sxw][scw],
long syn, long sxn, long sy0, long sx0, long sc0) {
long y, x, c;
for (y = 0; y < dyn; ++y) {
for (x = 0; x < dxn; ++x) {
for (c = 0; c < dcn; ++c) {
dst[c][y][x] = src[sy0 + y][sx0 + x][sc0 + c];
}
}
}
return dst;
}
static void CompressRange(long cn, long yn, long xn,
unsigned char img[cn][yn][xn]) {
static const char R[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
double f;
long c, y, x, L, H;
L = R[0], H = R[strlen(R) - 1];
for (c = 0; c < cn; ++c) {
for (y = 0; y < yn; ++y) {
for (x = 0; x < xn; ++x) {
f = img[c][y][x];
f /= 255;
f *= H - L;
f += L;
img[c][y][x] = MIN(H, MAX(L, lround(f)));
}
}
}
}
static void PrintCode(FILE *f, long cn, long yn, long xn,
unsigned char img[cn][yn][xn]) {
long c, y, x;
if (flags_.c) CompressRange(cn, yn, xn, img);
for (c = 0; c < cn; ++c) {
fputc('\n', f);
for (y = 0; y < yn; ++y) {
fputc('\n', f);
for (x = 0; x < xn; ++x) {
fputwc(bing(img[c][y][x], 0), f);
}
}
}
fputc('\n', f);
}
static void ProcessImage(const char *path, long scn, long syn, long sxn,
unsigned char img[syn][sxn][scn], FILE *f) {
double ry, rx;
long dyn, dxn, cn;
GetOutputGeometry(syn, sxn, &dyn, &dxn, &ry, &rx);
if (dyn == syn && dxn == sxn) {
/* TODO(jart): Why doesn't Gyarados no-op? */
PrintCode(f, scn, dyn, dxn,
Deinterlace(scn, syn, sxn, gc(memalign(32, scn * syn * sxn)), syn,
sxn, scn, img, syn, sxn, 0, 0, 0));
} else {
PrintCode(
f, scn, dyn, dxn,
EzGyarados(scn, dyn, dxn, gc(memalign(32, scn * dyn * dxn)), scn, syn,
sxn,
Deinterlace(scn, syn, sxn, gc(memalign(32, scn * syn * sxn)),
syn, sxn, scn, img, syn, sxn, 0, 0, 0),
0, scn, dyn, dxn, syn, sxn, ry, rx, 0, 0));
}
}
static void WithImageFile(const char *path, FILE *f,
void fn(const char *path, long cn, long yn, long xn,
unsigned char src[yn][xn][cn], FILE *f)) {
struct stat st;
void *map, *data;
int fd, yn, xn, cn;
CHECK_NE(-1, (fd = open(path, O_RDONLY)), "%s", path);
CHECK_NE(-1, fstat(fd, &st));
CHECK_GT(st.st_size, 0);
CHECK_LE(st.st_size, INT_MAX);
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)));
CHECK_NOTNULL(
(data = stbi_load_from_memory(map, st.st_size, &xn, &yn, &cn, 0)), "%s",
path);
CHECK_NE(-1, munmap(map, st.st_size));
CHECK_NE(-1, close(fd));
fn(path, cn, yn, xn, data, f);
free(data);
}
int main(int argc, char *argv[]) {
int i;
FILE *f;
showcrashreports();
GetOpts(&argc, argv);
stbi_set_unpremultiply_on_load(true);
CHECK_NOTNULL((f = fopen(flags_.o, "w")));
for (i = optind; i < argc; ++i) {
WithImageFile(argv[i], f, ProcessImage);
}
return fclose(f);
}

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_ */

184
tool/build/lz4toasm.c Normal file
View file

@ -0,0 +1,184 @@
/*-*- 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/stat.h"
#include "libc/conv/conv.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/kompressor.h"
#include "libc/nexgen32e/lz4.h"
#include "libc/runtime/ezmap.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
/**
* @fileoverview LZ4 content embedder.
*
* This tool converts an LZ4-compressed file into assembly source code,
* that will appear to C/C++ code as a global constant holding the
* uncompressed contents, which is unpacked automatically.
*
* @note this is a build tool that assumes input data is trustworthy
*/
#define COLS 8
struct stat st_;
size_t extractedsize;
noreturn void usage(char *argv[], FILE *f, int rc) {
fprintf(f, "%s: %s [-o %s] [-s %s] %s\n", "Usage", argv[0], "PATH", "SYMBOL",
"FILE");
exit(rc);
}
int main(int argc, char *argv[]) {
const char *symbol = "kData";
const char *lz4path = "/dev/stdin";
const char *outpath = "/dev/stdout";
const char *initprio = "400,_init_";
const unsigned char *lz4data;
int opt;
FILE *fin, *fout;
showcrashreports();
while ((opt = getopt(argc, argv, "ho:s:z:")) != -1) {
switch (opt) {
case 's':
symbol = optarg;
break;
case 'o':
outpath = optarg;
break;
case 'z':
extractedsize = strtoul(optarg, NULL, 0);
break;
case 'h':
case '?':
usage(argv, stdout, 0);
default:
usage(argv, stderr, 1);
}
}
if (argc - optind) {
lz4path = argv[optind];
}
CHECK_NOTNULL((fin = fopen(lz4path, "r")));
CHECK_NE(-1, fstat(fileno(fin), &st_));
CHECK_NOTNULL((lz4data = malloc(st_.st_size)));
CHECK_EQ(1, fread(lz4data, st_.st_size, 1, fin));
CHECK_NE(-1, fclose(fin));
CHECK_NOTNULL((fout = fopen(outpath, "w")));
CHECK_EQ(LZ4_MAGICNUMBER, LZ4_MAGIC(lz4data));
CHECK_EQ(1, LZ4_FRAME_VERSION(lz4data));
const unsigned char *frame = /* lz4check( */ lz4data /* ) */;
const unsigned char *block1 = frame + LZ4_FRAME_HEADERSIZE(frame);
const unsigned char *block2 = block1 + LZ4_BLOCK_SIZE(frame, block1);
CHECK(LZ4_BLOCK_ISCOMPRESSED(block1));
CHECK(LZ4_BLOCK_ISEOF(block2));
const unsigned char *data = LZ4_BLOCK_DATA(block1);
size_t size = LZ4_BLOCK_DATASIZE(block1);
fprintf(fout,
"/\t%s -o %s -s %s %s\n"
".include \"libc/macros.inc\"\n"
"\n",
argv[0], outpath, symbol, lz4path);
if (!extractedsize) {
if (LZ4_FRAME_BLOCKCONTENTSIZEFLAG(frame)) {
extractedsize = LZ4_FRAME_BLOCKCONTENTSIZE(frame);
} else {
fprintf(stderr, "error: need extractedsize\n");
exit(1);
}
}
size_t bss = ROUNDUP(extractedsize, 8);
size_t misalign = bss - extractedsize;
fprintf(fout, "\t.rodata\n");
fprintf(fout, "\t.align\t4\n");
fprintf(fout, "%sLen:\n", symbol);
fprintf(fout, "\t.long\t%lu\n", extractedsize);
fprintf(fout, "\t.endobj\t%sLen,globl,hidden\n", symbol);
fprintf(fout, "\t.previous\n");
fprintf(fout, "\n");
fprintf(fout, "\t.initbss %s%s\n", initprio, symbol);
fprintf(fout, "%s:\n", symbol);
fprintf(fout, "\t.zero\t%lu\n", bss);
fprintf(fout, "\t.endobj\t%s,globl,hidden\n", symbol);
fprintf(fout, "\t.previous\n");
fprintf(fout, "\n");
fprintf(fout, "\t.init.start %s%s\n", initprio, symbol);
fprintf(fout, "\tpush\t%%rsi\n");
fprintf(fout, "\tmov\t$%u,%%edx\n", size);
fprintf(fout, "\tcall\tlz4cpy\n");
if (misalign) {
fprintf(fout, "\tlea\t%zu(%%rax),%%rdi\n", misalign);
} else {
fprintf(fout, "\tmov\t%%rax,%%rdi\n");
}
fprintf(fout, "\tpop\t%%rsi\n");
fprintf(fout, "\tadd\t$%u,%%rsi\n", ROUNDUP(size, 8));
fprintf(fout, "\t.init.end %s%s\n", initprio, symbol);
fprintf(fout, "\n");
fprintf(fout, "\t.initro %s%s\n", initprio, symbol);
fprintf(fout, "%sLz4:\n", symbol);
int col = 0;
char16_t glyphs[COLS + 1];
for (unsigned i = 0; i < size; ++i) {
unsigned char ch = data[i];
if (col == 0) {
fprintf(fout, "\t.byte\t");
memset(glyphs, 0, sizeof(glyphs));
}
/* TODO(jart): Fix Emacs */
glyphs[col] = kCp437[ch == '"' || ch == '#' ? '.' : ch];
if (col) fputc(',', fout);
fprintf(fout, "0x%02x", ch);
if (++col == COLS) {
col = 0;
fprintf(fout, "\t#%hs\n", glyphs);
}
}
while (col++ != COLS) {
fprintf(fout, ",0x00");
}
fprintf(fout, "\n");
fprintf(fout, "\t.endobj\t%sLz4,globl,hidden\n", symbol);
fprintf(fout, "\t.previous\n");
return fclose(fout);
}

314
tool/build/mkdeps.c Normal file
View file

@ -0,0 +1,314 @@
/*-*- 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/alg.h"
#include "libc/alg/arraylist.h"
#include "libc/alg/arraylist2.h"
#include "libc/alg/bisectcarleft.h"
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/runtime/ezmap.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/mappings.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/madv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
/**
* @fileoverview Make dependency generator.
*
* This program generates Makefile code saying which sources include
* which headers, thus improving build invalidation.
*
* The same thing can be accomplished using GCC's -M flag. This tool is
* much faster. It's designed to process over 9,000 sources to generate
* 50k+ lines of make code in ~80ms using one core and a meg of ram.
*/
static const char *const kSourceExts[] = {".s", ".S", ".c", ".cc", ".cpp"};
static alignas(16) const char kIncludePrefix[] = "include \"";
static const char *const kModelessPackages[] = {
"libc/nt/",
"libc/stubs/",
"libc/sysv/",
};
struct Strings {
size_t i, n;
char *p;
};
struct Source {
uint32_t hash; /* 0 means empty w/ triangle probe */
uint32_t name; /* strings.p[name] w/ interning */
uint32_t id; /* rehashing changes indexes */
};
struct Edge {
int32_t from; /* sources.p[from.id] */
int32_t to; /* sources.p[to.id] */
};
struct Sources {
size_t i, n; /* phase 1: hashmap: popcount(n)==1 if n */
struct Source *p; /* phase 2: arraylist sorted by id */
};
struct Edges {
size_t i, n;
struct Edge *p;
};
int g_sourceid;
struct Strings strings;
struct Sources sources;
struct Edges edges;
const char *buildroot;
int *visited;
char *out;
FILE *fout;
static int CompareSourcesById(struct Source *a, struct Source *b) {
return a->id > b->id ? 1 : a->id < b->id ? -1 : 0;
}
static int CompareEdgesByFrom(struct Edge *a, struct Edge *b) {
return a->from > b->from ? 1 : a->from < b->from ? -1 : 0;
}
static uint32_t Hash(const void *s, size_t l) {
return max(1, crc32c(0, s, l));
}
void Crunch(void) {
size_t i, j;
for (i = 0, j = 0; j < sources.n; ++j) {
if (!sources.p[j].hash) continue;
if (i != j) memcpy(&sources.p[i], &sources.p[j], sizeof(sources.p[j]));
i++;
}
sources.i = i;
qsort(sources.p, sources.i, sizeof(*sources.p), (void *)CompareSourcesById);
qsort(edges.p, edges.i, sizeof(*edges.p), (void *)CompareEdgesByFrom);
}
void Rehash(void) {
size_t i, j, step;
struct Sources old;
memcpy(&old, &sources, sizeof(sources));
sources.n = sources.n ? sources.n << 1 : 16;
sources.p = calloc(sources.n, sizeof(struct Source));
for (i = 0; i < old.n; ++i) {
if (!old.p[i].hash) continue;
step = 0;
do {
j = (old.p[i].hash + step * (step + 1) / 2) & (sources.n - 1);
step++;
} while (sources.p[j].hash);
memcpy(&sources.p[j], &old.p[i], sizeof(old.p[i]));
}
free(old.p);
}
uint32_t GetSourceId(const char *name, size_t len) {
size_t i, step;
uint32_t hash = Hash(name, len);
if (sources.n) {
step = 0;
do {
i = (hash + step * (step + 1) / 2) & (sources.n - 1);
if (sources.p[i].hash == hash &&
memcmp(name, &strings.p[sources.p[i].name], len) == 0) {
return sources.p[i].id;
}
step++;
} while (sources.p[i].hash);
}
if (++sources.i >= (sources.n >> 1)) {
Rehash();
step = 0;
do {
i = (hash + step * (step + 1) / 2) & (sources.n - 1);
step++;
} while (sources.p[i].hash);
}
sources.p[i].hash = hash;
sources.p[i].name = concat(&strings, name, len);
strings.p[strings.i++] = '\0';
return (sources.p[i].id = g_sourceid++);
}
void LoadRelationships(int argc, char *argv[]) {
struct MappedFile mf;
const char *p, *pe;
size_t i, linecap = 0;
char *line = NULL;
FILE *finpaths;
for (i = optind; i < argc; ++i) {
CHECK_NOTNULL((finpaths = fopen(argv[i], "r")));
while (getline(&line, &linecap, finpaths) != -1) {
if (mapfileread(chomp(line), &mf) == -1) {
CHECK_EQ(ENOENT, errno, "%s", line);
/*
* This code helps GNU Make automatically fix itself when we
* delete a source file. It removes o/.../srcs.txt or
* o/.../hdrs.txt and exits nonzero. Since we use hyphen
* notation on mkdeps related rules, the build will
* automatically restart itself.
*/
fprintf(stderr, "%s %s...\n", "Refreshing", argv[i]);
unlink(argv[i]);
exit(1);
}
if (mf.size) {
if (mf.size > PAGESIZE) {
madvise(mf.addr, mf.size, MADV_WILLNEED | MADV_SEQUENTIAL);
}
uint32_t sauce = GetSourceId(line, strlen(line));
size_t inclen = strlen(kIncludePrefix);
p = mf.addr;
pe = p + mf.size;
while ((p = strstr(p, kIncludePrefix))) {
const char *path = p + inclen;
const char *pathend = memchr(path, '"', pe - path);
if (pathend && (intptr_t)p > (intptr_t)mf.addr &&
(p[-1] == '#' || p[-1] == '.') &&
(p - 1 == mf.addr || p[-2] == '\n')) {
uint32_t dependency = GetSourceId(path, pathend - path);
struct Edge edge;
edge.from = sauce;
edge.to = dependency;
append(&edges, &edge);
}
p = path;
}
}
CHECK_NE(-1, unmapfile(&mf));
}
CHECK_NE(-1, fclose(finpaths));
}
free_s(&line);
}
void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "ho:r:")) != -1) {
switch (opt) {
case 'o':
out = optarg;
break;
case 'r':
buildroot = optarg;
break;
default:
fprintf(stderr, "%s: %s [-r %s] [-o %s] [%s...]\n", "Usage", argv[0],
"BUILDROOT", "OUTPUT", "PATHSFILE");
exit(1);
}
}
if (isempty(out)) fprintf(stderr, "need -o FILE"), exit(1);
if (isempty(buildroot)) fprintf(stderr, "need -r o/$(MODE)"), exit(1);
}
const char *StripExt(const char *s) {
static bool once;
static size_t i, n;
static char *p, *dot;
if (!once) {
once = true;
__cxa_atexit(free_s, &p, NULL);
}
i = 0;
CONCAT(&p, &i, &n, s, strlen(s));
dot = strrchr(p, '.');
if (dot) *dot = '\0';
return p;
}
bool IsObjectSource(const char *name) {
for (size_t i = 0; i < ARRAYLEN(kSourceExts); ++i) {
if (endswith(name, kSourceExts[i])) return true;
}
return false;
}
void Dive(uint32_t sauce) {
for (uint32_t i = bisectcarleft((const int32_t(*)[2])edges.p, edges.i, sauce);
edges.p[i].from == sauce; ++i) {
int32_t dep = edges.p[i].to;
if (bts(visited, dep)) continue;
fputs(" \\\n\t", fout);
fputs(&strings.p[sources.p[dep].name], fout);
Dive(dep);
}
}
bool IsModeless(const char *path) {
size_t i;
for (i = 0; i < ARRAYLEN(kModelessPackages); ++i) {
if (startswith(path, kModelessPackages[i])) return true;
}
return false;
}
int main(int argc, char *argv[]) {
out = "/dev/stdout";
GetOpts(argc, argv);
char *tmp =
!fileexists(out) || isregularfile(out) ? xasprintf("%s.tmp", out) : NULL;
CHECK_NOTNULL((fout = fopen(tmp ? tmp : out, "w")));
LoadRelationships(argc, argv);
Crunch();
size_t bitmaplen = roundup((sources.i + 8) / 8, 4);
visited = malloc(bitmaplen);
for (size_t i = 0; i < sources.i; ++i) {
const char *path = &strings.p[sources.p[i].name];
if (!IsObjectSource(path)) continue;
bool needprefix = !startswith(path, "o/");
const char *prefix = !needprefix ? "" : IsModeless(path) ? "o/" : buildroot;
fprintf(fout, "\n%s%s.o: \\\n\t%s", prefix, StripExt(path), path);
memset(visited, 0, bitmaplen);
bts(visited, i);
Dive(i);
fprintf(fout, "\n");
}
if (fclose(fout) == -1) perror(out), exit(1);
if (tmp) {
if (rename(tmp, out) == -1) perror(out), exit(1);
}
free_s(&strings.p);
free_s(&sources.p);
free_s(&edges.p);
free_s(&visited);
free_s(&tmp);
return 0;
}

665
tool/build/package.c Normal file
View file

@ -0,0 +1,665 @@
/*-*- 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/alg.h"
#include "libc/alg/arraylist.h"
#include "libc/alg/bisect.h"
#include "libc/alg/bisectcarleft.h"
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/conv/conv.h"
#include "libc/conv/sizemultiply.h"
#include "libc/elf/def.h"
#include "libc/elf/elf.h"
#include "libc/elf/struct/rela.h"
#include "libc/errno.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/nexgen32e/kompressor.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
#include "third_party/xed/x86.h"
#include "third_party/zlib/zlib.h"
#include "tool/build/lib/elfwriter.h"
#include "tool/build/lib/persist.h"
/**
* @fileoverview Build Package Script.
*
* FIRST PURPOSE
*
* This script verifies the well-formedness of dependencies, e.g.
*
* o/tool/build/package.com \
* -o o/libc/stubs/stubs.pkg \
* o/libc/stubs/{a,b,...}.o
*
* o/tool/build/package.com \
* -o o/libc/nexgen32e/nexgen32e.pkg \
* -d o/libc/stubs/stubs.pkg \
* o/libc/nexgen32e/{a,b,...}.o
*
* We want the following:
*
* 1. FOO declares in FOO_DIRECTDEPS where its undefined symbols are.
* 2. FOO_DIRECTDEPS is complete, so FOO FOO_DIRECTDEPS has no UNDEFs.
* 3. FOO_DIRECTDEPS is non-transitive; thus this tool is incremental.
* 4. Package relationships on a whole are acyclic.
*
* These rules help keep the structure of large codebases easy to
* understand. More importantly, it allows us to further optimize
* compiled objects very cheaply as the build progresses.
*
* SECOND PURPOSE
*
* We want all storage to be thread-local storage. So we change
* RIP-relative instructions to be RBX-relative, only when they
* reference sections in the binary mutable after initialization.
*
* This is basically what the Go language does to implement its fiber
* multiprocessing model. We can have this in C by appropriating all the
* work folks put into enriching GNU C with WIN32 and ASLR lool.
*
* THIRD PURPOSE
*
* Compress read-only data sections of particularly low entropy, using
* the most appropriate directly-linked algorithm and then inject code
* into _init() that calls it. If the data is extremely low energy, we
* will inject code for merging page table entries too. The overcommit
* here is limitless.
*/
#define PACKAGE_MAGIC bswap_32(0xBEEFBEEFu)
#define PACKAGE_ABI 1
struct Packages {
size_t i, n;
struct Package {
uint32_t magic;
int32_t abi;
uint32_t path; /* pkg->strings.p[path] */
int64_t fd; /* not persisted */
void *addr; /* not persisted */
size_t size; /* not persisted */
struct Strings {
size_t i, n;
char *p; /* persisted as pkg+RVA */
} strings; /* TODO(jart): interning? */
struct Objects {
size_t i, n;
struct Object {
uint32_t path; /* pkg->strings.p[path] */
int64_t fd; /* 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;
struct Ops {
size_t i, n;
struct Op {
int32_t offset;
uint8_t decoded_length;
uint8_t pos_disp;
uint16_t __pad;
} * p;
} ops;
} * p;
} sections; /* not persisted */
} * p; /* persisted as pkg+RVA */
} objects;
struct Symbols {
size_t i, n;
struct Symbol {
uint32_t name; /* pkg->strings.p[name] */
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 */
};
int CompareSymbolName(const struct Symbol *a, const struct Symbol *b,
const char *strs[hasatleast 2]) {
return strcmp(&strs[0][a->name], &strs[1][b->name]);
}
struct Package *LoadPackage(const char *path) {
int fd;
ssize_t i;
struct stat st;
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)));
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);
CHECK(strcmp(path, &pkg->strings.p[pkg->path]) == 0,
"corrupt package: %`'s pkg=%p strings=%p", path, pkg, pkg->strings.p);
pkg->addr = pkg;
pkg->size = st.st_size;
return pkg;
}
void AddDependency(struct Packages *deps, const char *path) {
struct Package *pkg;
pkg = LoadPackage(path);
CHECK_NE(-1, append(deps, &pkg));
}
void WritePackage(struct Package *pkg) {
CHECK_NE(0, PACKAGE_MAGIC);
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},
},
});
}
void GetOpts(struct Package *pkg, struct Packages *deps, int argc,
char *argv[]) {
long i, si, opt;
pkg->path = -1;
while ((opt = getopt(argc, argv, "vho:d:")) != -1) {
switch (opt) {
case 'v':
g_loglevel = kLogDebug;
break;
case 'o':
pkg->path = concat(&pkg->strings, optarg, strlen(optarg) + 1);
break;
case 'd':
AddDependency(deps, optarg);
break;
default:
fprintf(stderr, "%s: %s [%s %s] [%s %s] %s\n", "Usage",
program_invocation_name, "-o", "OUTPACKAGE", "-d", "DEPPACKAGE",
"OBJECT...");
exit(1);
}
}
CHECK_NE(-1, pkg->path);
CHECK_LT(optind, argc,
"no objects passed to package.com; "
"is your foo.mk $(FOO_OBJS) glob broken?");
for (i = optind; i < argc; ++i) {
CHECK_NE(-1, (si = concat(&pkg->strings, argv[i], strlen(argv[i]) + 1)));
CHECK_NE(-1, append(&pkg->objects, (&(struct Object){si})));
}
}
void IndexSections(struct Object *obj) {
size_t i;
struct Op op;
const char *name;
const uint8_t *code;
struct Section sect;
const Elf64_Shdr *shdr;
struct XedDecodedInst xedd;
for (i = 0; i < obj->elf->e_shnum; ++i) {
memset(&sect, 0, 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 {
sect.kind = kText;
}
} else {
sect.kind = kUndef; /* should always and only be section #0 */
}
if (shdr->sh_flags & SHF_EXECINSTR) {
CHECK_NOTNULL((code = getelfsectionaddress(obj->elf, obj->size, shdr)));
for (op.offset = 0; op.offset < shdr->sh_size;
op.offset += op.decoded_length) {
if (xed_instruction_length_decode(
xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64),
&code[op.offset],
min(shdr->sh_size - op.offset, XED_MAX_INSTRUCTION_BYTES)) ==
XED_ERROR_NONE) {
op.decoded_length = xedd.decoded_length;
op.pos_disp = xedd.operands.pos_disp;
} else {
op.decoded_length = 1;
op.pos_disp = 0;
}
CHECK_NE(-1, append(&sect.ops, &op));
}
}
CHECK_NE(-1, append(&obj->sections, &sect));
}
}
enum SectionKind ClassifySection(struct Object *obj, uint8_t type,
Elf64_Section shndx) {
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;
}
void LoadSymbols(struct Package *pkg, uint32_t object) {
Elf64_Xword i;
const char *name;
struct Object *obj;
struct Symbol symbol;
obj = &pkg->objects.p[object];
symbol.object = object;
for (i = 0; i < obj->symcount; ++i) {
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));
}
}
}
}
void OpenObject(struct Package *pkg, struct Object *obj, int mode, int prot,
int flags) {
int fd;
struct stat st;
CHECK_NE(-1, (fd = open(&pkg->strings.p[obj->path], 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)));
CHECK_NE(-1, close(fd));
CHECK(iself64binary(obj->elf, obj->size));
CHECK_NOTNULL((obj->strs = getelfstringtable(obj->elf, obj->size)));
CHECK_NOTNULL(
(obj->syms = getelfsymboltable(obj->elf, obj->size, &obj->symcount)));
CHECK_NE(0, obj->symcount);
IndexSections(obj);
}
void CloseObject(struct Object *obj) {
CHECK_NE(-1, munmap(obj->elf, obj->size));
}
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);
LoadSymbols(pkg, i);
CloseObject(obj);
}
qsort_r(&pkg->symbols.p[0], pkg->symbols.i, sizeof(pkg->symbols.p[0]),
(void *)CompareSymbolName,
(const char *[2]){pkg->strings.p, pkg->strings.p});
}
bool FindSymbol(const char *name, struct Package *pkg,
struct Packages *directdeps, struct Package **out_pkg,
struct Symbol **out_sym) {
size_t i;
struct Package *dep;
struct Symbol key, *sym;
key.name = 0;
if ((sym = bisect(&key, &pkg->symbols.p[0], pkg->symbols.i,
sizeof(pkg->symbols.p[0]), (void *)CompareSymbolName,
(const char *[2]){name, pkg->strings.p}))) {
if (out_pkg) *out_pkg = pkg;
if (out_sym) *out_sym = sym;
return true;
}
for (i = 0; i < directdeps->i; ++i) {
dep = directdeps->p[i];
if ((sym = bisect(&key, &dep->symbols.p[0], dep->symbols.i,
sizeof(dep->symbols.p[0]), (void *)CompareSymbolName,
(const char *[2]){name, dep->strings.p}))) {
if (out_pkg) *out_pkg = dep;
if (out_sym) *out_sym = sym;
return true;
}
}
return false;
}
void CheckStrictDeps(struct Package *pkg, struct Packages *deps) {
size_t i, j;
struct Package *dep;
struct Symbol *undef;
for (i = 0; i < pkg->undefs.i; ++i) {
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]);
for (j = 0; j < deps->i; ++j) {
dep = deps->p[j];
fputc('\t', stderr);
fputs(&dep->strings.p[dep->path], stderr);
fputc('\n', stderr);
}
exit(1);
}
}
free(pkg->undefs.p);
memset(&pkg->undefs, 0, sizeof(pkg->undefs));
}
forceinline bool IsRipRelativeModrm(uint8_t modrm) {
return (modrm & 0b11000111) == 0b00000101;
}
forceinline uint8_t ChangeRipToRbx(uint8_t modrm) {
return (modrm & 0b00111000) | 0b10000011;
}
void OptimizeRelocations(struct Package *pkg, struct Packages *deps,
struct Object *obj) {
Elf64_Half i;
struct Op *op;
Elf64_Rela *rela;
struct Symbol *refsym;
struct Package *refpkg;
unsigned char *code, *p;
Elf64_Shdr *shdr, *shdrcode;
for (i = 0; i < obj->elf->e_shnum; ++i) {
shdr = getelfsectionheaderaddress(obj->elf, obj->size, i);
if (shdr->sh_type == SHT_RELA) {
CHECK_EQ(sizeof(struct Elf64_Rela), shdr->sh_entsize);
CHECK_NOTNULL((shdrcode = getelfsectionheaderaddress(obj->elf, obj->size,
shdr->sh_info)));
if (!(shdrcode->sh_flags & SHF_EXECINSTR)) continue;
CHECK_NOTNULL(
(code = getelfsectionaddress(obj->elf, obj->size, shdrcode)));
for (rela = getelfsectionaddress(obj->elf, obj->size, shdr);
((uintptr_t)rela + shdr->sh_entsize <=
min((uintptr_t)obj->elf + obj->size,
(uintptr_t)obj->elf + shdr->sh_offset + shdr->sh_size));
++rela) {
CHECK_LT(ELF64_R_SYM(rela->r_info), obj->symcount);
#if 0
/*
* Change (%rip) to (%rbx) on program instructions that
* reference memory, if and only if the memory location is a
* global variable that's mutable after initialization. The
* displacement is also updated to be relative to the image
* base, rather than relative to the program counter.
*/
if ((ELF64_R_TYPE(rela->r_info) == R_X86_64_PC32 ||
ELF64_R_TYPE(rela->r_info) == R_X86_64_GOTPCREL) &&
FindSymbol(
getelfstring(obj->elf, obj->size, obj->strs,
obj->syms[ELF64_R_SYM(rela->r_info)].st_name),
pkg, deps, &refpkg, &refsym) &&
(refsym->kind == kData || refsym->kind == kBss) &&
IsRipRelativeModrm(code[rela->r_offset - 1])) {
op = &obj->sections.p[shdr->sh_info].ops.p[bisectcarleft(
(const int32_t(*)[2])obj->sections.p[shdr->sh_info].ops.p,
obj->sections.p[shdr->sh_info].ops.i, rela->r_offset)];
CHECK_GT(op->decoded_length, 4);
CHECK_GT(op->pos_disp, 0);
rela->r_info = ELF64_R_INFO(ELF64_R_SYM(rela->r_info), R_X86_64_32S);
rela->r_addend = -IMAGE_BASE_VIRTUAL + rela->r_addend +
(op->decoded_length - op->pos_disp);
code[rela->r_offset - 1] = ChangeRipToRbx(code[rela->r_offset - 1]);
}
#endif
/*
* GCC isn't capable of -mnop-mcount when using -fpie.
* Let's fix that. It saves ~14 cycles per function call.
* Then libc/runtime/ftrace.greg.c morphs it back at runtime.
*/
if (ELF64_R_TYPE(rela->r_info) == R_X86_64_GOTPCRELX &&
strcmp(getelfstring(obj->elf, obj->size, obj->strs,
obj->syms[ELF64_R_SYM(rela->r_info)].st_name),
"mcount") == 0) {
rela->r_info = R_X86_64_NONE;
p = &code[rela->r_offset - 2];
p[0] = 0x66; /* nopw 0x00(%rax,%rax,1) */
p[1] = 0x0f;
p[2] = 0x1f;
p[3] = 0x44;
p[4] = 0x00;
p[5] = 0x00;
}
/*
* Let's just try to nop mcount calls in general due to the above.
*/
if ((ELF64_R_TYPE(rela->r_info) == R_X86_64_PC32 ||
ELF64_R_TYPE(rela->r_info) == R_X86_64_PLT32) &&
strcmp(getelfstring(obj->elf, obj->size, obj->strs,
obj->syms[ELF64_R_SYM(rela->r_info)].st_name),
"mcount") == 0) {
rela->r_info = R_X86_64_NONE;
p = &code[rela->r_offset - 1];
p[0] = 0x0f; /* nopl 0x00(%rax,%rax,1) */
p[1] = 0x1f;
p[2] = 0x44;
p[3] = 0x00;
p[4] = 0x00;
}
}
}
}
}
bool IsSymbolDirectlyReachable(struct Package *pkg, struct Packages *deps,
const char *symbol) {
return FindSymbol(symbol, pkg, deps, NULL, NULL);
}
struct RlEncoder {
size_t i, n;
struct RlDecode *p;
};
ssize_t rlencode_extend(struct RlEncoder *rle, size_t n) {
size_t n2;
struct RlDecode *p2;
n2 = rle->n;
if (!n2) n2 = 512;
while (n > n2) n2 += n2 >> 1;
if (!(p2 = realloc(rle->p, n2 * sizeof(rle->p[0])))) return -1;
rle->p = p2;
rle->n = n2;
return n2;
}
void rlencode_encode(struct RlEncoder *rle, const unsigned char *data,
size_t size) {
size_t i, j;
for (i = 0; i < size; i += j) {
for (j = 1; j < 255 && i + j < size; ++j) {
if (data[i] != data[i + j]) break;
}
rle->p[rle->i].repititions = j;
rle->p[rle->i].byte = data[i];
rle->i++;
}
rle->p[rle->i].repititions = 0;
rle->p[rle->i].byte = 0;
rle->i++;
}
ssize_t rlencode(struct RlEncoder *rle, const unsigned char *data,
size_t size) {
if (size + 1 > rle->n && rlencode_extend(rle, size + 1) == -1) return -1;
rlencode_encode(rle, data, size);
assert(rle->i <= rle->n);
return rle->i;
}
void CompressLowEntropyReadOnlyDataSections(struct Package *pkg,
struct Packages *deps,
struct Object *obj) {
Elf64_Half i;
const char *name;
unsigned char *p;
Elf64_Shdr *shdr;
struct RlEncoder rle;
bool haverldecode, isprofitable;
memset(&rle, 0, sizeof(rle));
haverldecode = IsSymbolDirectlyReachable(pkg, deps, "rldecode");
for (i = 0; i < obj->elf->e_shnum; ++i) {
if ((shdr = getelfsectionheaderaddress(obj->elf, obj->size, i)) &&
shdr->sh_size >= 256 &&
(shdr->sh_type == SHT_PROGBITS &&
!(shdr->sh_flags &
(SHF_WRITE | SHF_MERGE | SHF_STRINGS | SHF_COMPRESSED))) &&
(p = getelfsectionaddress(obj->elf, obj->size, shdr)) &&
startswith((name = getelfsectionname(obj->elf, obj->size, shdr)),
".rodata") &&
rlencode(&rle, p, shdr->sh_size) != -1) {
isprofitable = rle.i * sizeof(rle.p[0]) <= shdr->sh_size / 2;
LOGF("%s(%s): rlencode()%s on %s is%s profitable (%,zu → %,zu bytes)",
&pkg->strings.p[pkg->path], &pkg->strings.p[obj->path],
haverldecode ? "" : " [NOT LINKED]", name,
isprofitable ? "" : " NOT", shdr->sh_size, rle.i * sizeof(rle.p[0]));
}
}
free(rle.p);
}
void RewriteObjects(struct Package *pkg, struct Packages *deps) {
size_t i;
struct Object *obj;
#if 0
struct ElfWriter *elf;
elf = elfwriter_open(gc(xstrcat(&pkg->strings.p[pkg->path], ".o")), 0644);
elfwriter_cargoculting(elf);
#endif
for (i = 0; i < pkg->objects.i; ++i) {
obj = &pkg->objects.p[i];
OpenObject(pkg, obj, O_RDWR, PROT_READ | PROT_WRITE, MAP_SHARED);
OptimizeRelocations(pkg, deps, obj);
#if 0
CompressLowEntropyReadOnlyDataSections(pkg, deps, obj);
#endif
CloseObject(obj);
}
#if 0
elfwriter_close(elf);
#endif
}
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);
RewriteObjects(pkg, deps);
WritePackage(pkg);
for (i = 0; i < deps->i; ++i) {
CHECK_NE(-1, munmap(deps->p[i]->addr, deps->p[i]->size));
}
for (i = 0; i < pkg->objects.i; ++i) {
for (j = 0; j < pkg->objects.p[i].sections.i; ++j) {
free(pkg->objects.p[i].sections.p[j].ops.p);
}
free(pkg->objects.p[i].sections.p);
}
free_s(&pkg->strings.p);
free_s(&pkg->objects.p);
free_s(&pkg->symbols.p);
free_s(&deps->p);
}
int main(int argc, char *argv[]) {
struct Package pkg;
struct Packages deps;
memset(&pkg, 0, sizeof(pkg));
memset(&deps, 0, sizeof(deps));
Package(argc, argv, &pkg, &deps);
return 0;
}

126
tool/build/refactor.c Normal file
View file

@ -0,0 +1,126 @@
/*-*- 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/alg.h"
#include "libc/assert.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/stat.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/dt.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/x/x.h"
/**
* @fileoverview Pretty fast substring refactor tool.
*/
static const char kBefore[] = "\
Copyright 2019 Justine Alexandra Roberts Tunney \n\
\n\
Copying of this file is authorized only if (1) you are Justine Tunney, or \n\
(2) you make absolutely no changes to your copy. \n\
\n\
THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES │\n\
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF \n\
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR \n\
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES \n\
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN \n\
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF \n\
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \n\
";
const char kAfter[] = "\
Copyright 2020 Justine Alexandra Roberts Tunney \n\
\n\
This program is free software; you can redistribute it and/or modify \n\
it under the terms of the GNU General Public License as published by \n\
the Free Software Foundation; version 2 of the License. \n\
\n\
This program is distributed in the hope that it will be useful, but \n\
WITHOUT ANY WARRANTY; without even the implied warranty of \n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU \n\
General Public License for more details. \n\
\n\
You should have received a copy of the GNU General Public License \n\
along with this program; if not, write to the Free Software \n\
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA \n\
02110-1301 USA \n\
";
void RefactorFile(const char *path) {
int fd;
struct stat st;
size_t len, partlen, len1, len2;
char *mem, *spot = NULL, *part1, *part2;
CHECK_NE(-1, (fd = open(path, O_RDONLY)));
CHECK_NE(-1, fstat(fd, &st));
if ((len = st.st_size)) {
CHECK_NE(MAP_FAILED,
(mem = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0)));
partlen = sizeof(kBefore) - 1;
if ((spot = memmem(mem, len, kBefore, partlen))) {
part1 = gc(xmalloc((len1 = spot - mem)));
part2 = gc(xmalloc((len2 = len - partlen - (spot - mem))));
memcpy(part1, mem, len1);
memcpy(part2, spot + partlen, len2);
}
CHECK_NE(-1, munmap(mem, len));
}
CHECK_NE(-1, close(fd));
if (spot) {
fprintf(stderr, "found! %s\n", path);
CHECK_NE(-1, (fd = open(path, O_RDWR | O_TRUNC)));
CHECK_EQ(len1, write(fd, part1, len1));
CHECK_EQ(sizeof(kAfter) - 1, write(fd, kAfter, sizeof(kAfter) - 1));
CHECK_EQ(len2, write(fd, part2, len2));
CHECK_NE(-1, close(fd));
}
}
void RefactorDir(const char *dpath) {
DIR *dir;
struct dirent *ent;
char *path = gc(xmalloc(PATH_MAX));
CHECK_NOTNULL(dir = opendir(firstnonnull(dpath, ".")));
while ((ent = readdir(dir))) {
if (startswith(ent->d_name, ".")) continue;
if (strcmp(ent->d_name, "o") == 0) continue;
snprintf(path, PATH_MAX, "%s%s%s", dpath ? dpath : "", dpath ? "/" : "",
ent->d_name);
if (isdirectory(path)) {
RefactorDir(path);
} else if (isregularfile(path)) {
RefactorFile(path);
}
}
CHECK_NE(-1, closedir(dir));
}
int main(int argc, char *argv[]) {
RefactorDir(NULL);
return 0;
}

243
tool/build/rle.c Normal file
View file

@ -0,0 +1,243 @@
/*-*- 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/dce.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/errfuns.h"
#include "third_party/getopt/getopt.h"
#define USAGE1 \
"NAME\n\
\n\
rle - Run Length Encoder\n\
\n\
SYNOPSIS\n\
\n\
"
#define USAGE2 \
" [FLAGS] [FILE...]\n\
\n\
DESCRIPTION\n\
\n\
This is a primitive compression algorithm. Its advantage is that\n\
the concomitant rldecode() library is seventeen bytes, and works\n\
on IA-16, IA-32, and NexGen32e without needing to be recompiled.\n\
\n\
This CLI is consistent with gzip, bzip2, lzma, etc.\n\
\n\
FLAGS\n\
\n\
-1 .. -9 ignored\n\
-a ignored\n\
-c send to stdout\n\
-d decompress\n\
-f ignored\n\
-t test integrity\n\
-S SUFFIX overrides .rle extension\n\
-h shows this information\n"
FILE *fin_, *fout_;
bool decompress_, test_;
const char *suffix_, *hint_;
void StartErrorMessage(void) {
fputs("error: ", stderr);
fputs(hint_, stderr);
fputs(": ", stderr);
}
void PrintIoErrorMessage(void) {
int err;
err = errno;
StartErrorMessage();
fputs(strerror(err), stderr);
fputc('\n', stderr);
}
void PrintUsage(int rc, FILE *f) {
fputs(USAGE1, f);
fputs(program_invocation_name, f);
fputs(USAGE2, f);
exit(rc);
}
void GetOpts(int argc, char *argv[]) {
int opt;
fin_ = stdin;
suffix_ = ".rle";
while ((opt = getopt(argc, argv, "123456789S:acdfho:t")) != -1) {
switch (opt) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'a':
case 'f':
break;
case 'c':
fout_ = stdout;
break;
case 'd':
decompress_ = true;
break;
case 't':
test_ = true;
break;
case 'o':
fclose_s(&fout_);
if (!(fout_ = fopen((hint_ = optarg), "w"))) {
PrintIoErrorMessage();
exit(1);
}
break;
case 'S':
suffix_ = optarg;
break;
case 'h':
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
int RunLengthEncode1(void) {
int byte1, byte2, runlength;
byte2 = -1;
runlength = 0;
if ((byte1 = fgetc(fin_)) == -1) return -1;
do {
while (++runlength < 255) {
if ((byte2 = fgetc(fin_)) != byte1) break;
}
if (fputc(runlength, fout_) == -1 || fputc(byte1, fout_) == -1) {
return -1;
}
runlength = 0;
} while ((byte1 = byte2) != -1);
return feof(fin_) ? 0 : -1;
}
int RunLengthEncode2(void) { return fputc(0, fout_) | fputc(0, fout_); }
int EmitRun(unsigned char count, unsigned char byte) {
do {
if (fputc(byte, fout_) == -1) return -1;
} while (--count);
return 0;
}
int RunLengthDecode(void) {
int byte1, byte2;
if ((byte1 = fgetc(fin_)) == -1) return einval();
if ((byte2 = fgetc(fin_)) == -1) return einval();
while (byte1) {
if (!test_ && EmitRun(byte1, byte2) == -1) return -1;
if ((byte1 = fgetc(fin_)) == -1) break;
if ((byte2 = fgetc(fin_)) == -1) return einval();
}
if (byte1 != 0 || byte2 != 0) return einval();
fgetc(fin_);
return feof(fin_) ? 0 : -1;
}
int RunLengthCode(void) {
if (test_ || decompress_) {
return RunLengthDecode();
} else {
return RunLengthEncode1();
}
}
int Run(char **paths, size_t count) {
int rc;
char *p;
size_t i, suffixlen;
const char pathbuf[PATH_MAX];
if (!count) {
hint_ = "/dev/stdin";
if (!fout_) fout_ = stdout;
rc = RunLengthCode();
rc |= fclose_s(&fin_);
} else {
rc = fclose_s(&fin_);
for (i = 0; i < count && rc != -1; ++i) {
rc = -1;
if ((fin_ = fopen((hint_ = paths[i]), "r"))) {
if (test_ || fout_) {
rc = RunLengthCode();
} else {
suffixlen = strlen(suffix_);
if (!IsTrustworthy() && strlen(paths[i]) + suffixlen + 1 > PATH_MAX) {
return eoverflow();
}
p = stpcpy(pathbuf, paths[i]);
if (!decompress_) {
strcpy(p, suffix_);
} else if (p - pathbuf > suffixlen &&
memcmp(p - suffixlen, suffix_, suffixlen) == 0) {
p[-suffixlen] = '\0';
} else {
return enotsup();
}
if ((fout_ = fopen((hint_ = pathbuf), "w"))) {
rc = RunLengthCode();
if (rc != -1 && !decompress_) {
rc = RunLengthEncode2();
}
if ((rc |= fclose_s(&fout_)) != -1) {
unlink(paths[i]);
}
}
}
rc |= fclose_s(&fin_);
}
}
}
if (rc != -1 && fout_) {
rc = RunLengthEncode2();
rc |= fclose_s(&fout_);
}
return rc;
}
int main(int argc, char *argv[]) {
GetOpts(argc, argv);
if (Run(argv + optind, argc - optind) != -1) {
return EXIT_SUCCESS;
} else {
PrintIoErrorMessage();
return EXIT_FAILURE;
}
}

400
tool/build/runit.c Normal file
View file

@ -0,0 +1,400 @@
/*-*- 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/alg.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/stat.h"
#include "libc/conv/conv.h"
#include "libc/dce.h"
#include "libc/dns/dns.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/ipclassify.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ai.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/shut.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/sock.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "tool/build/runit.h"
/**
* @fileoverview Remote test runner.
*
* This is able to upload and run test binaries on remote operating
* systems with about 30 milliseconds of latency. It requires zero ops
* work too, since it deploys the ephemeral runit daemon via SSH upon
* ECONNREFUSED. That takes 10x longer (300 milliseconds). Further note
* there's no make -j race conditions here, thanks to SO_REUSEPORT.
*
* o/default/tool/build/runit.com \
* o/default/tool/build/runitd.com \
* o/default/test/libc/alg/qsort_test.com \
* freebsd.test.:31337:22
*
* The only thing that needs to be configured is /etc/hosts or Bind, to
* assign numbers to the officially reserved canned names. For example:
*
* 192.168.0.10 windows.test. windows
* 192.168.0.11 freebsd.test. freebsd
* 192.168.0.12 openbsd.test. openbsd
*
* Life is easiest if SSH public key authentication is configured too.
* It can be tuned as follows in ~/.ssh/config:
*
* host windows.test.
* user testacct
* host freebsd.test.
* user testacct
* host openbsd.test.
* user testacct
*
* Firewalls may need to be configured as well, to allow port tcp:31337
* from the local subnet. For example:
*
* iptables -L -vn
* iptables -I INPUT 1 -s 10.0.0.0/8 -p tcp --dport 31337 -j ACCEPT
* iptables -I INPUT 1 -s 192.168.0.0/16 -p tcp --dport 31337 -j ACCEPT
*
* If your system administrator blocks all ICMP, you'll likely encounter
* difficulties. Consider offering feedback to his/her manager and grand
* manager.
*
* Finally note this tool isn't designed for untrustworthy environments.
* It also isn't designed to process untrustworthy inputs.
*/
static const struct addrinfo kResolvHints = {.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP};
int g_sock;
jmp_buf g_jmpbuf;
uint16_t g_sshport, g_runitdport;
char *g_prog, *g_runitd, *g_ssh, g_hostname[128];
forceinline pureconst size_t GreatestTwoDivisor(size_t x) {
return x & (~x + 1);
}
noreturn void ShowUsage(FILE *f, int rc) {
fprintf(f, "Usage: %s RUNITD PROGRAM HOSTNAME[:RUNITDPORT[:SSHPORT]]...\n",
program_invocation_name);
exit(rc);
unreachable;
}
void CheckExists(const char *path) {
if (!isregularfile(path)) {
fprintf(stderr, "error: %s: not found or irregular\n", path);
ShowUsage(stderr, EX_USAGE);
unreachable;
}
}
nodiscard char *MakeDeployScript(struct addrinfo *remotenic, size_t combytes) {
const char *ip4 = (const char *)&remotenic->ai_addr4->sin_addr;
return xasprintf("mkdir -p o/ &&\n"
"dd bs=%zu count=%zu of=o/runitd.$$.com 2>/dev/null &&\n"
"exec <&- &&\n"
"chmod +x o/runitd.$$.com &&\n"
"o/runitd.$$.com -rdl%hhu.%hhu.%hhu.%hhu -p %hu &&\n"
"rm -f o/runitd.$$.com\n",
GreatestTwoDivisor(combytes),
combytes ? combytes / GreatestTwoDivisor(combytes) : 0,
ip4[0], ip4[1], ip4[2], ip4[3], g_runitdport);
}
void Upload(int pipe, int fd, struct stat *st) {
int64_t i;
for (i = 0; i < st->st_size;) {
CHECK_GT(splice(fd, &i, pipe, NULL, st->st_size - i, 0), 0);
}
CHECK_NE(-1, close(fd));
}
void DeployEphemeralRunItDaemonRemotelyViaSsh(struct addrinfo *ai) {
size_t got;
struct stat st;
char linebuf[32];
int sshpid, wstatus, binfd, sshfds[3];
DEBUGF("spawning %s on %s:%hu", g_runitd, g_hostname, g_runitdport);
CHECK_NE(-1, (binfd = open(g_runitd, O_RDONLY | O_CLOEXEC)));
CHECK_NE(-1, fstat(binfd, &st));
sshfds[0] = -1;
sshfds[1] = -1;
sshfds[2] = STDERR_FILENO;
CHECK_NE(-1, (sshpid = spawnve(
0, sshfds, g_ssh,
(char *const[]){"ssh", "-C", "-p",
gc(xasprintf("%hu", g_sshport)), g_hostname,
gc(MakeDeployScript(ai, st.st_size)), NULL},
environ)));
Upload(sshfds[0], binfd, &st);
CHECK_NE(-1, close(sshfds[0]));
CHECK_NE(-1, (got = read(sshfds[1], linebuf, sizeof(linebuf))));
CHECK_GT(got, 0);
linebuf[sizeof(linebuf) - 1] = '\0';
if (strncmp(linebuf, "ready ", 6) != 0) {
FATALF("expected ready response but got %`'.*s", got, linebuf);
}
g_runitdport = (uint16_t)atoi(&linebuf[6]);
CHECK_NE(-1, close(sshfds[1]));
CHECK_NE(-1, waitpid(sshpid, &wstatus, 0));
CHECK_EQ(0, WEXITSTATUS(wstatus));
}
void SetDeadline(int micros) {
setitimer(ITIMER_REAL, &(const struct itimerval){{0, 0}, {0, micros}}, NULL);
}
void Connect(int attempt) {
int rc, olderr;
const char *ip4;
struct addrinfo *ai;
if ((rc = getaddrinfo(g_hostname, gc(xasprintf("%hu", g_runitdport)),
&kResolvHints, &ai)) != 0) {
FATALF("%s:%hu: EAI_%s %m", g_hostname, g_runitdport, eai2str(rc));
unreachable;
}
if (ispublicip(ai->ai_family, &ai->ai_addr4->sin_addr)) {
ip4 = (const char *)&ai->ai_addr4->sin_addr;
FATALF("%s points to %hhu.%hhu.%hhu.%hhu"
" which isn't part of a local/private/testing subnet",
g_hostname, ip4[0], ip4[1], ip4[2], ip4[3]);
unreachable;
}
CHECK_NE(-1, (g_sock = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC,
ai->ai_protocol)));
SetDeadline(50000);
olderr = errno;
rc = connect(g_sock, ai->ai_addr, ai->ai_addrlen);
SetDeadline(0);
if (rc == -1) {
if (!attempt &&
(errno == ECONNREFUSED || errno == EHOSTUNREACH || errno == EINTR)) {
errno = olderr;
DeployEphemeralRunItDaemonRemotelyViaSsh(ai);
Connect(1);
} else if (errno == EINTR) {
fprintf(stderr, "%s(%s:%hu): %s\n", "connect", g_hostname, g_runitdport,
"offline, icmp misconfigured, or too slow; tune make HOSTS=...");
exit(1);
} else {
FATALF("%s(%s:%hu): %m", "connect", g_hostname, g_runitdport);
unreachable;
}
}
freeaddrinfo(ai);
}
void SendRequest(void) {
int fd;
int64_t off;
struct stat st;
const char *name;
unsigned char *hdr;
size_t progsize, namesize, hdrsize;
CHECK_NE(-1, (fd = open(g_prog, O_RDONLY)));
CHECK_NE(-1, fstat(fd, &st));
CHECK_LE((namesize = strlen((name = basename(g_prog)))), PATH_MAX);
CHECK_LE((progsize = st.st_size), INT_MAX);
CHECK_NOTNULL((hdr = gc(calloc(1, (hdrsize = 4 + 1 + 4 + 4 + namesize)))));
hdr[0 + 0] = (unsigned char)((unsigned)RUNITD_MAGIC >> 030);
hdr[0 + 1] = (unsigned char)((unsigned)RUNITD_MAGIC >> 020);
hdr[0 + 2] = (unsigned char)((unsigned)RUNITD_MAGIC >> 010);
hdr[0 + 3] = (unsigned char)((unsigned)RUNITD_MAGIC >> 000);
hdr[4 + 0] = kRunitExecute;
hdr[5 + 0] = (unsigned char)((unsigned)namesize >> 030);
hdr[5 + 1] = (unsigned char)((unsigned)namesize >> 020);
hdr[5 + 2] = (unsigned char)((unsigned)namesize >> 010);
hdr[5 + 3] = (unsigned char)((unsigned)namesize >> 000);
hdr[9 + 0] = (unsigned char)((unsigned)progsize >> 030);
hdr[9 + 1] = (unsigned char)((unsigned)progsize >> 020);
hdr[9 + 2] = (unsigned char)((unsigned)progsize >> 010);
hdr[9 + 3] = (unsigned char)((unsigned)progsize >> 000);
memcpy(&hdr[4 + 1 + 4 + 4], name, namesize);
CHECK_EQ(hdrsize, write(g_sock, hdr, hdrsize));
for (off = 0; off < progsize;) {
CHECK_GT(sendfile(g_sock, fd, &off, progsize - off), 0);
}
CHECK_NE(-1, shutdown(g_sock, SHUT_WR));
}
int ReadResponse(void) {
int res;
uint32_t size;
ssize_t rc;
size_t n, m;
unsigned char *p;
enum RunitCommand cmd;
static unsigned char msg[512];
res = -1;
for (;;) {
CHECK_NE(-1, (rc = recv(g_sock, msg, sizeof(msg), 0)));
p = &msg[0];
n = (size_t)rc;
if (!n) break;
do {
CHECK_GE(n, 4 + 1);
CHECK_EQ(RUNITD_MAGIC, read32be(p));
p += 4, n -= 4;
cmd = *p++, n--;
switch (cmd) {
case kRunitExit:
CHECK_GE(n, 1);
res = *p;
goto drop;
case kRunitStderr:
CHECK_GE(n, 4);
size = read32be(p), p += 4, n -= 4;
while (size) {
if (n) {
CHECK_NE(-1, (rc = write(STDERR_FILENO, p, min(n, size))));
CHECK_NE(0, (m = (size_t)rc));
p += m, n -= m, size -= m;
} else {
CHECK_NE(-1, (rc = recv(g_sock, msg, sizeof(msg), 0)));
p = &msg[0];
n = (size_t)rc;
if (!n) goto drop;
}
}
break;
default:
die();
}
} while (n);
}
drop:
CHECK_NE(-1, close(g_sock));
return res;
}
int RunOnHost(char *spec) {
char *p;
for (p = spec; *p; ++p) {
if (*p == ':') *p = ' ';
}
CHECK_GE(sscanf(spec, "%100s %hu %hu", g_hostname, &g_runitdport, &g_sshport),
1);
if (!strchr(g_hostname, '.')) strcat(g_hostname, ".test.");
Connect(0);
SendRequest();
return ReadResponse();
}
bool IsParallelBuild(void) {
const char *makeflags;
return (makeflags = getenv("MAKEFLAGS")) && strstr(makeflags, "-j");
}
bool ShouldRunInParralel(void) {
return !IsWindows() && IsParallelBuild();
}
int RunRemoteTestsInSerial(char *hosts[], int count) {
int i, exitcode;
for (i = 0; i < count; ++i) {
if ((exitcode = RunOnHost(hosts[i]))) {
return exitcode;
}
}
return 0;
}
void OnInterrupt(int sig) {
static bool once;
if (!once) {
once = true;
gclongjmp(g_jmpbuf, 128 + sig);
} else {
abort();
}
}
int RunRemoteTestsInParallel(char *hosts[], int count) {
const struct sigaction onsigterm = {.sa_handler = (void *)OnInterrupt};
struct sigaction onsigint = {.sa_handler = (void *)OnInterrupt};
int i, rc, exitcode;
int64_t leader, *pids;
leader = getpid();
pids = gc(xcalloc(count, sizeof(char *)));
if (!(exitcode = setjmp(g_jmpbuf))) {
sigaction(SIGINT, &onsigint, NULL);
sigaction(SIGTERM, &onsigterm, NULL);
for (i = 0; i < count; ++i) {
CHECK_NE(-1, (pids[i] = fork()));
if (!pids[i]) {
return RunOnHost(hosts[i]);
}
}
for (i = 0; i < count; ++i) {
CHECK_NE(-1, waitpid(pids[i], &rc, 0));
exitcode |= WEXITSTATUS(rc);
}
} else if (getpid() == leader) {
onsigint.sa_handler = SIG_IGN;
sigaction(SIGINT, &onsigint, NULL);
kill(0, SIGINT);
while (waitpid(-1, NULL, 0) > 0) donothing;
}
return exitcode;
}
int main(int argc, char *argv[]) {
showcrashreports();
g_loglevel = kLogDebug;
const struct sigaction onsigalrm = {.sa_handler = (void *)missingno};
if (argc > 1 &&
(strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
ShowUsage(stdout, 0);
unreachable;
}
if (argc < 1 + 2) ShowUsage(stderr, EX_USAGE);
CHECK_NOTNULL((g_ssh = commandv(firstnonnull(getenv("SSH"), "ssh"))));
CheckExists((g_runitd = argv[1]));
CheckExists((g_prog = argv[2]));
if (argc == 1 + 2) return 0; /* hosts list empty */
sigaction(SIGALRM, &onsigalrm, NULL);
g_sshport = 22;
g_runitdport = RUNITD_PORT;
return (ShouldRunInParralel() ? RunRemoteTestsInParallel
: RunRemoteTestsInSerial)(&argv[3], argc - 3);
}

15
tool/build/runit.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef COSMOPOLITAN_TOOL_BUILD_RUNIT_H_
#define COSMOPOLITAN_TOOL_BUILD_RUNIT_H_
#define RUNITD_PORT 31337
#define RUNITD_MAGIC 0xFEEDABEEu
#define RUNITD_TIMEOUT_MS (1000 * 10)
enum RunitCommand {
kRunitExecute,
kRunitStdout,
kRunitStderr,
kRunitExit,
};
#endif /* COSMOPOLITAN_TOOL_BUILD_RUNIT_H_ */

389
tool/build/runitd.c Normal file
View file

@ -0,0 +1,389 @@
/*-*- 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/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/stat.h"
#include "libc/conv/conv.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/nt/runtime.h"
#include "libc/paths.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/stdio/temp.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/inaddr.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/shut.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/sol.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
#include "tool/build/runit.h"
/**
* @fileoverview Remote test runner daemon.
* Delivers 10x latency improvement over SSH (100x if Debian defaults)
*
* Here's how it handles connections:
*
* 1. Receives atomically-written request header, comprised of:
*
* - 4 byte nbo magic = 0xFEEDABEEu
* - 1 byte command = kRunitExecute
* - 4 byte nbo name length in bytes, e.g. "test1"
* - 4 byte nbo executable file length in bytes
* - <name bytes> (no NUL terminator)
* - <file bytes> (it's binary data)
*
* 2. Runs program, after verifying it came from the IP that spawned
* this program via SSH. Be sure to only run this over a trusted
* physically-wired network. To use this software on untrustworthy
* networks, wrap it with stunnel and use your own CA.
*
* 3. Sends stdout/stderr fragments, potentially multiple times:
*
* - 4 byte nbo magic = 0xFEEDABEEu
* - 1 byte command = kRunitStdout/Stderr
* - 4 byte nbo byte length
* - <chunk bytes>
*
* 4. Sends process exit code:
*
* - 4 byte nbo magic = 0xFEEDABEEu
* - 1 byte command = kRunitExit
* - 1 byte exit status
*/
#define kLogFile "o/runitd.log"
#define kLogMaxBytes (2 * 1000 * 1000)
jmp_buf g_jb;
char *g_exepath;
volatile bool g_childterm;
struct sockaddr_in g_servaddr;
unsigned char g_buf[PAGESIZE];
bool g_daemonize, g_sendready;
int g_timeout, g_devnullfd, g_servfd, g_clifd, g_exefd;
void OnInterrupt(int sig) {
static bool once;
if (once) abort();
once = true;
kill(0, sig);
for (;;) {
if (waitpid(-1, NULL, 0) == -1) break;
}
gclongjmp(g_jb, sig);
unreachable;
}
void OnChildTerminated(int sig) {
g_childterm = true;
}
noreturn void ShowUsage(FILE *f, int rc) {
fprintf(f, "%s: %s %s\n", "Usage", program_invocation_name,
"[-d] [-r] [-l LISTENIP] [-p PORT] [-t TIMEOUTMS]");
exit(rc);
}
/\
* hi
*/
void GetOpts(int argc, char *argv[]) {
int opt;
g_timeout = RUNITD_TIMEOUT_MS;
g_servaddr.sin_family = AF_INET;
g_servaddr.sin_port = htons(RUNITD_PORT);
g_servaddr.sin_addr.s_addr = INADDR_ANY;
while ((opt = getopt(argc, argv, "hdrl:p:t:w:")) != -1) {
switch (opt) {
case 'd':
g_daemonize = true;
break;
case 'r':
g_sendready = true;
break;
case 't':
g_timeout = atoi(optarg);
break;
case 'p':
CHECK_NE(0xFFFF, (g_servaddr.sin_port = htons(parseport(optarg))));
break;
case 'l':
CHECK_EQ(1, inet_pton(AF_INET, optarg, &g_servaddr.sin_addr));
break;
case 'h':
ShowUsage(stdout, EXIT_SUCCESS);
unreachable;
default:
ShowUsage(stderr, EX_USAGE);
unreachable;
}
}
}
nodiscard char *DescribeAddress(struct sockaddr_in *addr) {
char ip4buf[16];
return xasprintf("%s:%hu",
inet_ntop(addr->sin_family, &addr->sin_addr.s_addr, ip4buf,
sizeof(ip4buf)),
ntohs(addr->sin_port));
}
void StartTcpServer(void) {
int yes = true;
uint32_t asize;
CHECK_NE(-1, (g_servfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)));
setsockopt(g_servfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
setsockopt(g_servfd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes));
if (bind(g_servfd, &g_servaddr, sizeof(g_servaddr)) == -1) {
if (g_servaddr.sin_port != 0) {
g_servaddr.sin_port = 0;
StartTcpServer();
return;
} else {
FATALF("bind failed %m");
}
}
CHECK_NE(-1, listen(g_servfd, 10));
asize = sizeof(g_servaddr);
CHECK_NE(-1, getsockname(g_servfd, &g_servaddr, &asize));
if (g_sendready) {
printf("ready %hu\n", ntohs(g_servaddr.sin_port));
fflush(stdout);
stdout->fd = g_devnullfd;
}
CHECK_NE(-1, fcntl(g_servfd, F_SETFD, FD_CLOEXEC));
LOGF("%s:%s", "listening on tcp", gc(DescribeAddress(&g_servaddr)));
}
void SendExitMessage(int sock, int rc) {
unsigned char msg[4 + 1 + 1];
msg[0 + 0] = (unsigned char)((unsigned)RUNITD_MAGIC >> 030);
msg[0 + 1] = (unsigned char)((unsigned)RUNITD_MAGIC >> 020);
msg[0 + 2] = (unsigned char)((unsigned)RUNITD_MAGIC >> 010);
msg[0 + 3] = (unsigned char)((unsigned)RUNITD_MAGIC >> 000);
msg[4] = kRunitExit;
msg[5] = (unsigned char)rc;
CHECK_EQ(sizeof(msg), send(sock, msg, sizeof(msg), 0));
}
void SendOutputFragmentMessage(int sock, enum RunitCommand kind,
unsigned char *buf, size_t size) {
ssize_t rc;
size_t sent;
unsigned char msg[4 + 1 + 4];
msg[0 + 0] = (unsigned char)((unsigned)RUNITD_MAGIC >> 030);
msg[0 + 1] = (unsigned char)((unsigned)RUNITD_MAGIC >> 020);
msg[0 + 2] = (unsigned char)((unsigned)RUNITD_MAGIC >> 010);
msg[0 + 3] = (unsigned char)((unsigned)RUNITD_MAGIC >> 000);
msg[4 + 0] = kind;
msg[5 + 0] = (unsigned char)((unsigned)size >> 030);
msg[5 + 1] = (unsigned char)((unsigned)size >> 020);
msg[5 + 2] = (unsigned char)((unsigned)size >> 010);
msg[5 + 3] = (unsigned char)((unsigned)size >> 000);
CHECK_EQ(sizeof(msg), send(sock, msg, sizeof(msg), 0));
while (size) {
CHECK_NE(-1, (rc = send(sock, buf, size, 0)));
CHECK_LE((sent = (size_t)rc), size);
size -= sent;
buf += sent;
}
}
void HandleClient(void) {
const size_t kMinMsgSize = 4 + 1 + 4 + 4;
const size_t kMaxNameSize = 32;
const size_t kMaxFileSize = 10 * 1024 * 1024;
unsigned char *p;
ssize_t got, wrote;
struct sockaddr_in addr;
char *addrstr, *exename;
int wstatus, child, stdiofds[3];
uint32_t addrsize, namesize, filesize, remaining;
/* read request to run program */
addrsize = sizeof(addr);
CHECK_NE(-1, (g_clifd = accept4(g_servfd, &addr, &addrsize, SOCK_CLOEXEC)));
defer(close_s, &g_clifd);
addrstr = gc(DescribeAddress(&addr));
DEBUGF("%s %s %s", gc(DescribeAddress(&g_servaddr)), "accepted", addrstr);
got = recv(g_clifd, (p = &g_buf[0]), sizeof(g_buf), 0);
CHECK_GE(got, kMinMsgSize);
CHECK_LE(got, sizeof(g_buf));
CHECK_EQ(RUNITD_MAGIC, read32be(p));
p += 4, got -= 4;
CHECK_EQ(kRunitExecute, *p++);
got--;
namesize = read32be(p), p += 4, got -= 4;
filesize = read32be(p), p += 4, got -= 4;
CHECK_GE(got, namesize);
CHECK_LE(namesize, kMaxNameSize);
CHECK_LE(filesize, kMaxFileSize);
exename = gc(xasprintf("%.*s", namesize, p));
g_exepath = gc(xasprintf("o/%d.%s", getpid(), basename(exename)));
LOGF("%s asked we run %`'s (%,u bytes @ %`'s)", addrstr, exename, filesize,
g_exepath);
p += namesize, got -= namesize;
/* write the file to disk */
remaining = filesize;
CHECK_NE(-1, (g_exefd = creat(g_exepath, 0700)));
defer(unlink_s, &g_exepath);
defer(close_s, &g_exefd);
ftruncate(g_exefd, filesize);
if (got) {
CHECK_EQ(got, write(g_exefd, p, got));
CHECK_LE(got, remaining);
remaining -= got;
}
while (remaining) {
CHECK_NE(-1, (got = recv(g_clifd, g_buf, sizeof(g_buf), 0)));
CHECK_LE(got, remaining);
if (!got) {
LOGF("%s %s %,u/%,u %s", addrstr, "sent", remaining, filesize,
"bytes before hangup");
return;
}
remaining -= got;
p = &g_buf[0];
do {
CHECK_GT((wrote = write(g_exefd, g_buf, got)), 0);
CHECK_LE(wrote, got);
} while ((got -= wrote));
}
/* CHECK_NE(-1, shutdown(g_clifd, SHUT_RD)); */
CHECK_NE(-1, close_s(&g_exefd));
/* run program, tee'ing stderr to both log and client */
DEBUGF("spawning %s", exename);
g_childterm = false;
stdiofds[0] = g_devnullfd;
stdiofds[1] = g_devnullfd;
stdiofds[2] = -1;
CHECK_NE(-1, (child = spawnve(0, stdiofds, g_exepath,
(char *const[]){g_exepath, NULL}, environ)));
DEBUGF("communicating %s[%d]", exename, child);
for (;;) {
CHECK_NE(-1, (got = read(stdiofds[2], g_buf, sizeof(g_buf))));
if (!got) {
close_s(&stdiofds[2]);
break;
}
fwrite(g_buf, got, 1, stderr);
SendOutputFragmentMessage(g_clifd, kRunitStderr, g_buf, got);
}
if (!g_childterm) {
CHECK_NE(-1, waitpid(child, &wstatus, 0));
}
DEBUGF("exited %s[%d] → %d", exename, child, WEXITSTATUS(wstatus));
/* let client know how it went */
SendExitMessage(g_clifd, WEXITSTATUS(wstatus));
/* CHECK_NE(-1, shutdown(g_clifd, SHUT_RDWR)); */
CHECK_NE(-1, close(g_clifd));
}
int Poll(void) {
int i, evcount;
struct pollfd fds[] = {{g_servfd, POLLIN}};
TryAgain:
evcount = poll(fds, ARRAYLEN(fds), g_timeout);
if (evcount == -1 && errno == EINTR) goto TryAgain;
CHECK_NE(-1, evcount);
for (i = 0; i < evcount; ++i) {
CHECK(fds[i].revents & POLLIN);
HandleClient();
}
return evcount;
}
int Serve(void) {
int rc;
const struct sigaction onsigint = {.sa_handler = (void *)OnInterrupt,
.sa_flags = SA_RESETHAND};
const struct sigaction onsigterm = {.sa_handler = (void *)OnInterrupt,
.sa_flags = SA_RESETHAND};
const struct sigaction onsigchld = {.sa_handler = SIG_IGN,
.sa_flags = SA_RESETHAND | SA_RESTART};
StartTcpServer();
defer(close_s, &g_servfd);
if (!(rc = setjmp(g_jb))) {
sigaction(SIGINT, &onsigint, NULL);
sigaction(SIGTERM, &onsigterm, NULL);
sigaction(SIGCHLD, &onsigchld, NULL);
while (g_servfd != -1) {
if (!Poll()) break;
}
LOGF("timeout expired, shutting down");
} else {
if (isatty(fileno(stderr))) fputc('\r', stderr);
LOGF("got %s, shutting down", strsignal(rc));
rc += 128;
}
return rc;
}
void Daemonize(void) {
struct stat st;
if (fork() > 0) _exit(0);
setsid();
if (fork() > 0) _exit(0);
stdin->fd = g_devnullfd;
if (!g_sendready) stdout->fd = g_devnullfd;
if (stat(kLogFile, &st) != -1 && st.st_size > kLogMaxBytes) unlink(kLogFile);
freopen(kLogFile, "a", stderr);
}
int main(int argc, char *argv[]) {
showcrashreports();
g_loglevel = kLogDebug;
GetOpts(argc, argv);
CHECK_NE(-1, (g_devnullfd = open("/dev/null", O_RDWR)));
defer(close_s, &g_devnullfd);
if (!isdirectory("o")) CHECK_NE(-1, mkdir("o", 0700));
if (g_daemonize) Daemonize();
return Serve();
}

303
tool/build/zipobj.c Normal file
View file

@ -0,0 +1,303 @@
/*-*- 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/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dos.h"
#include "libc/elf/def.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/mem/alloca.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/prot.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "libc/zip.h"
#include "third_party/getopt/getopt.h"
#include "third_party/zlib/zlib.h"
#include "tool/build/lib/elfwriter.h"
#define ZIP_LOCALFILE_SECTION ".piro.data.sort.zip.2."
#define ZIP_DIRECTORY_SECTION ".piro.data.sort.zip.4."
#define PUT8(P, V) *P++ = V
#define PUT16(P, V) P[0] = V & 0xff, P[1] = V >> 010 & 0xff, P += 2
#define PUT32(P, V) \
P[0] = V & 0xff, P[1] = V >> 010 & 0xff, P[2] = V >> 020 & 0xff, \
P[3] = V >> 030 & 0xff, P += 4
char *symbol_;
char *outpath_;
const size_t kMinCompressSize = 32;
const char kNoCompressExts[][8] = {".gz", ".xz", ".jpg", ".png",
".gif", ".zip", ".bz2", ".mpg",
".mp4", ".lz4", ".webp", ".mpeg"};
noreturn void PrintUsage(int rc, FILE *f) {
fprintf(f, "%s%s%s\n", "Usage: ", program_invocation_name,
" [-o FILE] [-s SYMBOL] [FILE...]\n");
exit(rc);
}
void GetOpts(int *argc, char ***argv) {
int opt;
while ((opt = getopt(*argc, *argv, "?ho:s:")) != -1) {
switch (opt) {
case 'o':
outpath_ = optarg;
break;
case 's':
symbol_ = optarg;
break;
case '?':
case 'h':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
*argc -= optind;
*argv += optind;
CHECK_NOTNULL(outpath_);
}
bool IsPureAscii(const void *data, size_t size) {
const unsigned char *p, *pe;
for (p = data, pe = p + size; p < pe; ++p) {
if (!*p || *p >= 0x80) {
return false;
}
}
return true;
}
bool ShouldCompress(const char *name, size_t size) {
size_t i;
char key[8];
const char *p;
if (!(p = memrchr(name, '.', size))) return true;
strncpy(key, p, sizeof(key));
for (i = 0; i < ARRAYLEN(kNoCompressExts); ++i) {
if (memcmp(key, kNoCompressExts[i], sizeof(key)) == 0) return false;
}
return true;
}
void GetDosLocalTime(int64_t utcunixts, uint16_t *out_time,
uint16_t *out_date) {
struct tm tm;
CHECK_NOTNULL(localtime_r(&utcunixts, &tm));
*out_time = DOS_TIME(tm.tm_hour, tm.tm_min, tm.tm_sec);
*out_date = DOS_DATE(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
}
static unsigned char *EmitZipLfileHdr(unsigned char *op, const void *name,
size_t namesize, uint32_t crc,
uint8_t era, uint16_t gflags,
uint16_t method, uint16_t mtime,
uint16_t mdate, size_t compsize,
size_t uncompsize) {
PUT32(op, kZipLfileHdrMagic);
PUT8(op, era);
PUT8(op, kZipOsDos);
PUT16(op, gflags);
PUT16(op, method);
PUT16(op, mtime);
PUT16(op, mdate);
PUT32(op, crc);
PUT32(op, compsize);
PUT32(op, uncompsize);
PUT16(op, namesize);
PUT16(op, 0); /* extra */
return mempcpy(op, name, namesize);
}
static void EmitZipCdirHdr(unsigned char *op, const void *name, size_t namesize,
uint32_t crc, uint8_t era, uint16_t gflags,
uint16_t method, uint16_t mtime, uint16_t mdate,
uint16_t iattrs, uint16_t dosmode, uint16_t unixmode,
size_t compsize, size_t uncompsize,
size_t commentsize) {
PUT32(op, kZipCfileHdrMagic);
PUT8(op, kZipCosmopolitanVersion);
PUT8(op, kZipOsUnix);
PUT8(op, era);
PUT8(op, kZipOsDos);
PUT16(op, gflags);
PUT16(op, method);
PUT16(op, mtime);
PUT16(op, mdate);
PUT32(op, crc);
PUT32(op, compsize);
PUT32(op, uncompsize);
PUT16(op, namesize);
PUT16(op, 0); /* extra size */
PUT16(op, commentsize);
PUT16(op, 0); /* disk */
PUT16(op, iattrs);
PUT16(op, dosmode);
PUT16(op, unixmode);
PUT32(op, 0); /* RELOCATE ME (kZipCfileOffsetOffset) */
memcpy(op, name, namesize);
}
void EmitZip(struct ElfWriter *elf, const char *name, size_t namesize,
const unsigned char *data, struct stat *st) {
z_stream zs;
uint8_t era;
uint32_t crc;
unsigned char *lfile, *cfile;
struct ElfWriterSymRef lfilesym;
size_t lfilehdrsize, uncompsize, compsize, commentsize;
uint16_t method, gflags, mtime, mdate, iattrs, dosmode;
compsize = st->st_size;
uncompsize = st->st_size;
CHECK_LE(uncompsize, UINT32_MAX);
lfilehdrsize = kZipLfileHdrMinSize + namesize;
crc = crc32_z(0, data, uncompsize);
GetDosLocalTime(st->st_mtim.tv_sec, &mtime, &mdate);
gflags = IsPureAscii(name, namesize) ? 0 : kZipGflagUtf8;
commentsize = kZipCdirHdrLinkableSize - (kZipCfileHdrMinSize + namesize);
iattrs = IsPureAscii(data, st->st_size) ? kZipIattrAscii : kZipIattrBinary;
dosmode = !(st->st_mode & 0200) ? kNtFileAttributeReadonly : 0;
method = (st->st_size >= kMinCompressSize && ShouldCompress(name, namesize))
? kZipCompressionDeflate
: kZipCompressionNone;
/* emit embedded file content w/ pkzip local file header */
elfwriter_align(elf, kZipCdirAlign, 0);
elfwriter_startsection(elf,
gc(xasprintf("%s%s", ZIP_LOCALFILE_SECTION, name)),
SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
if (method == kZipCompressionDeflate) {
CHECK_EQ(Z_OK, deflateInit2(memset(&zs, 0, sizeof(zs)),
Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY));
zs.next_in = data;
zs.avail_in = uncompsize;
zs.next_out = ((lfile = elfwriter_reserve(
elf, (lfilehdrsize +
(zs.avail_out = compressBound(uncompsize))))) +
lfilehdrsize);
CHECK_EQ(Z_STREAM_END, deflate(&zs, Z_FINISH));
CHECK_EQ(Z_OK, deflateEnd(&zs));
if (zs.total_out < uncompsize) {
compsize = zs.total_out;
} else {
method = kZipCompressionNone;
}
} else {
lfile = elfwriter_reserve(elf, lfilehdrsize + uncompsize);
}
if (method == kZipCompressionNone) {
memcpy(lfile + lfilehdrsize, data, uncompsize);
}
era = (gflags || method) ? kZipEra1993 : kZipEra1989;
EmitZipLfileHdr(lfile, name, namesize, crc, era, gflags, method, mtime, mdate,
compsize, uncompsize);
elfwriter_commit(elf, lfilehdrsize + compsize);
lfilesym = elfwriter_appendsym(elf, gc(xasprintf("%s%s", "zip+lfile:", name)),
ELF64_ST_INFO(STB_LOCAL, STT_OBJECT),
STV_DEFAULT, 0, lfilehdrsize);
elfwriter_appendsym(elf, name, ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT),
STV_DEFAULT, lfilehdrsize, compsize);
elfwriter_finishsection(elf);
/* emit central directory record */
elfwriter_align(elf, kZipCdirAlign, 0);
elfwriter_startsection(elf,
gc(xasprintf("%s%s", ZIP_DIRECTORY_SECTION, name)),
SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
EmitZipCdirHdr((cfile = elfwriter_reserve(elf, kZipCdirHdrLinkableSize)),
name, namesize, crc, era, gflags, method, mtime, mdate, iattrs,
dosmode, st->st_mode, compsize, uncompsize, commentsize);
elfwriter_appendsym(elf, gc(xasprintf("%s%s", "zip+cdir:", name)),
ELF64_ST_INFO(STB_LOCAL, STT_OBJECT), STV_DEFAULT, 0,
kZipCdirHdrLinkableSize);
elfwriter_appendrela(elf, kZipCfileOffsetOffset, lfilesym, R_X86_64_32,
-IMAGE_BASE_VIRTUAL);
elfwriter_commit(elf, kZipCdirHdrLinkableSize);
elfwriter_finishsection(elf);
}
void ProcessFile(struct ElfWriter *elf, const char *path) {
int fd;
void *map;
struct stat st;
CHECK_NE(-1, (fd = open(path, O_RDONLY)));
CHECK_NE(-1, fstat(fd, &st));
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)));
EmitZip(elf, path, strlen(path), map, &st);
CHECK_NE(-1, munmap(map, st.st_size));
CHECK_NE(-1, close(fd));
}
void PullEndOfCentralDirectoryIntoLinkage(struct ElfWriter *elf) {
elfwriter_align(elf, 1, 0);
elfwriter_startsection(elf, ".yoink", SHT_PROGBITS,
SHF_ALLOC | SHF_EXECINSTR);
elfwriter_yoink(elf, "__zip_start");
elfwriter_yoink(elf, "__zip_end");
elfwriter_finishsection(elf);
}
void CheckFilenameKosher(const char *path) {
CHECK_LE(strlen(path), PATH_MAX);
CHECK(!startswith(path, "/"));
CHECK(!strstr(path, ".."));
}
void zipobj(int argc, char **argv) {
size_t i;
struct ElfWriter *elf;
CHECK_LT(argc, UINT16_MAX / 3 - 64); /* ELF 64k section limit */
GetOpts(&argc, &argv);
for (i = 0; i < argc; ++i) CheckFilenameKosher(argv[i]);
elf = elfwriter_open(outpath_, 0644);
elfwriter_cargoculting(elf);
for (i = 0; i < argc; ++i) ProcessFile(elf, argv[i]);
PullEndOfCentralDirectoryIntoLinkage(elf);
elfwriter_close(elf);
}
int main(int argc, char **argv) {
zipobj(argc, argv);
return 0;
}

193
tool/cc/c11.l Normal file
View file

@ -0,0 +1,193 @@
/* http://www.quut.com/c/ANSI-C-grammar-l-2011.html */
%e 1019
%p 2807
%n 371
%k 284
%a 1213
%o 1117
O [0-7]
D [0-9]
NZ [1-9]
L [a-zA-Z_]
A [a-zA-Z_0-9]
H [a-fA-F0-9]
HP (0[xX])
E ([Ee][+-]?{D}+)
P ([Pp][+-]?{D}+)
FS (f|F|l|L)
IS (((u|U)(l|L|ll|LL)?)|((l|L|ll|LL)(u|U)?))
CP (u|U|L)
SP (u8|u|U|L)
ES (\\(['"\?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F0-9]+))
WS [ \t\v\n\f]
%{
#include <stdio.h>
#include "y.tab.h"
extern void yyerror(const char *); /* prints grammar violation message */
extern int sym_type(const char *); /* returns type from symbol table */
#define sym_type(identifier) IDENTIFIER /* with no symbol table, fake it */
static void comment(void);
static int check_type(void);
%}
%%
"/*" { comment(); }
"//".* { /* consume //-comment */ }
"auto" { return(AUTO); }
"break" { return(BREAK); }
"case" { return(CASE); }
"char" { return(CHAR); }
"const" { return(CONST); }
"continue" { return(CONTINUE); }
"default" { return(DEFAULT); }
"do" { return(DO); }
"double" { return(DOUBLE); }
"else" { return(ELSE); }
"enum" { return(ENUM); }
"extern" { return(EXTERN); }
"float" { return(FLOAT); }
"for" { return(FOR); }
"goto" { return(GOTO); }
"if" { return(IF); }
"inline" { return(INLINE); }
"int" { return(INT); }
"long" { return(LONG); }
"register" { return(REGISTER); }
"restrict" { return(RESTRICT); }
"return" { return(RETURN); }
"short" { return(SHORT); }
"signed" { return(SIGNED); }
"sizeof" { return(SIZEOF); }
"static" { return(STATIC); }
"struct" { return(STRUCT); }
"switch" { return(SWITCH); }
"typedef" { return(TYPEDEF); }
"union" { return(UNION); }
"unsigned" { return(UNSIGNED); }
"void" { return(VOID); }
"volatile" { return(VOLATILE); }
"while" { return(WHILE); }
"_Alignas" { return ALIGNAS; }
"_Alignof" { return ALIGNOF; }
"_Atomic" { return ATOMIC; }
"_Bool" { return BOOL; }
"_Complex" { return COMPLEX; }
"_Generic" { return GENERIC; }
"_Imaginary" { return IMAGINARY; }
"_Noreturn" { return NORETURN; }
"_Static_assert" { return STATIC_ASSERT; }
"_Thread_local" { return THREAD_LOCAL; }
"__func__" { return FUNC_NAME; }
{L}{A}* { return check_type(); }
{HP}{H}+{IS}? { return I_CONSTANT; }
{NZ}{D}*{IS}? { return I_CONSTANT; }
"0"{O}*{IS}? { return I_CONSTANT; }
{CP}?"'"([^'\\\n]|{ES})+"'" { return I_CONSTANT; }
{D}+{E}{FS}? { return F_CONSTANT; }
{D}*"."{D}+{E}?{FS}? { return F_CONSTANT; }
{D}+"."{E}?{FS}? { return F_CONSTANT; }
{HP}{H}+{P}{FS}? { return F_CONSTANT; }
{HP}{H}*"."{H}+{P}{FS}? { return F_CONSTANT; }
{HP}{H}+"."{P}{FS}? { return F_CONSTANT; }
({SP}?\"([^"\\\n]|{ES})*\"{WS}*)+ { return STRING_LITERAL; }
"..." { return ELLIPSIS; }
">>=" { return RIGHT_ASSIGN; }
"<<=" { return LEFT_ASSIGN; }
"+=" { return ADD_ASSIGN; }
"-=" { return SUB_ASSIGN; }
"*=" { return MUL_ASSIGN; }
"/=" { return DIV_ASSIGN; }
"%=" { return MOD_ASSIGN; }
"&=" { return AND_ASSIGN; }
"^=" { return XOR_ASSIGN; }
"|=" { return OR_ASSIGN; }
">>" { return RIGHT_OP; }
"<<" { return LEFT_OP; }
"++" { return INC_OP; }
"--" { return DEC_OP; }
"->" { return PTR_OP; }
"&&" { return AND_OP; }
"||" { return OR_OP; }
"<=" { return LE_OP; }
">=" { return GE_OP; }
"==" { return EQ_OP; }
"!=" { return NE_OP; }
";" { return ';'; }
("{"|"<%") { return '{'; }
("}"|"%>") { return '}'; }
"," { return ','; }
":" { return ':'; }
"=" { return '='; }
"(" { return '('; }
")" { return ')'; }
("["|"<:") { return '['; }
("]"|":>") { return ']'; }
"." { return '.'; }
"&" { return '&'; }
"!" { return '!'; }
"~" { return '~'; }
"-" { return '-'; }
"+" { return '+'; }
"*" { return '*'; }
"/" { return '/'; }
"%" { return '%'; }
"<" { return '<'; }
">" { return '>'; }
"^" { return '^'; }
"|" { return '|'; }
"?" { return '?'; }
{WS}+ { /* whitespace separates tokens */ }
. { /* discard bad characters */ }
%%
int yywrap(void) /* called at end of input */
{
return 1; /* terminate now */
}
static void comment(void)
{
int c;
while ((c = input()) != 0)
if (c == '*')
{
while ((c = input()) == '*')
;
if (c == '/')
return;
if (c == 0)
break;
}
yyerror("unterminated comment");
}
static int check_type(void)
{
switch (sym_type(yytext))
{
case TYPEDEF_NAME: /* previously defined */
return TYPEDEF_NAME;
case ENUMERATION_CONSTANT: /* previously defined */
return ENUMERATION_CONSTANT;
default: /* includes undefined */
return IDENTIFIER;
}
}

535
tool/cc/c11.y Normal file
View file

@ -0,0 +1,535 @@
/* http://www.quut.com/c/ANSI-C-grammar-y-2011.html */
%token IDENTIFIER I_CONSTANT F_CONSTANT STRING_LITERAL FUNC_NAME SIZEOF
%token PTR_OP INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP
%token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN
%token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN
%token XOR_ASSIGN OR_ASSIGN
%token TYPEDEF_NAME ENUMERATION_CONSTANT
%token TYPEDEF EXTERN STATIC AUTO REGISTER INLINE
%token CONST RESTRICT VOLATILE
%token BOOL CHAR SHORT INT LONG SIGNED UNSIGNED FLOAT DOUBLE VOID
%token COMPLEX IMAGINARY
%token STRUCT UNION ENUM ELLIPSIS
%token CASE DEFAULT IF ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK RETURN
%token ALIGNAS ALIGNOF ATOMIC GENERIC NORETURN STATIC_ASSERT THREAD_LOCAL
%start translation_unit
%%
primary_expression
: IDENTIFIER
| constant
| string
| '(' expression ')'
| generic_selection
;
constant
: I_CONSTANT /* includes character_constant */
| F_CONSTANT
| ENUMERATION_CONSTANT /* after it has been defined as such */
;
enumeration_constant /* before it has been defined as such */
: IDENTIFIER
;
string
: STRING_LITERAL
| FUNC_NAME
;
generic_selection
: GENERIC '(' assignment_expression ',' generic_assoc_list ')'
;
generic_assoc_list
: generic_association
| generic_assoc_list ',' generic_association
;
generic_association
: type_name ':' assignment_expression
| DEFAULT ':' assignment_expression
;
postfix_expression
: primary_expression
| postfix_expression '[' expression ']'
| postfix_expression '(' ')'
| postfix_expression '(' argument_expression_list ')'
| postfix_expression '.' IDENTIFIER
| postfix_expression PTR_OP IDENTIFIER
| postfix_expression INC_OP
| postfix_expression DEC_OP
| '(' type_name ')' '{' initializer_list '}'
| '(' type_name ')' '{' initializer_list ',' '}'
;
argument_expression_list
: assignment_expression
| argument_expression_list ',' assignment_expression
;
unary_expression
: postfix_expression
| INC_OP unary_expression
| DEC_OP unary_expression
| unary_operator cast_expression
| SIZEOF unary_expression
| SIZEOF '(' type_name ')'
| ALIGNOF '(' type_name ')'
;
unary_operator
: '&'
| '*'
| '+'
| '-'
| '~'
| '!'
;
cast_expression
: unary_expression
| '(' type_name ')' cast_expression
;
multiplicative_expression
: cast_expression
| multiplicative_expression '*' cast_expression
| multiplicative_expression '/' cast_expression
| multiplicative_expression '%' cast_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
shift_expression
: additive_expression
| shift_expression LEFT_OP additive_expression
| shift_expression RIGHT_OP additive_expression
;
relational_expression
: shift_expression
| relational_expression '<' shift_expression
| relational_expression '>' shift_expression
| relational_expression LE_OP shift_expression
| relational_expression GE_OP shift_expression
;
equality_expression
: relational_expression
| equality_expression EQ_OP relational_expression
| equality_expression NE_OP relational_expression
;
and_expression
: equality_expression
| and_expression '&' equality_expression
;
exclusive_or_expression
: and_expression
| exclusive_or_expression '^' and_expression
;
inclusive_or_expression
: exclusive_or_expression
| inclusive_or_expression '|' exclusive_or_expression
;
logical_and_expression
: inclusive_or_expression
| logical_and_expression AND_OP inclusive_or_expression
;
logical_or_expression
: logical_and_expression
| logical_or_expression OR_OP logical_and_expression
;
conditional_expression
: logical_or_expression
| logical_or_expression '?' expression ':' conditional_expression
;
assignment_expression
: conditional_expression
| unary_expression assignment_operator assignment_expression
;
assignment_operator
: '='
| MUL_ASSIGN
| DIV_ASSIGN
| MOD_ASSIGN
| ADD_ASSIGN
| SUB_ASSIGN
| LEFT_ASSIGN
| RIGHT_ASSIGN
| AND_ASSIGN
| XOR_ASSIGN
| OR_ASSIGN
;
expression
: assignment_expression
| expression ',' assignment_expression
;
constant_expression
: conditional_expression /* with constraints */
;
declaration
: declaration_specifiers ';'
| declaration_specifiers init_declarator_list ';'
| static_assert_declaration
;
declaration_specifiers
: storage_class_specifier declaration_specifiers
| storage_class_specifier
| type_specifier declaration_specifiers
| type_specifier
| type_qualifier declaration_specifiers
| type_qualifier
| function_specifier declaration_specifiers
| function_specifier
| alignment_specifier declaration_specifiers
| alignment_specifier
;
init_declarator_list
: init_declarator
| init_declarator_list ',' init_declarator
;
init_declarator
: declarator '=' initializer
| declarator
;
storage_class_specifier
: TYPEDEF /* identifiers must be flagged as TYPEDEF_NAME */
| EXTERN
| STATIC
| THREAD_LOCAL
| AUTO
| REGISTER
;
type_specifier
: VOID
| CHAR
| SHORT
| INT
| LONG
| FLOAT
| DOUBLE
| SIGNED
| UNSIGNED
| BOOL
| COMPLEX
| IMAGINARY /* non-mandated extension */
| atomic_type_specifier
| struct_or_union_specifier
| enum_specifier
| TYPEDEF_NAME /* after it has been defined as such */
;
struct_or_union_specifier
: struct_or_union '{' struct_declaration_list '}'
| struct_or_union IDENTIFIER '{' struct_declaration_list '}'
| struct_or_union IDENTIFIER
;
struct_or_union
: STRUCT
| UNION
;
struct_declaration_list
: struct_declaration
| struct_declaration_list struct_declaration
;
struct_declaration
: specifier_qualifier_list ';' /* for anonymous struct/union */
| specifier_qualifier_list struct_declarator_list ';'
| static_assert_declaration
;
specifier_qualifier_list
: type_specifier specifier_qualifier_list
| type_specifier
| type_qualifier specifier_qualifier_list
| type_qualifier
;
struct_declarator_list
: struct_declarator
| struct_declarator_list ',' struct_declarator
;
struct_declarator
: ':' constant_expression
| declarator ':' constant_expression
| declarator
;
enum_specifier
: ENUM '{' enumerator_list '}'
| ENUM '{' enumerator_list ',' '}'
| ENUM IDENTIFIER '{' enumerator_list '}'
| ENUM IDENTIFIER '{' enumerator_list ',' '}'
| ENUM IDENTIFIER
;
enumerator_list
: enumerator
| enumerator_list ',' enumerator
;
enumerator /* identifiers must be flagged as ENUMERATION_CONSTANT */
: enumeration_constant '=' constant_expression
| enumeration_constant
;
atomic_type_specifier
: ATOMIC '(' type_name ')'
;
type_qualifier
: CONST
| RESTRICT
| VOLATILE
| ATOMIC
;
function_specifier
: INLINE
| NORETURN
;
alignment_specifier
: ALIGNAS '(' type_name ')'
| ALIGNAS '(' constant_expression ')'
;
declarator
: pointer direct_declarator
| direct_declarator
;
direct_declarator
: IDENTIFIER
| '(' declarator ')'
| direct_declarator '[' ']'
| direct_declarator '[' '*' ']'
| direct_declarator '[' STATIC type_qualifier_list assignment_expression ']'
| direct_declarator '[' STATIC assignment_expression ']'
| direct_declarator '[' type_qualifier_list '*' ']'
| direct_declarator '[' type_qualifier_list STATIC assignment_expression ']'
| direct_declarator '[' type_qualifier_list assignment_expression ']'
| direct_declarator '[' type_qualifier_list ']'
| direct_declarator '[' assignment_expression ']'
| direct_declarator '(' parameter_type_list ')'
| direct_declarator '(' ')'
| direct_declarator '(' identifier_list ')'
;
pointer
: '*' type_qualifier_list pointer
| '*' type_qualifier_list
| '*' pointer
| '*'
;
type_qualifier_list
: type_qualifier
| type_qualifier_list type_qualifier
;
parameter_type_list
: parameter_list ',' ELLIPSIS
| parameter_list
;
parameter_list
: parameter_declaration
| parameter_list ',' parameter_declaration
;
parameter_declaration
: declaration_specifiers declarator
| declaration_specifiers abstract_declarator
| declaration_specifiers
;
identifier_list
: IDENTIFIER
| identifier_list ',' IDENTIFIER
;
type_name
: specifier_qualifier_list abstract_declarator
| specifier_qualifier_list
;
abstract_declarator
: pointer direct_abstract_declarator
| pointer
| direct_abstract_declarator
;
direct_abstract_declarator
: '(' abstract_declarator ')'
| '[' ']'
| '[' '*' ']'
| '[' STATIC type_qualifier_list assignment_expression ']'
| '[' STATIC assignment_expression ']'
| '[' type_qualifier_list STATIC assignment_expression ']'
| '[' type_qualifier_list assignment_expression ']'
| '[' type_qualifier_list ']'
| '[' assignment_expression ']'
| direct_abstract_declarator '[' ']'
| direct_abstract_declarator '[' '*' ']'
| direct_abstract_declarator '[' STATIC type_qualifier_list assignment_expression ']'
| direct_abstract_declarator '[' STATIC assignment_expression ']'
| direct_abstract_declarator '[' type_qualifier_list assignment_expression ']'
| direct_abstract_declarator '[' type_qualifier_list STATIC assignment_expression ']'
| direct_abstract_declarator '[' type_qualifier_list ']'
| direct_abstract_declarator '[' assignment_expression ']'
| '(' ')'
| '(' parameter_type_list ')'
| direct_abstract_declarator '(' ')'
| direct_abstract_declarator '(' parameter_type_list ')'
;
initializer
: '{' initializer_list '}'
| '{' initializer_list ',' '}'
| assignment_expression
;
initializer_list
: designation initializer
| initializer
| initializer_list ',' designation initializer
| initializer_list ',' initializer
;
designation
: designator_list '='
;
designator_list
: designator
| designator_list designator
;
designator
: '[' constant_expression ']'
| '.' IDENTIFIER
;
static_assert_declaration
: STATIC_ASSERT '(' constant_expression ',' STRING_LITERAL ')' ';'
;
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
labeled_statement
: IDENTIFIER ':' statement
| CASE constant_expression ':' statement
| DEFAULT ':' statement
;
compound_statement
: '{' '}'
| '{' block_item_list '}'
;
block_item_list
: block_item
| block_item_list block_item
;
block_item
: declaration
| statement
;
expression_statement
: ';'
| expression ';'
;
selection_statement
: IF '(' expression ')' statement ELSE statement
| IF '(' expression ')' statement
| SWITCH '(' expression ')' statement
;
iteration_statement
: WHILE '(' expression ')' statement
| DO statement WHILE '(' expression ')' ';'
| FOR '(' expression_statement expression_statement ')' statement
| FOR '(' expression_statement expression_statement expression ')' statement
| FOR '(' declaration expression_statement ')' statement
| FOR '(' declaration expression_statement expression ')' statement
;
jump_statement
: GOTO IDENTIFIER ';'
| CONTINUE ';'
| BREAK ';'
| RETURN ';'
| RETURN expression ';'
;
translation_unit
: external_declaration
| translation_unit external_declaration
;
external_declaration
: function_definition
| declaration
;
function_definition
: declaration_specifiers declarator declaration_list compound_statement
| declaration_specifiers declarator compound_statement
;
declaration_list
: declaration
| declaration_list declaration
;
%%
#include <stdio.h>
void yyerror(const char *s) {
fflush(stdout);
fprintf(stderr, "*** %s\n", s);
}

11
tool/cc/cc.mk Normal file
View file

@ -0,0 +1,11 @@
#-*-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_CC
o/$(MODE)/tool/cc/c11.c: tool/cc/c11.l o/$(MODE)/third_party/lex/lex.com.dbg
@mkdir -p $(dir $@)
o/$(MODE)/third_party/lex/lex.com.dbg -o $@ $<
.PHONY: o/$(MODE)/tool/cc
o/$(MODE)/tool/cc:

33
tool/debug/debug.mk Normal file
View file

@ -0,0 +1,33 @@
#-*-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_DEBUG
TOOL_DEBUG_SRCS := $(wildcard tool/debug/*.c)
TOOL_DEBUG_OBJS = $(TOOL_DEBUG_SRCS:%.c=o/$(MODE)/%.o)
TOOL_DEBUG_COMS = $(TOOL_DEBUG_OBJS:%.o=%.com)
TOOL_DEBUG_DEPS := $(call uniq, \
$(LIBC_STR) \
$(LIBC_RUNTIME) \
$(LIBC_STDIO) \
$(LIBC_X) \
$(LIBC_LOG))
TOOL_DEBUG_BINS = \
$(TOOL_DEBUG_COMS) \
$(TOOL_DEBUG_COMS:%=%.dbg)
o/$(MODE)/tool/debug/%.com.dbg: \
$(TOOL_DEBUG_DEPS) \
o/$(MODE)/tool/debug/%.o \
$(CRT) \
$(APE)
@$(APELINK)
$(TOOL_DEBUG_OBJS): \
$(BUILD_FILES) \
tool/debug/debug.mk
.PHONY: o/$(MODE)/tool/debug
o/$(MODE)/tool/debug: $(TOOL_DEBUG_BINS) $(TOOL_DEBUG_CHECKS)

66
tool/decode/decode.mk Normal file
View file

@ -0,0 +1,66 @@
#-*-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_DECODE
TOOL_DECODE_FILES := $(wildcard tool/decode/*)
TOOL_DECODE_HDRS = $(filter %.h,$(TOOL_DECODE_FILES))
TOOL_DECODE_SRCS = $(filter %.c,$(TOOL_DECODE_FILES))
TOOL_DECODE_COMS = $(TOOL_DECODE_OBJS:%.o=%.com)
TOOL_DECODE_OBJS = \
$(TOOL_DECODE_SRCS:%=o/$(MODE)/%.zip.o) \
$(TOOL_DECODE_SRCS:%.c=o/$(MODE)/%.o)
TOOL_DECODE_BINS = \
$(TOOL_DECODE_COMS) \
$(TOOL_DECODE_COMS:%=%.dbg)
TOOL_DECODE_CHECKS = \
$(TOOL_DECODE_HDRS:%=o/$(MODE)/%.ok)
TOOL_DECODE_DIRECTDEPS = \
LIBC_CALLS \
LIBC_CONV \
LIBC_ELF \
LIBC_FMT \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RAND \
LIBC_RUNTIME \
LIBC_RUNTIME \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_UNICODE \
LIBC_X \
TOOL_DECODE_LIB \
THIRD_PARTY_GETOPT \
THIRD_PARTY_XED
TOOL_DECODE_DEPS := \
$(call uniq,$(foreach x,$(TOOL_DECODE_DIRECTDEPS),$($(x))))
o/$(MODE)/tool/decode/decode.pkg: \
$(TOOL_DECODE_OBJS) \
$(foreach x,$(TOOL_DECODE_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/tool/decode/%.com.dbg: \
$(TOOL_DECODE_DEPS) \
o/$(MODE)/tool/decode/%.o \
o/$(MODE)/tool/decode/decode.pkg \
$(CRT) \
$(APE)
@$(APELINK)
$(TOOL_DECODE_OBJS): \
$(BUILD_FILES) \
tool/decode/decode.mk
.PHONY: o/$(MODE)/tool/decode
o/$(MODE)/tool/decode: \
o/$(MODE)/tool/decode/lib \
$(TOOL_DECODE_BINS) \
$(TOOL_DECODE_CHECKS)

329
tool/decode/elf.c Normal file
View file

@ -0,0 +1,329 @@
/*-*- 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/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/conv/conv.h"
#include "libc/elf/elf.h"
#include "libc/elf/struct/rela.h"
#include "libc/elf/struct/shdr.h"
#include "libc/errno.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "tool/decode/lib/asmcodegen.h"
#include "tool/decode/lib/elfidnames.h"
#include "tool/decode/lib/flagger.h"
#include "tool/decode/lib/titlegen.h"
/**
* @fileoverview ELF executable metadata disassembler.
*/
static const char *path;
static struct stat st[1];
static Elf64_Ehdr *elf;
static void startfile(void) {
showtitle("αcτµαlly pδrταblε εxεcµταblε", "tool/decode/elf", basename(path),
NULL, &kModelineAsm);
printf("#include \"libc/elf.h\"\n\n", path);
}
static void printelfehdr(void) {
show(".ascii", format(b1, "%`'.*s", 4, (const char *)&elf->e_ident[0]),
"magic");
show(".byte",
firstnonnull(findnamebyid(&kElfClassNames[0], elf->e_ident[EI_CLASS]),
format(b1, "%d", elf->e_ident[EI_CLASS])),
"elf->e_ident[EI_CLASS]");
show(".byte",
firstnonnull(findnamebyid(kElfDataNames, elf->e_ident[EI_DATA]),
format(b1, "%d", elf->e_ident[EI_DATA])),
"elf->e_ident[EI_DATA]");
show(".byte", format(b1, "%d", elf->e_ident[EI_VERSION]),
"elf->e_ident[EI_VERSION]");
show(".byte",
firstnonnull(findnamebyid(kElfOsabiNames, elf->e_ident[EI_OSABI]),
format(b1, "%d", elf->e_ident[EI_OSABI])),
"elf->e_ident[EI_OSABI]");
show(".byte", format(b1, "%d", elf->e_ident[EI_ABIVERSION]),
"elf->e_ident[EI_ABIVERSION]");
show(".byte",
format(b1, "%d,%d,%d,%d,%d,%d,%d", elf->e_ident[EI_PAD + 0],
elf->e_ident[EI_PAD + 1], elf->e_ident[EI_PAD + 2],
elf->e_ident[EI_PAD + 3], elf->e_ident[EI_PAD + 4],
elf->e_ident[EI_PAD + 5], elf->e_ident[EI_PAD + 6]),
"padding");
show(".org", "0x10", NULL);
show(".short",
firstnonnull(findnamebyid(kElfTypeNames, elf->e_type),
format(b1, "%hd", elf->e_type)),
"elf->e_type");
show(".short",
firstnonnull(findnamebyid(kElfMachineNames, elf->e_machine),
format(b1, "%hd", elf->e_machine)),
"elf->e_machine");
show(".long", format(b1, "%d", elf->e_version), "elf->e_version");
show(".quad", format(b1, "%#x", elf->e_entry), "elf->e_entry");
show(".quad", format(b1, "%#x", elf->e_phoff), "elf->e_phoff");
show(".quad", format(b1, "%#x", elf->e_shoff), "elf->e_shoff");
show(".long", format(b1, "%#x", elf->e_flags), "elf->e_flags");
show(".short", format(b1, "%hd", elf->e_ehsize), "elf->e_ehsize");
show(".short", format(b1, "%hd", elf->e_phentsize), "elf->e_phentsize");
show(".short", format(b1, "%hd", elf->e_phnum), "elf->e_phnum");
show(".short", format(b1, "%hd", elf->e_shentsize), "elf->e_shentsize");
show(".short", format(b1, "%hd", elf->e_shnum), "elf->e_shnum");
show(".short", format(b1, "%hd", elf->e_shstrndx), "elf->e_shstrndx");
}
static void printelfsegmentheader(int i) {
Elf64_Phdr *phdr = getelfsegmentheaderaddress(elf, st->st_size, i);
printf("/\tElf64_Phdr *phdr = getelfsegmentheaderaddress(elf, st->st_size, "
"%d)\n",
i);
printf(".Lph%d:", i);
show(".long",
firstnonnull(findnamebyid(kElfSegmentTypeNames, phdr->p_type),
format(b1, "%#x", phdr->p_type)),
"phdr->p_type");
show(".long", recreateflags(kElfSegmentFlagNames, phdr->p_flags),
"phdr->p_flags");
show(".quad", format(b1, "%#x", phdr->p_offset), "phdr->p_offset");
show(".quad", format(b1, "%#x", phdr->p_vaddr), "phdr->p_vaddr");
show(".quad", format(b1, "%#x", phdr->p_paddr), "phdr->p_paddr");
show(".quad", format(b1, "%#x", phdr->p_filesz), "phdr->p_filesz");
show(".quad", format(b1, "%#x", phdr->p_memsz), "phdr->p_memsz");
show(".quad", format(b1, "%#x", phdr->p_align), "phdr->p_align");
fflush(stdout);
}
static void printelfsegmentheaders(void) {
printf("\n");
printf("\t.org\t%#x\n", elf->e_phoff);
for (unsigned i = 0; i < elf->e_phnum; ++i) printelfsegmentheader(i);
}
static void printelfsectionheader(int i, char *shstrtab) {
Elf64_Shdr *shdr;
shdr = getelfsectionheaderaddress(elf, st->st_size, i);
printf("/\tElf64_Shdr *shdr = getelfsectionheaderaddress(elf, st->st_size, "
"%d)\n",
i);
printf(".Lsh%d:", i);
show(".long", format(b1, "%d", shdr->sh_name),
format(b2,
"%`'s == getelfstring(elf, st->st_size, shstrtab, shdr->sh_name)",
getelfstring(elf, st->st_size, shstrtab, shdr->sh_name)));
show(".long",
firstnonnull(findnamebyid(kElfSectionTypeNames, shdr->sh_type),
format(b1, "%d", shdr->sh_type)),
"shdr->sh_type");
show(".long", recreateflags(kElfSectionFlagNames, shdr->sh_flags),
"shdr->sh_flags");
show(".quad", format(b1, "%#x", shdr->sh_addr), "shdr->sh_addr");
show(".quad", format(b1, "%#x", shdr->sh_offset), "shdr->sh_offset");
show(".quad", format(b1, "%#x", shdr->sh_size), "shdr->sh_size");
show(".long", format(b1, "%#x", shdr->sh_link), "shdr->sh_link");
show(".long", format(b1, "%#x", shdr->sh_info), "shdr->sh_info");
show(".quad", format(b1, "%#x", shdr->sh_addralign), "shdr->sh_addralign");
show(".quad", format(b1, "%#x", shdr->sh_entsize), "shdr->sh_entsize");
fflush(stdout);
}
static void printelfsectionheaders(void) {
Elf64_Half i;
char *shstrtab = getelfsectionnamestringtable(elf, st->st_size);
if (shstrtab) {
printf("\n");
printf("\t.org\t%#x\n", elf->e_shoff);
for (i = 0; i < elf->e_shnum; ++i) {
printelfsectionheader(i, shstrtab);
}
printf("\n/\t%s\n", "elf->e_shstrndx");
printf("\t.org\t%#x\n",
getelfsectionheaderaddress(elf, st->st_size, elf->e_shstrndx)
->sh_offset);
for (i = 0; i < elf->e_shnum; ++i) {
Elf64_Shdr *shdr = getelfsectionheaderaddress(elf, st->st_size, i);
const char *str = getelfstring(elf, st->st_size, shstrtab, shdr->sh_name);
show(".asciz", format(b1, "%`'s", str), NULL);
}
}
}
static void printelfsymbolinfo(Elf64_Sym *sym) {
int bind = (sym->st_info >> 4) & 0xf;
const char *bindname = findnamebyid(kElfSymbolBindNames, bind);
int type = (sym->st_info >> 0) & 0xf;
const char *typename = findnamebyid(kElfSymbolTypeNames, type);
show(".byte",
format(b1, "%s%s%s", bindname ? format(b2, "%s<<4", bindname) : "",
bindname && typename ? "|" : "", firstnonnull(typename, "")),
"sym->st_info");
}
static void printelfsymbolother(Elf64_Sym *sym) {
int visibility = sym->st_other & 0x3;
const char *visibilityname =
findnamebyid(kElfSymbolVisibilityNames, visibility);
int other = sym->st_other & ~0x3;
show(".byte",
format(b1, "%s%s%s", firstnonnull(visibilityname, ""),
other && visibilityname ? "+" : "",
other ? format(b2, "%d", other) : ""),
"sym->st_other");
}
static void printelfsymbol(Elf64_Sym *sym, char *strtab, char *shstrtab) {
show(".long", format(b1, "%d", sym->st_name),
format(b2, "%`'s (sym->st_name)",
getelfstring(elf, st->st_size, strtab, sym->st_name)));
printelfsymbolinfo(sym);
printelfsymbolother(sym);
show(".short", format(b1, "%d", sym->st_shndx),
format(b2, "%s sym->st_shndx",
sym->st_shndx < 0xff00
? format(b1, "%`'s",
getelfstring(elf, st->st_size, shstrtab,
getelfsectionheaderaddress(
elf, st->st_size, sym->st_shndx)
->sh_name))
: findnamebyid(kElfSpecialSectionNames, sym->st_shndx)));
show(".quad", format(b1, "%#x", sym->st_value), "sym->st_value");
show(".quad", format(b1, "%#x", sym->st_size), "sym->st_size");
}
static void printelfsymboltable(void) {
size_t i, symcount = 0;
Elf64_Sym *symtab = getelfsymboltable(elf, st->st_size, &symcount);
char *strtab = getelfstringtable(elf, st->st_size);
char *shstrtab = getelfsectionnamestringtable(elf, st->st_size);
if (symtab && strtab) {
printf("\n\n");
printf("\t.org\t%#x\n", (intptr_t)symtab - (intptr_t)elf);
for (i = 0; i < symcount; ++i) {
printf(".Lsym%d:\n", i);
printelfsymbol(&symtab[i], strtab, shstrtab);
}
}
}
static char *getelfsymbolname(const Elf64_Ehdr *elf, size_t mapsize,
const char *strtab, const char *shstrtab,
const Elf64_Sym *sym) {
char *res;
const Elf64_Shdr *shdr;
if (elf && sym &&
((shstrtab && !sym->st_name &&
ELF64_ST_TYPE(sym->st_info) == STT_SECTION &&
(shdr = getelfsectionheaderaddress(elf, mapsize, sym->st_shndx)) &&
(res = getelfstring(elf, mapsize, shstrtab, shdr->sh_name))) ||
(strtab && (res = getelfstring(elf, mapsize, strtab, sym->st_name))))) {
return res;
} else {
return NULL;
}
}
static void printelfrelocations(void) {
int sym;
size_t i, j;
const Elf64_Sym *syms;
const Elf64_Rela *rela;
const Elf64_Shdr *shdr, *boop;
char *strtab, *shstrtab, *symbolname;
strtab = getelfstringtable(elf, st->st_size);
shstrtab = getelfsectionnamestringtable(elf, st->st_size);
for (i = 0; i < elf->e_shnum; ++i) {
if ((shdr = getelfsectionheaderaddress(elf, st->st_size, i)) &&
shdr->sh_type == SHT_RELA &&
(rela = getelfsectionaddress(elf, st->st_size, shdr))) {
printf("\n/\t%s\n", getelfsectionname(elf, st->st_size, shdr));
printf("\t.org\t%#x\n", (intptr_t)rela - (intptr_t)elf);
for (j = 0; ((uintptr_t)rela + sizeof(Elf64_Rela) <=
min((uintptr_t)elf + st->st_size,
(uintptr_t)elf + shdr->sh_offset + shdr->sh_size));
++rela, ++j) {
boop = getelfsectionheaderaddress(elf, st->st_size, shdr->sh_link);
syms = getelfsectionaddress(elf, st->st_size, boop);
sym = ELF64_R_SYM(rela->r_info);
symbolname =
getelfsymbolname(elf, st->st_size, strtab, shstrtab, &syms[sym]);
printf("/\t%s+%#lx → %s%c%#lx\n",
getelfstring(
elf, st->st_size, shstrtab,
getelfsectionheaderaddress(elf, st->st_size, shdr->sh_info)
->sh_name),
rela->r_offset, symbolname, rela->r_addend >= 0 ? '+' : '-',
abs(rela->r_addend));
printf("%s_%zu_%zu:\n", ".Lrela", i, j);
show(".quad", format(b1, "%#lx", rela->r_offset), "rela->r_offset");
show(".long",
format(b1, "%s%s", "R_X86_64_",
findnamebyid(kElfNexgen32eRelocationNames,
ELF64_R_TYPE(rela->r_info))),
"ELF64_R_TYPE(rela->r_info)");
show(".long", format(b1, "%d", ELF64_R_SYM(rela->r_info)),
"ELF64_R_SYM(rela->r_info)");
show(".quad", format(b1, "%#lx", rela->r_addend), "rela->r_addend");
}
}
}
}
int main(int argc, char *argv[]) {
showcrashreports();
if (argc != 2) {
fprintf(stderr, "usage: %`s FILE: %s\n", argv[0]);
return 1;
}
path = argv[1];
int64_t fd = open(path, O_RDONLY);
if (fd == -1) {
if (errno == ENOENT) {
fprintf(stderr, "error: %`s not found\n", path);
exit(1);
}
perror("open");
exit(1);
}
fstat(fd, st);
CHECK_NE(MAP_FAILED,
(elf = mmap(NULL, st->st_size, PROT_READ, MAP_SHARED, fd, 0)));
if (memcmp(elf->e_ident, ELFMAG, 4) != 0) {
fprintf(stderr, "error: not an elf executable: %'s\n", path);
exit(1);
}
startfile();
printelfehdr();
printelfsegmentheaders();
printelfsectionheaders();
printelfrelocations();
printelfsymboltable();
munmap(elf, st->st_size);
close(fd);
return 0;
}

267
tool/decode/ent.c Normal file
View file

@ -0,0 +1,267 @@
/* PUBLIC DOMAIN */
/* clang-format off */
/*
ENT -- Entropy calculation and analysis of putative
random sequences.
Designed and implemented by John "Random" Walker in May 1985.
Multiple analyses of random sequences added in December 1985.
Bit stream analysis added in September 1997.
Terse mode output, getopt() command line processing,
optional stdin input, and HTML documentation added in
October 1998.
Documentation for the -t (terse output) option added
in July 2006.
Replaced table look-up for chi square to probability
conversion with algorithmic computation in January 2008.
For additional information and the latest version,
see http://www.fourmilab.ch/random/
*/
#include "libc/calls/calls.h"
#include "libc/math.h"
#include "libc/rand/rand.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "third_party/getopt/getopt.h"
#define UPDATE "January 28th, 2008"
#define FALSE 0
#define TRUE 1
#ifdef M_PI
#define PI M_PI
#else
#define PI 3.14159265358979323846
#endif
/* ISO 8859/1 Latin-1 alphabetic and upper and lower case bit vector tables. */
#define isISOspace(x) \
((isascii(((unsigned char)(x))) && isspace(((unsigned char)(x)))) || \
((x) == 0xA0))
#define isISOalpha(x) \
((isoalpha[(((unsigned char)(x))) / 8] & \
(0x80 >> ((((unsigned char)(x))) % 8))) != 0)
#define isISOupper(x) \
((isoupper[(((unsigned char)(x))) / 8] & \
(0x80 >> ((((unsigned char)(x))) % 8))) != 0)
#define isISOlower(x) \
((isolower[(((unsigned char)(x))) / 8] & \
(0x80 >> ((((unsigned char)(x))) % 8))) != 0)
#define isISOprint(x) ((((x) >= ' ') && ((x) <= '~')) || ((x) >= 0xA0))
#define toISOupper(x) \
(isISOlower(x) \
? (isascii(((unsigned char)(x))) ? toupper(x) \
: (((((unsigned char)(x)) != 0xDF) && \
(((unsigned char)(x)) != 0xFF)) \
? (((unsigned char)(x)) - 0x20) \
: (x))) \
: (x))
#define toISOlower(x) \
(isISOupper(x) \
? (isascii(((unsigned char)(x))) ? tolower(x) \
: (((unsigned char)(x)) + 0x20)) \
: (x))
unsigned char isoalpha[32] = {
0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 224, 127, 255, 255, 224,
0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 254, 255, 255, 255, 254, 255};
unsigned char isoupper[32] = {0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255,
224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 255, 255, 254, 254, 0, 0, 0, 0};
unsigned char isolower[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 127, 255, 255, 224, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 255, 255, 254, 255};
/* HELP -- Print information on how to call */
static void help(void)
{
printf("ent -- Calculate entropy of file. Call");
printf("\n with ent [options] [input-file]");
printf("\n");
printf("\n Options: -b Treat input as a stream of bits");
printf("\n -c Print occurrence counts");
printf("\n -f Fold upper to lower case letters");
printf("\n -t Terse output in CSV format");
printf("\n -u Print this message\n");
printf("\nBy John Walker");
printf("\n http://www.fourmilab.ch/");
printf("\n %s\n", UPDATE);
}
/* Main program */
int main(int argc, char *argv[])
{
int i, oc, opt;
long ccount[256]; /* Bins to count occurrences of values */
long totalc = 0; /* Total character count */
char *samp;
double montepi, chip,
scc, ent, mean, chisq;
FILE *fp = stdin;
int counts = FALSE, /* Print character counts */
fold = FALSE, /* Fold upper to lower */
binary = FALSE, /* Treat input as a bitstream */
terse = FALSE; /* Terse (CSV format) output */
while ((opt = getopt(argc, argv, "bcftu?BCFTU")) != -1) {
switch (toISOlower(opt)) {
case 'b':
binary = TRUE;
break;
case 'c':
counts = TRUE;
break;
case 'f':
fold = TRUE;
break;
case 't':
terse = TRUE;
break;
case '?':
case 'u':
help();
return 0;
}
}
if (optind < argc) {
if (optind != (argc - 1)) {
printf("Duplicate file name.\n");
help();
return 2;
}
if ((fp = fopen(argv[optind], "rb")) == NULL) {
printf("Cannot open file %s\n", argv[optind]);
return 2;
}
}
samp = binary ? "bit" : "byte";
memset(ccount, 0, sizeof ccount);
/* Initialise for calculations */
rt_init(binary);
/* Scan input file and count character occurrences */
while ((oc = fgetc(fp)) != EOF) {
unsigned char ocb;
if (fold && isISOalpha(oc) && isISOupper(oc)) {
oc = toISOlower(oc);
}
ocb = (unsigned char) oc;
totalc += binary ? 8 : 1;
if (binary) {
int b;
unsigned char ob = ocb;
for (b = 0; b < 8; b++) {
ccount[ob & 1]++;
ob >>= 1;
}
} else {
ccount[ocb]++; /* Update counter for this bin */
}
rt_add(&ocb, 1);
}
fclose(fp);
/* Complete calculation and return sequence metrics */
rt_end(&ent, &chisq, &mean, &montepi, &scc);
if (terse) {
printf("0,File-%ss,Entropy,Chi-square,Mean,Monte-Carlo-Pi,Serial-Correlation\n",
binary ? "bit" : "byte");
printf("1,%ld,%f,%f,%f,%f,%f\n",
totalc, ent, chisq, mean, montepi, scc);
}
/* Calculate probability of observed distribution occurring from
the results of the Chi-Square test */
chip = pochisq(chisq, (binary ? 1 : 255));
/* Print bin counts if requested */
if (counts) {
if (terse) {
printf("2,Value,Occurrences,Fraction\n");
} else {
printf("Value Char Occurrences Fraction\n");
}
for (i = 0; i < (binary ? 2 : 256); i++) {
if (terse) {
printf("3,%d,%ld,%f\n", i,
ccount[i], ((double) ccount[i] / totalc));
} else {
if (ccount[i] > 0) {
printf("%3d %c %10ld %f\n", i,
/* The following expression shows ISO 8859-1
Latin1 characters and blanks out other codes.
The test for ISO space replaces the ISO
non-blanking space (0xA0) with a regular
ASCII space, guaranteeing it's rendered
properly even when the font doesn't contain
that character, which is the case with many
X fonts. */
(!isISOprint(i) || isISOspace(i)) ? ' ' : i,
ccount[i], ((double) ccount[i] / totalc));
}
}
}
if (!terse) {
printf("\nTotal: %10ld %f\n\n", totalc, 1.0);
}
}
/* Print calculated results */
if (!terse) {
printf("Entropy = %f bits per %s.\n", ent, samp);
printf("\nOptimum compression would reduce the size\n");
printf("of this %ld %s file by %d percent.\n\n", totalc, samp,
(short) ((100 * ((binary ? 1 : 8) - ent) /
(binary ? 1.0 : 8.0))));
printf(
"Chi square distribution for %ld samples is %1.2f, and randomly\n",
totalc, chisq);
if (chip < 0.0001) {
printf("would exceed this value less than 0.01 percent of the times.\n\n");
} else if (chip > 0.9999) {
printf("would exceed this value more than than 99.99 percent of the times.\n\n");
} else {
printf("would exceed this value %1.2f percent of the times.\n\n",
chip * 100);
}
printf(
"Arithmetic mean value of data %ss is %1.4f (%.1f = random).\n",
samp, mean, binary ? 0.5 : 127.5);
printf("Monte Carlo value for Pi is %1.9f (error %1.2f percent).\n",
montepi, 100.0 * (fabs(PI - montepi) / PI));
printf("Serial correlation coefficient is ");
if (scc >= -99999) {
printf("%1.6f (totally uncorrelated = 0.0).\n", scc);
} else {
printf("undefined (all values equal!).\n");
}
}
return 0;
}

33
tool/decode/hex.c Normal file
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/stdio/stdio.h"
/**
* @fileoverview Binary to hex converter program.
*/
int main() {
int o;
while (0 <= (o = getchar()) && o <= 255) {
putchar("0123456789ABCDEF"[o / 16]);
putchar("0123456789ABCDEF"[o % 16]);
}
putchar('\n');
}

View file

@ -0,0 +1,57 @@
/*-*- 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/bits/safemacros.h"
#include "libc/fmt/fmt.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "tool/decode/lib/asmcodegen.h"
char b1[BUFSIZ];
char b2[BUFSIZ];
char *format(char *buf, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
vsnprintf(buf, BUFSIZ, fmt, va);
va_end(va);
return buf;
}
nodiscard char *tabpad(const char *s, unsigned width) {
char *p;
size_t i, l, need;
l = strlen(s);
need = width > l ? (roundup(width, 8) - l - 1) / 8 + 1 : 0;
p = memcpy(malloc(l + need + 1), s, l);
for (i = 0; i < need; ++i) p[l + i] = '\t';
p[l + need] = '\0';
return p;
}
void show(const char *directive, const char *value, const char *comment) {
if (comment) {
printf("\t%s\t%s# %s\n", directive, gc(tabpad(value, COLUMN_WIDTH)),
comment);
} else {
printf("\t%s\t%s\n", directive, gc(tabpad(value, COLUMN_WIDTH)));
}
}

View file

@ -0,0 +1,27 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_ASMCODEGEN_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_ASMCODEGEN_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define COLUMN_WIDTH 24
#define showint(x) show(".long", format(b1, "%d", x), #x)
#define showint64(x) show(".quad", format(b1, "%ld", x), #x)
#define showbyte(x) show(".byte", format(b1, "%hhn", x), #x)
#define showshort(x) show(".short", format(b1, "%hn", x), #x)
#define showshorthex(x) show(".short", format(b1, "%#-6hX", x), #x)
#define showinthex(x) show(".long", format(b1, "%#X", x), #x)
#define showint64hex(x) show(".quad", format(b1, "%#lX", x), #x)
#define showorg(x) show(".org", format(b1, "%#lX", x), #x)
extern char b1[BUFSIZ];
extern char b2[BUFSIZ];
char *format(char *buf, const char *fmt, ...);
nodiscard char *tabpad(const char *s, unsigned width);
void show(const char *directive, const char *value, const char *comment);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_ASMCODEGEN_H_ */

View file

@ -0,0 +1,76 @@
/*-*- 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/assert.h"
#include "libc/bits/bits.h"
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "tool/decode/lib/bitabuilder.h"
/**
* @fileoverview Sparse bit array builder.
*/
struct BitaBuilder {
size_t i, n;
unsigned *p;
};
struct BitaBuilder *bitabuilder_new(void) {
return calloc(1, sizeof(struct BitaBuilder));
}
void bitabuilder_free(struct BitaBuilder **bbpp) {
if (*bbpp) {
free_s(&(*bbpp)->p);
free_s(bbpp);
}
}
/**
* Sets bit.
*
* @return false if out of memory
*/
bool bitabuilder_setbit(struct BitaBuilder *bb, size_t bit) {
void *p2;
size_t i, n;
i = MAX(bb->i, ROUNDUP(bit / CHAR_BIT + 1, __BIGGEST_ALIGNMENT__));
if (i > bb->n) {
n = i + (i >> 2);
if ((p2 = realloc(bb->p, n))) {
memset((char *)p2 + bb->n, 0, n - bb->n);
bb->n = n;
bb->p = p2;
} else {
return false;
}
}
bb->i = i;
bts(bb->p, bit);
return true;
}
bool bitabuilder_fwrite(const struct BitaBuilder *bb, FILE *f) {
return fwrite(bb->p, bb->i, 1, f);
}

View file

@ -0,0 +1,15 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_BITABUILDER_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_BITABUILDER_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct FILE;
struct BitaBuilder;
struct BitaBuilder *bitabuilder_new(void);
bool bitabuilder_setbit(struct BitaBuilder *, size_t);
bool bitabuilder_fwrite(const struct BitaBuilder *, struct FILE *);
void bitabuilder_free(struct BitaBuilder **);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_BITABUILDER_H_ */

View file

@ -0,0 +1,62 @@
#-*-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_DECODE_LIB
TOOL_DECODE_LIB_ARTIFACTS += TOOL_DECODE_LIB_A
TOOL_DECODE_LIB = $(TOOL_DECODE_LIB_A_DEPS) $(TOOL_DECODE_LIB_A)
TOOL_DECODE_LIB_A = o/$(MODE)/tool/decode/lib/decodelib.a
TOOL_DECODE_LIB_A_FILES := $(wildcard tool/decode/lib/*)
TOOL_DECODE_LIB_A_HDRS = $(filter %.h,$(TOOL_DECODE_LIB_A_FILES))
TOOL_DECODE_LIB_A_SRCS_S = $(filter %.S,$(TOOL_DECODE_LIB_A_FILES))
TOOL_DECODE_LIB_A_SRCS_C = $(filter %.c,$(TOOL_DECODE_LIB_A_FILES))
TOOL_DECODE_LIB_A_CHECKS = $(TOOL_DECODE_LIB_A).pkg
TOOL_DECODE_LIB_A_SRCS = \
$(TOOL_DECODE_LIB_A_SRCS_S) \
$(TOOL_DECODE_LIB_A_SRCS_C)
TOOL_DECODE_LIB_A_OBJS = \
$(TOOL_DECODE_LIB_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(TOOL_DECODE_LIB_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(TOOL_DECODE_LIB_A_SRCS_C:%.c=o/$(MODE)/%.o)
TOOL_DECODE_LIB_A_DIRECTDEPS = \
LIBC_FMT \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STUBS \
LIBC_STR \
LIBC_STDIO \
LIBC_SYSV \
LIBC_UNICODE
TOOL_DECODE_LIB_A_DEPS := \
$(call uniq,$(foreach x,$(TOOL_DECODE_LIB_A_DIRECTDEPS),$($(x))))
$(TOOL_DECODE_LIB_A): \
tool/decode/lib/ \
$(TOOL_DECODE_LIB_A).pkg \
$(TOOL_DECODE_LIB_A_OBJS)
$(TOOL_DECODE_LIB_A).pkg: \
$(TOOL_DECODE_LIB_A_OBJS) \
$(foreach x,$(TOOL_DECODE_LIB_A_DIRECTDEPS),$($(x)_A).pkg)
TOOL_DECODE_LIB_LIBS = $(foreach x,$(TOOL_DECODE_LIB_ARTIFACTS),$($(x)))
TOOL_DECODE_LIB_SRCS = $(foreach x,$(TOOL_DECODE_LIB_ARTIFACTS),$($(x)_SRCS))
TOOL_DECODE_LIB_HDRS = $(foreach x,$(TOOL_DECODE_LIB_ARTIFACTS),$($(x)_HDRS))
TOOL_DECODE_LIB_BINS = $(foreach x,$(TOOL_DECODE_LIB_ARTIFACTS),$($(x)_BINS))
TOOL_DECODE_LIB_CHECKS = $(foreach x,$(TOOL_DECODE_LIB_ARTIFACTS),$($(x)_CHECKS))
TOOL_DECODE_LIB_OBJS = $(foreach x,$(TOOL_DECODE_LIB_ARTIFACTS),$($(x)_OBJS))
TOOL_DECODE_LIB_TESTS = $(foreach x,$(TOOL_DECODE_LIB_ARTIFACTS),$($(x)_TESTS))
o/$(MODE)/tool/decode/lib/elfidnames.o \
o/$(MODE)/tool/decode/lib/machoidnames.o \
o/$(MODE)/tool/decode/lib/peidnames.o: \
DEFAULT_CFLAGS += \
-fdata-sections
.PHONY: o/$(MODE)/tool/decode/lib
o/$(MODE)/tool/decode/lib: $(TOOL_DECODE_LIB_CHECKS)

View file

@ -0,0 +1,60 @@
/*-*- 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/stdio/stdio.h"
#include "libc/str/str.h"
#include "tool/decode/lib/disassemblehex.h"
static size_t countzeroes(const uint8_t *data, size_t size) {
size_t i;
for (i = 0; i < size; ++i) {
if (data[i] != '\0') break;
}
return i;
}
void disassemblehex(uint8_t *data, size_t size, FILE *f) {
int col;
uint8_t ch;
size_t i, z;
char16_t glyphs[kDisassembleHexColumns + 1];
col = 0;
for (i = 0; i < size; ++i) {
ch = data[i];
if (!col) {
z = countzeroes(&data[i], size - i) / kDisassembleHexColumns;
if (z > 2) {
fprintf(f, "\t.%s\t%zu*%d\n", "zero", z, kDisassembleHexColumns);
i += z * kDisassembleHexColumns;
if (i == size) break;
}
fprintf(f, "\t.%s\t", "byte");
memset(glyphs, 0, sizeof(glyphs));
}
/* TODO(jart): Fix Emacs */
glyphs[col] = kCp437[ch == '"' || ch == '\\' || ch == '#' ? '.' : ch];
if (col) fputc(',', f);
fprintf(f, "0x%02x", ch);
if (++col == kDisassembleHexColumns) {
col = 0;
fprintf(f, "\t#%hs\n", glyphs);
}
}
if (col) fputc('\n', f);
}

View file

@ -0,0 +1,13 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_DISASSEMBLEHEX_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_DISASSEMBLEHEX_H_
#define kDisassembleHexColumns 8
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void disassemblehex(uint8_t *data, size_t size, FILE *f);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_DISASSEMBLEHEX_H_ */

View file

@ -0,0 +1,264 @@
/*-*- 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/elf.h"
#include "tool/decode/lib/elfidnames.h"
const struct IdName kElfTypeNames[] = {
{ET_NONE, "ET_NONE"},
{ET_REL, "ET_REL"},
{ET_EXEC, "ET_EXEC"},
{ET_DYN, "ET_DYN"},
{ET_CORE, "ET_CORE"},
{ET_NUM, "ET_NUM"},
{ET_LOOS, "ET_LOOS"},
{ET_HIOS, "ET_HIOS"},
{ET_LOPROC, "ET_LOPROC"},
{ET_HIPROC, "ET_HIPROC"},
{0, 0},
};
const struct IdName kElfOsabiNames[] = {
{ELFOSABI_NONE, "ELFOSABI_NONE"},
{ELFOSABI_SYSV, "ELFOSABI_SYSV"},
{ELFOSABI_HPUX, "ELFOSABI_HPUX"},
{ELFOSABI_NETBSD, "ELFOSABI_NETBSD"},
{ELFOSABI_GNU, "ELFOSABI_GNU"},
{ELFOSABI_LINUX, "ELFOSABI_LINUX"},
{ELFOSABI_SOLARIS, "ELFOSABI_SOLARIS"},
{ELFOSABI_AIX, "ELFOSABI_AIX"},
{ELFOSABI_IRIX, "ELFOSABI_IRIX"},
{ELFOSABI_FREEBSD, "ELFOSABI_FREEBSD"},
{ELFOSABI_TRU64, "ELFOSABI_TRU64"},
{ELFOSABI_MODESTO, "ELFOSABI_MODESTO"},
{ELFOSABI_OPENBSD, "ELFOSABI_OPENBSD"},
{ELFOSABI_ARM, "ELFOSABI_ARM"},
{ELFOSABI_STANDALONE, "ELFOSABI_STANDALONE"},
{0, 0},
};
const struct IdName kElfClassNames[] = {
{ELFCLASSNONE, "ELFCLASSNONE"},
{ELFCLASS32, "ELFCLASS32"},
{ELFCLASS64, "ELFCLASS64"},
{0, 0},
};
const struct IdName kElfDataNames[] = {
{ELFDATANONE, "ELFDATANONE"},
{ELFDATA2LSB, "ELFDATA2LSB"},
{ELFDATA2MSB, "ELFDATA2MSB"},
{0, 0},
};
const struct IdName kElfMachineNames[] = {
{EM_M32, "EM_M32"},
{EM_386, "EM_386"},
{EM_S390, "EM_S390"},
{EM_ARM, "EM_ARM"},
{EM_NEXGEN32E, "EM_NEXGEN32E"},
{EM_PDP11, "EM_PDP11"},
{EM_CRAYNV2, "EM_CRAYNV2"},
{EM_L10M, "EM_L10M"},
{EM_K10M, "EM_K10M"},
{EM_AARCH64, "EM_AARCH64"},
{EM_CUDA, "EM_CUDA"},
{EM_Z80, "EM_Z80"},
{EM_RISCV, "EM_RISCV"},
{EM_BPF, "EM_BPF"},
{0, 0},
};
const struct IdName kElfSegmentTypeNames[] = {
{PT_NULL, "PT_NULL"}, /* Program header table entry unused */
{PT_LOAD, "PT_LOAD"}, /* Loadable program segment */
{PT_DYNAMIC, "PT_DYNAMIC"}, /* Dynamic linking information */
{PT_INTERP, "PT_INTERP"}, /* Program interpreter */
{PT_NOTE, "PT_NOTE"}, /* Auxiliary information */
{PT_SHLIB, "PT_SHLIB"}, /* Reserved */
{PT_PHDR, "PT_PHDR"}, /* Entry for header table itself */
{PT_TLS, "PT_TLS"}, /* Thread-local storage segment */
{PT_NUM, "PT_NUM"}, /* Number of defined types */
{PT_LOOS, "PT_LOOS"}, /* Start of OS-specific */
{PT_GNU_EH_FRAME, "PT_GNU_EH_FRAME"}, /* GCC .eh_frame_hdr segment */
{PT_GNU_STACK, "PT_GNU_STACK"}, /* Indicates stack executability */
{PT_GNU_RELRO, "PT_GNU_RELRO"}, /* Read-only after relocation */
{PT_LOSUNW, "PT_LOSUNW"}, /* <Reserved for Sun Micrososystems> */
{PT_SUNWBSS, "PT_SUNWBSS"}, /* Sun Specific segment */
{PT_SUNWSTACK, "PT_SUNWSTACK"}, /* Stack segment */
{PT_HISUNW, "PT_HISUNW"}, /* </Reserved for Sun Micrososystems> */
{PT_HIOS, "PT_HIOS"}, /* End of OS-specific */
{PT_LOPROC, "PT_LOPROC"}, /* Start of processor-specific */
{PT_HIPROC, "PT_HIPROC"}, /* End of processor-specific */
{0, 0},
};
const struct IdName kElfSectionTypeNames[] = {
{SHT_NULL, "SHT_NULL"},
{SHT_PROGBITS, "SHT_PROGBITS"},
{SHT_SYMTAB, "SHT_SYMTAB"},
{SHT_STRTAB, "SHT_STRTAB"},
{SHT_RELA, "SHT_RELA"},
{SHT_HASH, "SHT_HASH"},
{SHT_DYNAMIC, "SHT_DYNAMIC"},
{SHT_NOTE, "SHT_NOTE"},
{SHT_NOBITS, "SHT_NOBITS"},
{SHT_REL, "SHT_REL"},
{SHT_SHLIB, "SHT_SHLIB"},
{SHT_DYNSYM, "SHT_DYNSYM"},
{SHT_INIT_ARRAY, "SHT_INIT_ARRAY"},
{SHT_FINI_ARRAY, "SHT_FINI_ARRAY"},
{SHT_PREINIT_ARRAY, "SHT_PREINIT_ARRAY"},
{SHT_GROUP, "SHT_GROUP"},
{SHT_SYMTAB_SHNDX, "SHT_SYMTAB_SHNDX"},
{SHT_NUM, "SHT_NUM"},
{SHT_LOOS, "SHT_LOOS"},
{SHT_GNU_ATTRIBUTES, "SHT_GNU_ATTRIBUTES"},
{SHT_GNU_HASH, "SHT_GNU_HASH"},
{SHT_GNU_LIBLIST, "SHT_GNU_LIBLIST"},
{SHT_CHECKSUM, "SHT_CHECKSUM"},
{SHT_LOSUNW, "SHT_LOSUNW"},
{SHT_SUNW_move, "SHT_SUNW_move"},
{SHT_SUNW_COMDAT, "SHT_SUNW_COMDAT"},
{SHT_SUNW_syminfo, "SHT_SUNW_syminfo"},
{SHT_GNU_verdef, "SHT_GNU_verdef"},
{SHT_GNU_verneed, "SHT_GNU_verneed"},
{SHT_GNU_versym, "SHT_GNU_versym"},
{SHT_HISUNW, "SHT_HISUNW"},
{SHT_HIOS, "SHT_HIOS"},
{SHT_LOPROC, "SHT_LOPROC"},
{SHT_HIPROC, "SHT_HIPROC"},
{SHT_LOUSER, "SHT_LOUSER"},
{SHT_HIUSER, "SHT_HIUSER"},
{0, 0},
};
const struct IdName kElfSegmentFlagNames[] = {
{PF_X, "PF_X"},
{PF_W, "PF_W"},
{PF_R, "PF_R"},
{PF_MASKOS, "PF_MASKOS"},
{PF_MASKPROC, "PF_MASKPROC"},
{0, 0},
};
const struct IdName kElfSectionFlagNames[] = {
{SHF_WRITE, "SHF_WRITE"},
{SHF_ALLOC, "SHF_ALLOC"},
{SHF_EXECINSTR, "SHF_EXECINSTR"},
{SHF_MERGE, "SHF_MERGE"},
{SHF_STRINGS, "SHF_STRINGS"},
{SHF_INFO_LINK, "SHF_INFO_LINK"},
{SHF_LINK_ORDER, "SHF_LINK_ORDER"},
{SHF_OS_NONCONFORMING, "SHF_OS_NONCONFORMING"},
{SHF_GROUP, "SHF_GROUP"},
{SHF_TLS, "SHF_TLS"},
{SHF_COMPRESSED, "SHF_COMPRESSED"},
{SHF_MASKOS, "SHF_MASKOS"},
{SHF_MASKPROC, "SHF_MASKPROC"},
{SHF_ORDERED, "SHF_ORDERED"},
{SHF_EXCLUDE, "SHF_EXCLUDE"},
{0, 0},
};
const struct IdName kElfSymbolTypeNames[] = {
{STT_NOTYPE, "STT_NOTYPE"}, {STT_OBJECT, "STT_OBJECT"},
{STT_FUNC, "STT_FUNC"}, {STT_SECTION, "STT_SECTION"},
{STT_FILE, "STT_FILE"}, {STT_COMMON, "STT_COMMON"},
{STT_TLS, "STT_TLS"}, {STT_NUM, "STT_NUM"},
{STT_LOOS, "STT_LOOS"}, {STT_GNU_IFUNC, "STT_GNU_IFUNC"},
{STT_HIOS, "STT_HIOS"}, {STT_LOPROC, "STT_LOPROC"},
{STT_HIPROC, "STT_HIPROC"}, {0, 0},
};
const struct IdName kElfSymbolBindNames[] = {
{STB_LOCAL, "STB_LOCAL"}, {STB_GLOBAL, "STB_GLOBAL"},
{STB_WEAK, "STB_WEAK"}, {STB_NUM, "STB_NUM"},
{STB_LOOS, "STB_LOOS"}, {STB_GNU_UNIQUE, "STB_GNU_UNIQUE"},
{STB_HIOS, "STB_HIOS"}, {STB_LOPROC, "STB_LOPROC"},
{STB_HIPROC, "STB_HIPROC"}, {0, 0},
};
const struct IdName kElfSymbolVisibilityNames[] = {
{STV_DEFAULT, "STV_DEFAULT"},
{STV_INTERNAL, "STV_INTERNAL"},
{STV_HIDDEN, "STV_HIDDEN"},
{STV_PROTECTED, "STV_PROTECTED"},
{0, 0},
};
const struct IdName kElfSpecialSectionNames[] = {
{SHN_UNDEF, "SHN_UNDEF"},
{SHN_LORESERVE, "SHN_LORESERVE"},
{SHN_LOPROC, "SHN_LOPROC"},
{SHN_BEFORE, "SHN_BEFORE"},
{SHN_AFTER, "SHN_AFTER"},
{SHN_HIPROC, "SHN_HIPROC"},
{SHN_LOOS, "SHN_LOOS"},
{SHN_HIOS, "SHN_HIOS"},
{SHN_ABS, "SHN_ABS"},
{SHN_COMMON, "SHN_COMMON"},
{SHN_XINDEX, "SHN_XINDEX"},
{SHN_HIRESERVE, "SHN_HIRESERVE"},
{0, 0},
};
const struct IdName kElfNexgen32eRelocationNames[] = {
{R_X86_64_64, "64"},
{R_X86_64_PC32, "PC32"},
{R_X86_64_GOT32, "GOT32"},
{R_X86_64_PLT32, "PLT32"},
{R_X86_64_COPY, "COPY"},
{R_X86_64_GLOB_DAT, "GLOB_DAT"},
{R_X86_64_JUMP_SLOT, "JUMP_SLOT"},
{R_X86_64_RELATIVE, "RELATIVE"},
{R_X86_64_GOTPCREL, "GOTPCREL"},
{R_X86_64_32, "32"},
{R_X86_64_32S, "32S"},
{R_X86_64_16, "16"},
{R_X86_64_PC16, "PC16"},
{R_X86_64_8, "8"},
{R_X86_64_PC8, "PC8"},
{R_X86_64_DTPMOD64, "DTPMOD64"},
{R_X86_64_DTPOFF64, "DTPOFF64"},
{R_X86_64_TPOFF64, "TPOFF64"},
{R_X86_64_TLSGD, "TLSGD"},
{R_X86_64_TLSLD, "TLSLD"},
{R_X86_64_DTPOFF32, "DTPOFF32"},
{R_X86_64_GOTTPOFF, "GOTTPOFF"},
{R_X86_64_TPOFF32, "TPOFF32"},
{R_X86_64_PC64, "PC64"},
{R_X86_64_GOTOFF64, "GOTOFF64"},
{R_X86_64_GOTPC32, "GOTPC32"},
{R_X86_64_GOT64, "GOT64"},
{R_X86_64_GOTPCREL64, "GOTPCREL64"},
{R_X86_64_GOTPC64, "GOTPC64"},
{R_X86_64_GOTPLT64, "GOTPLT64"},
{R_X86_64_PLTOFF64, "PLTOFF64"},
{R_X86_64_SIZE32, "SIZE32"},
{R_X86_64_SIZE64, "SIZE64"},
{R_X86_64_GOTPC32_TLSDESC, "GOTPC32_TLSDESC"},
{R_X86_64_TLSDESC_CALL, "TLSDESC_CALL"},
{R_X86_64_TLSDESC, "TLSDESC"},
{R_X86_64_IRELATIVE, "IRELATIVE"},
{R_X86_64_RELATIVE64, "RELATIVE64"},
{R_X86_64_GOTPCRELX, "GOTPCRELX"},
{R_X86_64_REX_GOTPCRELX, "REX_GOTPCRELX"},
{0, 0},
};

View file

@ -0,0 +1,24 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_ELFIDNAMES_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_ELFIDNAMES_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const struct IdName kElfTypeNames[];
extern const struct IdName kElfOsabiNames[];
extern const struct IdName kElfClassNames[];
extern const struct IdName kElfDataNames[];
extern const struct IdName kElfMachineNames[];
extern const struct IdName kElfSegmentTypeNames[];
extern const struct IdName kElfSectionTypeNames[];
extern const struct IdName kElfSegmentFlagNames[];
extern const struct IdName kElfSectionFlagNames[];
extern const struct IdName kElfSymbolTypeNames[];
extern const struct IdName kElfSymbolBindNames[];
extern const struct IdName kElfSymbolVisibilityNames[];
extern const struct IdName kElfSpecialSectionNames[];
extern const struct IdName kElfNexgen32eRelocationNames[];
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_ELFIDNAMES_H_ */

62
tool/decode/lib/flagger.c Normal file
View file

@ -0,0 +1,62 @@
/*-*- 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/fmt/fmt.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "tool/decode/lib/flagger.h"
struct FlagNameBuf {
size_t i, n;
char *p;
};
/**
* Formats numeric flags integer as symbolic code.
*
* @param names maps individual flags to string names in no order
* @param id is the flags
* @return NUL-terminated string that needs free()
*/
nodiscard char *recreateflags(const struct IdName *names, unsigned long id) {
struct FlagNameBuf buf = {};
char extrabuf[20];
bool first;
first = true;
for (; names->name; names++) {
if ((id == 0 && names->id == 0) ||
(id != 0 && names->id != 0 && (id & names->id) == names->id)) {
id &= ~names->id;
if (!first) {
append(&buf, "|");
} else {
first = false;
}
concat(&buf, names->name, strlen(names->name));
}
}
if (id) {
if (buf.i) append(&buf, "|");
concat(&buf, extrabuf, snprintf(extrabuf, sizeof(extrabuf), "%#x", id));
} else if (!buf.i) {
append(&buf, "0");
}
return buf.p;
}

11
tool/decode/lib/flagger.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_FLAGGER_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_FLAGGER_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
char *recreateflags(const struct IdName *, unsigned long) nodiscard;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_FLAGGER_H_ */

29
tool/decode/lib/idname.c Normal file
View file

@ -0,0 +1,29 @@
/*-*- 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/decode/lib/idname.h"
const char *findnamebyid(const struct IdName *names, unsigned long id) {
for (; names->name; names++) {
if (names->id == id) {
return names->name;
}
}
return NULL;
}

15
tool/decode/lib/idname.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_IDNAME_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_IDNAME_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct IdName {
unsigned long id;
const char *const name;
};
const char *findnamebyid(const struct IdName *names, unsigned long id);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_IDNAME_H_ */

View file

@ -0,0 +1,150 @@
/*-*- 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/macho.h"
#include "tool/decode/lib/machoidnames.h"
const struct IdName kMachoFileTypeNames[] = {
{MAC_OBJECT, "MAC_OBJECT"},
{MAC_EXECUTE, "MAC_EXECUTE"},
{MAC_FVMLIB, "MAC_FVMLIB"},
{MAC_CORE, "MAC_CORE"},
{MAC_PRELOAD, "MAC_PRELOAD"},
{MAC_DYLIB, "MAC_DYLIB"},
{MAC_DYLINKER, "MAC_DYLINKER"},
{MAC_BUNDLE, "MAC_BUNDLE"},
{0, 0},
};
const struct IdName kMachoFlagNames[] = {
{MAC_NOUNDEFS, "MAC_NOUNDEFS"},
{MAC_INCRLINK, "MAC_INCRLINK"},
{MAC_DYLDLINK, "MAC_DYLDLINK"},
{MAC_BINDATLOAD, "MAC_BINDATLOAD"},
{MAC_PREBOUND, "MAC_PREBOUND"},
{MAC_SPLIT_SEGS, "MAC_SPLIT_SEGS"},
{MAC_LAZY_INIT, "MAC_LAZY_INIT"},
{MAC_TWOLEVEL, "MAC_TWOLEVEL"},
{MAC_FORCE_FLAT, "MAC_FORCE_FLAT"},
{MAC_NOMULTIDEFS, "MAC_NOMULTIDEFS"},
{MAC_NOFIXPREBINDING, "MAC_NOFIXPREBINDING"},
{MAC_PREBINDABLE, "MAC_PREBINDABLE"},
{MAC_ALLMODSBOUND, "MAC_ALLMODSBOUND"},
{MAC_SUBSECTIONS_VIA_SYMBOLS, "MAC_SUBSECTIONS_VIA_SYMBOLS"},
{MAC_CANONICAL, "MAC_CANONICAL"},
{0, 0},
};
const struct IdName kMachoSegmentFlagNames[] = {
{MAC_SG_HIGHVM, "MAC_SG_HIGHVM"},
{MAC_SG_FVMLIB, "MAC_SG_FVMLIB"},
{MAC_SG_NORELOC, "MAC_SG_NORELOC"},
{0, 0},
};
const struct IdName kMachoSectionTypeNames[] = {
{MAC_S_REGULAR, "MAC_S_REGULAR"},
{MAC_S_ZEROFILL, "MAC_S_ZEROFILL"},
{MAC_S_CSTRING_LITERALS, "MAC_S_CSTRING_LITERALS"},
{MAC_S_4BYTE_LITERALS, "MAC_S_4BYTE_LITERALS"},
{MAC_S_8BYTE_LITERALS, "MAC_S_8BYTE_LITERALS"},
{MAC_S_LITERAL_POINTERS, "MAC_S_LITERAL_POINTERS"},
{MAC_S_NON_LAZY_SYMBOL_POINTERS, "MAC_S_NON_LAZY_SYMBOL_POINTERS"},
{MAC_S_LAZY_SYMBOL_POINTERS, "MAC_S_LAZY_SYMBOL_POINTERS"},
{MAC_S_SYMBOL_STUBS, "MAC_S_SYMBOL_STUBS"},
{MAC_S_MOD_INIT_FUNC_POINTERS, "MAC_S_MOD_INIT_FUNC_POINTERS"},
{MAC_S_MOD_TERM_FUNC_POINTERS, "MAC_S_MOD_TERM_FUNC_POINTERS"},
{MAC_S_COALESCED, "MAC_S_COALESCED"},
{MAC_S_GB_ZEROFILL, "MAC_S_GB_ZEROFILL"},
{MAC_S_INTERPOSING, "MAC_S_INTERPOSING"},
{MAC_S_16BYTE_LITERALS, "MAC_S_16BYTE_LITERALS"},
{0, 0},
};
const struct IdName kMachoSectionAttributeNames[] = {
{MAC_SECTION_ATTRIBUTES_USR, "MAC_SECTION_ATTRIBUTES_USR"},
{MAC_S_ATTR_PURE_INSTRUCTIONS, "MAC_S_ATTR_PURE_INSTRUCTIONS"},
{MAC_S_ATTR_NO_TOC, "MAC_S_ATTR_NO_TOC"},
{MAC_S_ATTR_STRIP_STATIC_SYMS, "MAC_S_ATTR_STRIP_STATIC_SYMS"},
{MAC_S_ATTR_NO_DEAD_STRIP, "MAC_S_ATTR_NO_DEAD_STRIP"},
{MAC_S_ATTR_LIVE_SUPPORT, "MAC_S_ATTR_LIVE_SUPPORT"},
{MAC_S_ATTR_SELF_MODIFYING_CODE, "MAC_S_ATTR_SELF_MODIFYING_CODE"},
{MAC_S_ATTR_DEBUG, "MAC_S_ATTR_DEBUG"},
{MAC_SECTION_ATTRIBUTES_SYS, "MAC_SECTION_ATTRIBUTES_SYS"},
{MAC_S_ATTR_SOME_INSTRUCTIONS, "MAC_S_ATTR_SOME_INSTRUCTIONS"},
{MAC_S_ATTR_EXT_RELOC, "MAC_S_ATTR_EXT_RELOC"},
{MAC_S_ATTR_LOC_RELOC, "MAC_S_ATTR_LOC_RELOC"},
{0, 0},
};
const struct IdName kMachoLoadCommandNames[] = {
{MAC_LC_REQ_DYLD, "MAC_LC_REQ_DYLD"},
{MAC_LC_SEGMENT, "MAC_LC_SEGMENT"},
{MAC_LC_SYMTAB, "MAC_LC_SYMTAB"},
{MAC_LC_SYMSEG, "MAC_LC_SYMSEG"},
{MAC_LC_THREAD, "MAC_LC_THREAD"},
{MAC_LC_UNIXTHREAD, "MAC_LC_UNIXTHREAD"},
{MAC_LC_LOADFVMLIB, "MAC_LC_LOADFVMLIB"},
{MAC_LC_IDFVMLIB, "MAC_LC_IDFVMLIB"},
{MAC_LC_IDENT, "MAC_LC_IDENT"},
{MAC_LC_FVMFILE, "MAC_LC_FVMFILE"},
{MAC_LC_PREPAGE, "MAC_LC_PREPAGE"},
{MAC_LC_DYSYMTAB, "MAC_LC_DYSYMTAB"},
{MAC_LC_LOAD_DYLIB, "MAC_LC_LOAD_DYLIB"},
{MAC_LC_ID_DYLIB, "MAC_LC_ID_DYLIB"},
{MAC_LC_LOAD_DYLINKER, "MAC_LC_LOAD_DYLINKER"},
{MAC_LC_ID_DYLINKER, "MAC_LC_ID_DYLINKER"},
{MAC_LC_PREBOUND_DYLIB, "MAC_LC_PREBOUND_DYLIB"},
{MAC_LC_ROUTINES, "MAC_LC_ROUTINES"},
{MAC_LC_SUB_FRAMEWORK, "MAC_LC_SUB_FRAMEWORK"},
{MAC_LC_SUB_UMBRELLA, "MAC_LC_SUB_UMBRELLA"},
{MAC_LC_SUB_CLIENT, "MAC_LC_SUB_CLIENT"},
{MAC_LC_SUB_LIBRARY, "MAC_LC_SUB_LIBRARY"},
{MAC_LC_TWOLEVEL_HINTS, "MAC_LC_TWOLEVEL_HINTS"},
{MAC_LC_PREBIND_CKSUM, "MAC_LC_PREBIND_CKSUM"},
{MAC_LC_LOAD_WEAK_DYLIB, "MAC_LC_LOAD_WEAK_DYLIB"},
{MAC_LC_SEGMENT_64, "MAC_LC_SEGMENT_64"},
{MAC_LC_ROUTINES_64, "MAC_LC_ROUTINES_64"},
{MAC_LC_UUID, "MAC_LC_UUID"},
{MAC_LC_CODE_SIGNATURE, "MAC_LC_CODE_SIGNATURE"},
{MAC_LC_SEGMENT_SPLIT_INFO, "MAC_LC_SEGMENT_SPLIT_INFO"},
{MAC_LC_LAZY_LOAD_DYLIB, "MAC_LC_LAZY_LOAD_DYLIB"},
{MAC_LC_ENCRYPTION_INFO, "MAC_LC_ENCRYPTION_INFO"},
{MAC_LC_DYLD_INFO, "MAC_LC_DYLD_INFO"},
{MAC_LC_VERSION_MIN_MACOSX, "MAC_LC_VERSION_MIN_MACOSX"},
{MAC_LC_VERSION_MIN_IPHONEOS, "MAC_LC_VERSION_MIN_IPHONEOS"},
{MAC_LC_FUNCTION_STARTS, "MAC_LC_FUNCTION_STARTS"},
{MAC_LC_DYLD_ENVIRONMENT, "MAC_LC_DYLD_ENVIRONMENT"},
{MAC_LC_DATA_IN_CODE, "MAC_LC_DATA_IN_CODE"},
{MAC_LC_SOURCE_VERSION, "MAC_LC_SOURCE_VERSION"},
{MAC_LC_RPATH, "MAC_LC_RPATH"},
{MAC_LC_MAIN, "MAC_LC_MAIN"},
{0, 0},
};
const struct IdName kMachoVmProtNames[] = {
{VM_PROT_READ, "VM_PROT_READ"},
{VM_PROT_WRITE, "VM_PROT_WRITE"},
{VM_PROT_EXECUTE, "VM_PROT_EXECUTE"},
{VM_PROT_NO_CHANGE, "VM_PROT_NO_CHANGE"},
{VM_PROT_COPY, "VM_PROT_COPY"},
{VM_PROT_TRUSTED, "VM_PROT_TRUSTED"},
{VM_PROT_STRIP_READ, "VM_PROT_STRIP_READ"},
{0, 0},
};

View file

@ -0,0 +1,17 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_MACHOIDNAMES_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_MACHOIDNAMES_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const struct IdName kMachoFileTypeNames[];
extern const struct IdName kMachoFlagNames[];
extern const struct IdName kMachoSegmentFlagNames[];
extern const struct IdName kMachoSectionTypeNames[];
extern const struct IdName kMachoSectionAttributeNames[];
extern const struct IdName kMachoLoadCommandNames[];
extern const struct IdName kMachoVmProtNames[];
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_MACHOIDNAMES_H_ */

View file

@ -0,0 +1,51 @@
/*-*- 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/nt/enum/fileflagandattributes.h"
#include "tool/decode/lib/ntfileflagnames.h"
const struct IdName kNtFileFlagNames[] = {
{kNtFileAttributeReadonly, "kNtFileAttributeReadonly"},
{kNtFileAttributeHidden, "kNtFileAttributeHidden"},
{kNtFileAttributeSystem, "kNtFileAttributeSystem"},
{kNtFileAttributeVolumelabel, "kNtFileAttributeVolumelabel"},
{kNtFileAttributeDirectory, "kNtFileAttributeDirectory"},
{kNtFileAttributeArchive, "kNtFileAttributeArchive"},
{kNtFileAttributeDevice, "kNtFileAttributeDevice"},
{kNtFileAttributeNormal, "kNtFileAttributeNormal"},
{kNtFileAttributeTemporary, "kNtFileAttributeTemporary"},
{kNtFileAttributeSparseFile, "kNtFileAttributeSparseFile"},
{kNtFileAttributeReparsePoint, "kNtFileAttributeReparsePoint"},
{kNtFileAttributeCompressed, "kNtFileAttributeCompressed"},
{kNtFileAttributeOffline, "kNtFileAttributeOffline"},
{kNtFileAttributeNotContentIndexed, "kNtFileAttributeNotContentIndexed"},
{kNtFileAttributeEncrypted, "kNtFileAttributeEncrypted"},
{kNtFileFlagWriteThrough, "kNtFileFlagWriteThrough"},
{kNtFileFlagOverlapped, "kNtFileFlagOverlapped"},
{kNtFileFlagNoBuffering, "kNtFileFlagNoBuffering"},
{kNtFileFlagRandomAccess, "kNtFileFlagRandomAccess"},
{kNtFileFlagSequentialScan, "kNtFileFlagSequentialScan"},
{kNtFileFlagDeleteOnClose, "kNtFileFlagDeleteOnClose"},
{kNtFileFlagBackupSemantics, "kNtFileFlagBackupSemantics"},
{kNtFileFlagPosixSemantics, "kNtFileFlagPosixSemantics"},
{kNtFileFlagOpenReparsePoint, "kNtFileFlagOpenReparsePoint"},
{kNtFileFlagOpenNoRecall, "kNtFileFlagOpenNoRecall"},
{kNtFileFlagFirstPipeInstance, "kNtFileFlagFirstPipeInstance"},
{0, 0},
};

View file

@ -0,0 +1,11 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_NTFILEFLAGNAMES_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_NTFILEFLAGNAMES_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const struct IdName kNtFileFlagNames[];
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_NTFILEFLAGNAMES_H_ */

179
tool/decode/lib/peidnames.c Normal file
View file

@ -0,0 +1,179 @@
/*-*- 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/pe.h"
#include "tool/decode/lib/peidnames.h"
const struct IdName kNtImageFileMachineNames[] = {
{kNtImageFileMachineUnknown, "kNtImageFileMachineUnknown"},
{kNtImageFileMachineTargetHost, "kNtImageFileMachineTargetHost"},
{kNtImageFileMachineI386, "kNtImageFileMachineI386"},
{kNtImageFileMachineR3000, "kNtImageFileMachineR3000"},
{kNtImageFileMachineR4000, "kNtImageFileMachineR4000"},
{kNtImageFileMachineR10000, "kNtImageFileMachineR10000"},
{kNtImageFileMachineWcemipsv2, "kNtImageFileMachineWcemipsv2"},
{kNtImageFileMachineAlpha, "kNtImageFileMachineAlpha"},
{kNtImageFileMachineSh3, "kNtImageFileMachineSh3"},
{kNtImageFileMachineSh3dsp, "kNtImageFileMachineSh3dsp"},
{kNtImageFileMachineSh3e, "kNtImageFileMachineSh3e"},
{kNtImageFileMachineSh4, "kNtImageFileMachineSh4"},
{kNtImageFileMachineSh5, "kNtImageFileMachineSh5"},
{kNtImageFileMachineArm, "kNtImageFileMachineArm"},
{kNtImageFileMachineThumb, "kNtImageFileMachineThumb"},
{kNtImageFileMachineArmnt, "kNtImageFileMachineArmnt"},
{kNtImageFileMachineAm33, "kNtImageFileMachineAm33"},
{kNtImageFileMachinePowerpc, "kNtImageFileMachinePowerpc"},
{kNtImageFileMachinePowerpcfp, "kNtImageFileMachinePowerpcfp"},
{kNtImageFileMachineIa64, "kNtImageFileMachineIa64"},
{kNtImageFileMachineMips16, "kNtImageFileMachineMips16"},
{kNtImageFileMachineAlpha64, "kNtImageFileMachineAlpha64"},
{kNtImageFileMachineMipsfpu, "kNtImageFileMachineMipsfpu"},
{kNtImageFileMachineMipsfpu16, "kNtImageFileMachineMipsfpu16"},
{kNtImageFileMachineAxp64, "kNtImageFileMachineAxp64"},
{kNtImageFileMachineTricore, "kNtImageFileMachineTricore"},
{kNtImageFileMachineCef, "kNtImageFileMachineCef"},
{kNtImageFileMachineEbc, "kNtImageFileMachineEbc"},
{kNtImageFileMachineNexgen32e, "kNtImageFileMachineNexgen32e"},
{kNtImageFileMachineM32r, "kNtImageFileMachineM32r"},
{kNtImageFileMachineArm64, "kNtImageFileMachineArm64"},
{kNtImageFileMachineCee, "kNtImageFileMachineCee"},
{0, 0},
};
const struct IdName kNtPeOptionalHeaderMagicNames[] = {
{kNtPe32bit, "kNtPe32bit"},
{kNtPe64bit, "kNtPe64bit"},
{0, 0},
};
const struct IdName kNtImageDllcharacteristicNames[] = {
{kNtImageDllcharacteristicsHighEntropyVa,
"kNtImageDllcharacteristicsHighEntropyVa"},
{kNtImageDllcharacteristicsDynamicBase,
"kNtImageDllcharacteristicsDynamicBase"},
{kNtImageDllcharacteristicsForceIntegrity,
"kNtImageDllcharacteristicsForceIntegrity"},
{kNtImageDllcharacteristicsNxCompat, "kNtImageDllcharacteristicsNxCompat"},
{kNtImageDllcharacteristicsNoIsolation,
"kNtImageDllcharacteristicsNoIsolation"},
{kNtImageDllcharacteristicsNoSeh, "kNtImageDllcharacteristicsNoSeh"},
{kNtImageDllcharacteristicsNoBind, "kNtImageDllcharacteristicsNoBind"},
{kNtImageDllcharacteristicsAppcontainer,
"kNtImageDllcharacteristicsAppcontainer"},
{kNtImageDllcharacteristicsWdmDriver,
"kNtImageDllcharacteristicsWdmDriver"},
{kNtImageDllcharacteristicsGuardCf, "kNtImageDllcharacteristicsGuardCf"},
{kNtImageDllcharacteristicsTerminalServerAware,
"kNtImageDllcharacteristicsTerminalServerAware"},
{0, 0},
};
const struct IdName kNtImageSubsystemNames[] = {
{kNtImageSubsystemUnknown, "kNtImageSubsystemUnknown"},
{kNtImageSubsystemNative, "kNtImageSubsystemNative"},
{kNtImageSubsystemWindowsGui, "kNtImageSubsystemWindowsGui"},
{kNtImageSubsystemWindowsCui, "kNtImageSubsystemWindowsCui"},
{kNtImageSubsystemOs2Cui, "kNtImageSubsystemOs2Cui"},
{kNtImageSubsystemPosixCui, "kNtImageSubsystemPosixCui"},
{kNtImageSubsystemNativeWindows, "kNtImageSubsystemNativeWindows"},
{kNtImageSubsystemWindowsCeGui, "kNtImageSubsystemWindowsCeGui"},
{kNtImageSubsystemEfiApplication, "kNtImageSubsystemEfiApplication"},
{kNtImageSubsystemEfiBootServiceDriver,
"kNtImageSubsystemEfiBootServiceDriver"},
{kNtImageSubsystemEfiRuntimeDriver, "kNtImageSubsystemEfiRuntimeDriver"},
{kNtImageSubsystemEfiRom, "kNtImageSubsystemEfiRom"},
{kNtImageSubsystemXbox, "kNtImageSubsystemXbox"},
{kNtImageSubsystemWindowsBootApplication,
"kNtImageSubsystemWindowsBootApplication"},
{kNtImageSubsystemXboxCodeCatalog, "kNtImageSubsystemXboxCodeCatalog"},
{0, 0},
};
const struct IdName kNtImageScnNames[] = {
{kNtImageScnTypeNoPad, "kNtImageScnTypeNoPad"},
{kNtImageScnCntCode, "kNtImageScnCntCode"},
{kNtImageScnCntInitializedData, "kNtImageScnCntInitializedData"},
{kNtImageScnCntUninitializedData, "kNtImageScnCntUninitializedData"},
{kNtImageScnLnkOther, "kNtImageScnLnkOther"},
{kNtImageScnLnkInfo, "kNtImageScnLnkInfo"},
{kNtImageScnLnkRemove, "kNtImageScnLnkRemove"},
{kNtImageScnLnkComdat, "kNtImageScnLnkComdat"},
{kNtImageScnNoDeferSpecExc, "kNtImageScnNoDeferSpecExc"},
{kNtImageScnGprel, "kNtImageScnGprel"},
{kNtImageScnMemFardata, "kNtImageScnMemFardata"},
{kNtImageScnMemPurgeable, "kNtImageScnMemPurgeable"},
{kNtImageScnMem16bit, "kNtImageScnMem16bit"},
{kNtImageScnMemLocked, "kNtImageScnMemLocked"},
{kNtImageScnMemPreload, "kNtImageScnMemPreload"},
{0, 0},
};
const struct IdName kNtImageDirectoryEntryNames[] = {
{kNtImageDirectoryEntryExport, "kNtImageDirectoryEntryExport"},
{kNtImageDirectoryEntryImport, "kNtImageDirectoryEntryImport"},
{kNtImageDirectoryEntryResource, "kNtImageDirectoryEntryResource"},
{kNtImageDirectoryEntryException, "kNtImageDirectoryEntryException"},
{kNtImageDirectoryEntrySecurity, "kNtImageDirectoryEntrySecurity"},
{kNtImageDirectoryEntryBasereloc, "kNtImageDirectoryEntryBasereloc"},
{kNtImageDirectoryEntryDebug, "kNtImageDirectoryEntryDebug"},
{kNtImageDirectoryEntryArchitecture, "kNtImageDirectoryEntryArchitecture"},
{kNtImageDirectoryEntryGlobalptr, "kNtImageDirectoryEntryGlobalptr"},
{kNtImageDirectoryEntryTls, "kNtImageDirectoryEntryTls"},
{kNtImageDirectoryEntryLoadConfig, "kNtImageDirectoryEntryLoadConfig"},
{kNtImageDirectoryEntryBoundImport, "kNtImageDirectoryEntryBoundImport"},
{kNtImageDirectoryEntryIat, "kNtImageDirectoryEntryIat"},
{kNtImageDirectoryEntryDelayImport, "kNtImageDirectoryEntryDelayImport"},
{kNtImageDirectoryEntryComDescriptor,
"kNtImageDirectoryEntryComDescriptor"},
{0, 0},
};
const struct IdName kNtImageCharacteristicNames[] = {
{kNtImageFileRelocsStripped, "kNtImageFileRelocsStripped"},
{kNtImageFileExecutableImage, "kNtImageFileExecutableImage"},
{kNtImageFileLineNumsStripped, "kNtImageFileLineNumsStripped"},
{kNtImageFileLocalSymsStripped, "kNtImageFileLocalSymsStripped"},
{kNtImageFileAggresiveWsTrim, "kNtImageFileAggresiveWsTrim"},
{kNtImageFileLargeAddressAware, "kNtImageFileLargeAddressAware"},
{kNtImageFileBytesReversedLo, "kNtImageFileBytesReversedLo"},
{kNtImageFile32bitMachine, "kNtImageFile32bitMachine"},
{kNtImageFileDebugStripped, "kNtImageFileDebugStripped"},
{kNtImageFileRemovableRunFromSwap, "kNtImageFileRemovableRunFromSwap"},
{kNtImageFileNetRunFromSwap, "kNtImageFileNetRunFromSwap"},
{kNtImageFileSystem, "kNtImageFileSystem"},
{kNtImageFileDll, "kNtImageFileDll"},
{kNtImageFileUpSystemOnly, "kNtImageFileUpSystemOnly"},
{kNtImageFileBytesReversedHi, "kNtImageFileBytesReversedHi"},
{0, 0},
};
const struct IdName kNtPeSectionNames[] = {
{kNtPeSectionCntCode, "kNtPeSectionCntCode"},
{kNtPeSectionCntInitializedData, "kNtPeSectionCntInitializedData"},
{kNtPeSectionCntUninitializedData, "kNtPeSectionCntUninitializedData"},
{kNtPeSectionGprel, "kNtPeSectionGprel"},
{kNtPeSectionMemDiscardable, "kNtPeSectionMemDiscardable"},
{kNtPeSectionMemNotCached, "kNtPeSectionMemNotCached"},
{kNtPeSectionMemNotPaged, "kNtPeSectionMemNotPaged"},
{kNtPeSectionMemShared, "kNtPeSectionMemShared"},
{kNtPeSectionMemExecute, "kNtPeSectionMemExecute"},
{kNtPeSectionMemRead, "kNtPeSectionMemRead"},
{kNtPeSectionMemWrite, "kNtPeSectionMemWrite"},
{0, 0},
};

View file

@ -0,0 +1,18 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_PEIDNAMES_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_PEIDNAMES_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const struct IdName kNtImageFileMachineNames[];
extern const struct IdName kNtPeOptionalHeaderMagicNames[];
extern const struct IdName kNtImageCharacteristicNames[];
extern const struct IdName kNtImageDllcharacteristicNames[];
extern const struct IdName kNtImageSubsystemNames[];
extern const struct IdName kNtImageScnNames[];
extern const struct IdName kNtImageDirectoryEntryNames[];
extern const struct IdName kNtPeSectionNames[];
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_PEIDNAMES_H_ */

View file

@ -0,0 +1,73 @@
/*-*- mode:asm; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 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 "ape/relocations.h"
#include "ape/relocations.h"
#include "libc/macros.h"
.Lrows = 0 # w/ 2 cols
.macro .tab sym:req str
.pushsection .rodata.str1.1,"aSM",@progbits,1
.L\@: .asciz "\str"
.popsection
.long RVA(\sym)
.long RVA(.L\@)
.Lrows = .Lrows + 1
.endm
.initro 301,_init_kPollNames
kPollNamesRo:
.tab POLLNVAL "POLLNVAL"
.tab POLLWRNORM "POLLWRNORM"
.tab POLLWRBAND "POLLWRBAND"
.tab POLLRDNORM "POLLRDNORM"
.tab POLLRDHUP "POLLRDHUP"
.tab POLLRDBAND "POLLRDBAND"
.tab POLLHUP "POLLHUP"
.tab POLLERR "POLLERR"
.tab POLLPRI "POLLPRI"
.tab POLLOUT "POLLOUT"
.tab POLLIN "POLLIN"
.endobj kPollNamesRo,globl,hidden
.previous
/ Mapping of poll() flags to their string names.
/ @see recreateflags()
.initbss 301,_init_kPollNames
kPollNames:
.rept .Lrows
.quad 0 # unsigned long id
.quad 0 # const char *const name
.endr
.quad 0,0 # terminator row
.endobj kPollNames,globl
.previous
.init.start 301,_init_kPollNames
pushpop .Lrows,%rcx # relocate ROBSS b/c -fPIE crap
0: lodsl
mov (%rbx,%rax),%rax # read what systemfive.S decoded
stosq
lodsl
add %rbx,%rax # %rbx is image base (cosmo abi)
stosq
loop 0b
add $16,%rdi
.init.end 301,_init_kPollNames

View file

@ -0,0 +1,11 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_POLLNAMES_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_POLLNAMES_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern struct IdName kPollNames[];
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_POLLNAMES_H_ */

View file

@ -0,0 +1,55 @@
/*-*- 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/dns/dns.h"
#include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ai.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/sock.h"
#include "tool/decode/lib/socknames.h"
const struct IdName kAddressFamilyNames[] = {
{AF_UNSPEC, "AF_UNSPEC"},
{AF_UNIX, "AF_UNIX"},
{AF_INET, "AF_INET"},
{0, 0},
};
const struct IdName kSockTypeNames[] = {
{SOCK_STREAM, "SOCK_STREAM"},
{SOCK_DGRAM, "SOCK_DGRAM"},
{SOCK_RAW, "SOCK_RAW"},
{SOCK_RDM, "SOCK_RDM"},
{SOCK_SEQPACKET, "SOCK_SEQPACKET"},
{0, 0},
};
const struct IdName kAddrInfoFlagNames[] = {
{AI_PASSIVE, "AI_PASSIVE"},
{AI_CANONNAME, "AI_CANONNAME"},
{AI_NUMERICHOST, "AI_NUMERICHOST"},
{0, 0},
};
const struct IdName kProtocolNames[] = {
{IPPROTO_IP, "IPPROTO_IP"}, {IPPROTO_ICMP, "IPPROTO_ICMP"},
{IPPROTO_TCP, "IPPROTO_TCP"}, {IPPROTO_UDP, "IPPROTO_UDP"},
{IPPROTO_RAW, "IPPROTO_RAW"}, {0, 0},
};

View file

@ -0,0 +1,14 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_SOCKNAMES_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_SOCKNAMES_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const struct IdName kAddressFamilyNames[];
extern const struct IdName kSockTypeNames[];
extern const struct IdName kAddrInfoFlagNames[];
extern const struct IdName kProtocolNames[];
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_SOCKNAMES_H_ */

View file

@ -0,0 +1,60 @@
/*-*- 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/fmt/fmt.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "tool/decode/lib/titlegen.h"
const struct Modeline kModelineAsm = {
" mode:asm; indent-tabs-mode:t; tab-width:8; coding:utf-8 ",
" set et ft=asm ts=8 sw=8 fenc=utf-8 "};
/**
* Displays one of those ANSI block source dividers we love so much.
*/
void showtitle(const char *brand, const char *tool, const char *title,
const char *description, const struct Modeline *modeline) {
char buf[512], *p;
p = stpcpy(buf, brand);
if (tool) {
p = stpcpy(stpcpy(p, " § "), tool);
if (title) {
p = stpcpy(stpcpy(p, " » "), title);
}
}
printf("/*");
if (modeline) {
printf("-*-%-71s-*-│\n│vi:%-72s:vi│\n", modeline->emacs, modeline->vim);
for (unsigned i = 0; i < 78; ++i) printf("");
printf("\n│ %-76s ", buf);
} else {
for (unsigned i = 0; i < 75; ++i) printf("");
printf("│─╗\n│ %-73s ─╬─", buf);
}
printf("\n╚─");
for (unsigned i = 0; i < 75; ++i) printf("");
printf("%s", modeline ? "" : "");
if (description) {
/* TODO(jart): paragraph fill */
printf("─╝\n%s ", description);
} else {
}
printf("*/\n");
}

View file

@ -0,0 +1,18 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_TITLEGEN_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_TITLEGEN_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct Modeline {
const char *emacs;
const char *vim;
};
extern const struct Modeline kModelineAsm;
void showtitle(const char *brand, const char *tool, const char *title,
const char *description, const struct Modeline *modeline);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_TITLEGEN_H_ */

View file

@ -0,0 +1,29 @@
/*-*- 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/nexgen32e/x86info.h"
#include "tool/decode/lib/x86idnames.h"
const struct IdName kX86GradeNames[] = {
{X86_GRADE_UNKNOWN, "Unknown"}, {X86_GRADE_APPLIANCE, "Appliance"},
{X86_GRADE_MOBILE, "Mobile"}, {X86_GRADE_TABLET, "Tablet"},
{X86_GRADE_DESKTOP, "Desktop"}, {X86_GRADE_CLIENT, "Client"},
{X86_GRADE_DENSITY, "Density"}, {X86_GRADE_SERVER, "Server"},
{X86_GRADE_SCIENCE, "Science"}, {0, 0},
};

View file

@ -0,0 +1,12 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_X86IDNAMES_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_X86IDNAMES_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const struct IdName kX86MarchNames[];
extern const struct IdName kX86GradeNames[];
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_X86IDNAMES_H_ */

View file

@ -0,0 +1,47 @@
/*-*- 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/nexgen32e/x86info.h"
#include "tool/decode/lib/x86idnames.h"
const struct IdName kX86MarchNames[] = {
{X86_MARCH_UNKNOWN, "Unknown"},
{X86_MARCH_CORE2, "Core 2"},
{X86_MARCH_NEHALEM, "Nehalem"},
{X86_MARCH_WESTMERE, "Westmere"},
{X86_MARCH_SANDYBRIDGE, "Sandybridge"},
{X86_MARCH_IVYBRIDGE, "Ivybridge"},
{X86_MARCH_HASWELL, "Haswell"},
{X86_MARCH_BROADWELL, "Broadwell"},
{X86_MARCH_SKYLAKE, "Skylake"},
{X86_MARCH_KABYLAKE, "Kabylake"},
{X86_MARCH_CANNONLAKE, "Cannonlake"},
{X86_MARCH_ICELAKE, "Icelake"},
{X86_MARCH_TIGERLAKE, "Tigerlake"},
{X86_MARCH_BONNELL, "Bonnell"},
{X86_MARCH_SALTWELL, "Saltwell"},
{X86_MARCH_SILVERMONT, "Silvermont"},
{X86_MARCH_AIRMONT, "Airmont"},
{X86_MARCH_GOLDMONT, "Goldmont"},
{X86_MARCH_GOLDMONTPLUS, "Goldmont Plus"},
{X86_MARCH_TREMONT, "Tremont"},
{X86_MARCH_KNIGHTSLANDING, "Knights Landing"},
{X86_MARCH_KNIGHTSMILL, "Knights Mill"},
{0, 0},
};

View file

@ -0,0 +1,47 @@
/*-*- 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 "third_party/xed/x86.h"
#include "tool/decode/lib/idname.h"
const struct IdName kXedErrorNames[] = {
{XED_ERROR_NONE, "NONE"},
{XED_ERROR_BUFFER_TOO_SHORT, "BUFFER_TOO_SHORT"},
{XED_ERROR_GENERAL_ERROR, "GENERAL_ERROR"},
{XED_ERROR_INVALID_FOR_CHIP, "INVALID_FOR_CHIP"},
{XED_ERROR_BAD_REGISTER, "BAD_REGISTER"},
{XED_ERROR_BAD_LOCK_PREFIX, "BAD_LOCK_PREFIX"},
{XED_ERROR_BAD_REP_PREFIX, "BAD_REP_PREFIX"},
{XED_ERROR_BAD_LEGACY_PREFIX, "BAD_LEGACY_PREFIX"},
{XED_ERROR_BAD_REX_PREFIX, "BAD_REX_PREFIX"},
{XED_ERROR_BAD_EVEX_UBIT, "BAD_EVEX_UBIT"},
{XED_ERROR_BAD_MAP, "BAD_MAP"},
{XED_ERROR_BAD_EVEX_V_PRIME, "BAD_EVEX_V_PRIME"},
{XED_ERROR_BAD_EVEX_Z_NO_MASKING, "BAD_EVEX_Z_NO_MASKING"},
{XED_ERROR_NO_OUTPUT_POINTER, "NO_OUTPUT_POINTER"},
{XED_ERROR_NO_AGEN_CALL_BACK_REGISTERED, "NO_AGEN_CALL_BACK_REGISTERED"},
{XED_ERROR_BAD_MEMOP_INDEX, "BAD_MEMOP_INDEX"},
{XED_ERROR_CALLBACK_PROBLEM, "CALLBACK_PROBLEM"},
{XED_ERROR_GATHER_REGS, "GATHER_REGS"},
{XED_ERROR_INSTR_TOO_LONG, "INSTR_TOO_LONG"},
{XED_ERROR_INVALID_MODE, "INVALID_MODE"},
{XED_ERROR_BAD_EVEX_LL, "BAD_EVEX_LL"},
{XED_ERROR_UNIMPLEMENTED, "UNIMPLEMENTED"},
{0, 0},
};

View file

@ -0,0 +1,11 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_XEDERRORS_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_XEDERRORS_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const struct IdName kXedErrorNames[];
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_XEDERRORS_H_ */

View file

@ -0,0 +1,71 @@
/*-*- 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/nt/enum/fileflagandattributes.h"
#include "libc/zip.h"
#include "tool/decode/lib/zipnames.h"
const struct IdName kZipCompressionNames[] = {
{kZipCompressionNone, "kZipCompressionNone"},
{kZipCompressionDeflate, "kZipCompressionDeflate"},
{0, 0},
};
const struct IdName kZipExtraNames[] = {
{kZipExtraZip64, "kZipExtraZip64"},
{kZipExtraNtfs, "kZipExtraNtfs"},
{0, 0},
};
const struct IdName kZipIattrNames[] = {
{kZipIattrBinary, "kZipIattrBinary"},
{kZipIattrAscii, "kZipIattrAscii"},
{0, 0},
};
const struct IdName kZipOsNames[] = {
{kZipOsDos, "kZipOsDos"},
{kZipOsAmiga, "kZipOsAmiga"},
{kZipOsOpenvms, "kZipOsOpenvms"},
{kZipOsUnix, "kZipOsUnix"},
{kZipOsVmcms, "kZipOsVmcms"},
{kZipOsAtarist, "kZipOsAtarist"},
{kZipOsOs2hpfs, "kZipOsOs2hpfs"},
{kZipOsMacintosh, "kZipOsMacintosh"},
{kZipOsZsystem, "kZipOsZsystem"},
{kZipOsCpm, "kZipOsCpm"},
{kZipOsWindowsntfs, "kZipOsWindowsntfs"},
{kZipOsMvsos390zos, "kZipOsMvsos390zos"},
{kZipOsVse, "kZipOsVse"},
{kZipOsAcornrisc, "kZipOsAcornrisc"},
{kZipOsVfat, "kZipOsVfat"},
{kZipOsAltmvs, "kZipOsAltmvs"},
{kZipOsBeos, "kZipOsBeos"},
{kZipOsTandem, "kZipOsTandem"},
{kZipOsOs400, "kZipOsOs400"},
{kZipOsOsxdarwin, "kZipOsOsxdarwin"},
{0, 0},
};
const struct IdName kZipEraNames[] = {
{kZipEra1989, "kZipEra1989"},
{kZipEra1993, "kZipEra1993"},
{kZipEra2001, "kZipEra2001"},
{0, 0},
};

View file

@ -0,0 +1,15 @@
#ifndef COSMOPOLITAN_TOOL_DECODE_LIB_ZIPNAMES_H_
#define COSMOPOLITAN_TOOL_DECODE_LIB_ZIPNAMES_H_
#include "tool/decode/lib/idname.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const struct IdName kZipCompressionNames[];
extern const struct IdName kZipExtraNames[];
extern const struct IdName kZipIattrNames[];
extern const struct IdName kZipOsNames[];
extern const struct IdName kZipEraNames[];
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_TOOL_DECODE_LIB_ZIPNAMES_H_ */

263
tool/decode/macho.c Normal file
View file

@ -0,0 +1,263 @@
/*-*- 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/assert.h"
#include "libc/bits/safemacros.h"
#include "libc/conv/conv.h"
#include "libc/macho.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/calls.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "tool/decode/lib/asmcodegen.h"
#include "tool/decode/lib/flagger.h"
#include "tool/decode/lib/machoidnames.h"
#include "tool/decode/lib/titlegen.h"
/**
* @fileoverview Apple Mach-O metadata disassembler.
*/
static const char *path;
static struct MachoHeader *macho;
static size_t machosize;
static void startfile(void) {
showtitle("αcτµαlly pδrταblε εxεcµταblε", "tool/decode/macho", NULL, NULL,
&kModelineAsm);
printf("#include \"libc/macho.h\"\n\n", path);
}
static void showmachoheader(void) {
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
if (sizeof(struct MachoHeader) > machosize) {
fprintf(stderr, "error: %'s: MachoHeader overruns eof\n", path);
exit(1);
}
#endif
showtitle(basename(path), "macho", "header", NULL, NULL);
printf("\n");
showinthex(macho->magic);
showinthex(macho->arch);
showinthex(macho->arch2);
show(".long",
firstnonnull(findnamebyid(kMachoFileTypeNames, macho->filetype),
format(b1, "%#x", macho->filetype)),
"macho->filetype");
showinthex(macho->loadcount);
showinthex(macho->loadsize);
show(".long",
firstnonnull(recreateflags(kMachoFlagNames, macho->flags),
format(b1, "%#x", macho->flags)),
"macho->flags");
showinthex(macho->__reserved);
printf("\n");
}
static void showmachosection(struct MachoSection *section) {
show(".ascin", format(b1, "%`'s,16", section->name), "section->name");
show(".ascin", format(b1, "%`'s,16", section->commandname),
"section->commandname");
showint64hex(section->vaddr);
showint64hex(section->memsz);
showinthex(section->offset);
showinthex(section->alignlog2);
showinthex(section->relotaboff);
showinthex(section->relocount);
showinthex(section->attr);
show(".long",
format(b1, "%d,%d,%d", section->__reserved[0], section->__reserved[1],
section->__reserved[2]),
"section->__reserved");
}
static void showmacholoadsegment(unsigned i, struct MachoLoadSegment *loadseg) {
assert(loadseg->size ==
sizeof(struct MachoLoadSegment) +
loadseg->sectioncount * sizeof(struct MachoSection));
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
if ((intptr_t)loadseg + sizeof(struct MachoLoadSegment) +
loadseg->sectioncount * sizeof(struct MachoSection) >
(intptr_t)macho + machosize) {
abort();
}
#endif
show(".ascin", format(b1, "%`'s,16", loadseg->name), "loadseg->name");
showint64hex(loadseg->vaddr);
showint64hex(loadseg->memsz);
showint64hex(loadseg->offset);
showint64hex(loadseg->filesz);
show(".long",
firstnonnull(recreateflags(kMachoVmProtNames, loadseg->maxprot),
format(b1, "%#x", loadseg->maxprot)),
"loadseg->maxprot");
show(".long",
firstnonnull(recreateflags(kMachoVmProtNames, loadseg->initprot),
format(b1, "%#x", loadseg->initprot)),
"loadseg->initprot");
showinthex(loadseg->sectioncount);
show(".long",
firstnonnull(recreateflags(kMachoSegmentFlagNames, loadseg->flags),
format(b1, "%#x", loadseg->flags)),
"loadseg->flags");
for (unsigned j = 0; j < loadseg->sectioncount; ++j) {
printf("%d:", (i + 1) * 100 + (j + 1) * 10);
showmachosection((struct MachoSection *)((intptr_t)loadseg +
sizeof(struct MachoLoadSegment) +
j * sizeof(struct MachoSection)));
}
}
static void showmacholoadsymtabshowall(struct MachoLoadSymtab *ls) {
assert(ls->size == sizeof(struct MachoLoadSymtab));
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
if ((intptr_t)ls + sizeof(struct MachoLoadSymtab) >
(intptr_t)macho + machosize) {
abort();
}
#endif
showinthex(ls->offset);
showinthex(ls->count);
showinthex(ls->stroff);
showinthex(ls->strsize);
}
static void showmacholoaduuid(struct MachoLoadUuid *lu) {
assert(lu->size == sizeof(struct MachoLoadUuid));
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
if ((intptr_t)lu + sizeof(struct MachoLoadUuid) >
(intptr_t)macho + machosize) {
abort();
}
#endif
printf("\t.byte\t");
for (unsigned i = 0; i < 16; ++i) {
if (i) {
if (i == 8) {
printf("\n\t.byte\t");
} else {
printf(",");
}
}
printf("%#hhx", lu->uuid[i]);
}
printf("\n");
}
static void showmacholoadsourceversion(
struct MachoLoadSourceVersionCommand *sv) {
assert(sv->size == sizeof(struct MachoLoadSourceVersionCommand));
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
if ((intptr_t)sv + sizeof(struct MachoLoadSourceVersionCommand) >
(intptr_t)macho + machosize) {
abort();
}
#endif
showint64hex(sv->version);
}
static void showmacholoadunixthread(struct MachoLoadThreadCommand *lc) {
assert(lc->size == 4 + 4 + 4 + 4 + lc->count * 4);
showinthex(lc->flavor);
showint(lc->count);
for (unsigned i = 0; i < lc->count; ++i) {
showinthex(lc->wut[i]);
}
}
static void showmacholoadcommand(struct MachoLoadCommand *lc, unsigned i) {
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
if ((intptr_t)lc > (intptr_t)macho + machosize ||
(intptr_t)lc + lc->size > (intptr_t)macho + machosize) {
abort();
}
#endif
showorg((intptr_t)lc - (intptr_t)macho);
printf("%d:", (i + 1) * 10);
show(".long",
firstnonnull(findnamebyid(kMachoLoadCommandNames, lc->command),
format(b1, "%#x", lc->command)),
"lc->command");
showinthex(lc->size);
switch (lc->command) {
case MAC_LC_SEGMENT_64:
showmacholoadsegment(i, (struct MachoLoadSegment *)lc);
break;
case MAC_LC_SYMTAB:
showmacholoadsymtabshowall((struct MachoLoadSymtab *)lc);
break;
case MAC_LC_UUID:
showmacholoaduuid((struct MachoLoadUuid *)lc);
break;
case MAC_LC_SOURCE_VERSION:
showmacholoadsourceversion((struct MachoLoadSourceVersionCommand *)lc);
break;
case MAC_LC_UNIXTHREAD:
showmacholoadunixthread((struct MachoLoadThreadCommand *)lc);
break;
default:
break;
}
printf("\n");
}
static void showmacholoadcommands(void) {
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
if (sizeof(struct MachoHeader) + macho->loadsize > machosize) {
fprintf(stderr, "error: %'s: macho->loadsize overruns eof\n", path);
exit(1);
}
#endif
unsigned i = 0;
const unsigned count = macho->loadcount;
for (struct MachoLoadCommand *lc =
(void *)((intptr_t)macho + sizeof(struct MachoHeader));
i < count; ++i, lc = (void *)((intptr_t)lc + lc->size)) {
showmacholoadcommand(lc, i);
}
}
void showall(void) {
startfile();
showmachoheader();
showmacholoadcommands();
}
int main(int argc, char *argv[]) {
int64_t fd;
struct stat st[1];
if (argc != 2) fprintf(stderr, "usage: %s FILE\n", argv[0]), exit(1);
if ((fd = open((path = argv[1]), O_RDONLY)) == -1 || fstat(fd, st) == -1 ||
(macho = mmap(NULL, (machosize = st->st_size), PROT_READ, MAP_SHARED, fd,
0)) == MAP_FAILED) {
fprintf(stderr, "error: %'s %m\n", path);
exit(1);
}
if (macho->magic != 0xFEEDFACF) {
fprintf(stderr, "error: %'s not a macho x64 executable\n", path);
exit(1);
}
showall();
munmap(macho, machosize);
close(fd);
return 0;
}

121
tool/decode/mkcombos.c Normal file
View file

@ -0,0 +1,121 @@
/*-*- 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/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/conv/conv.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "third_party/getopt/getopt.h"
#include "third_party/zlib/zlib.h"
#include "tool/decode/lib/bitabuilder.h"
static size_t linecap;
static FILE *fin, *fout;
static uint32_t bit, maxbit;
static struct BitaBuilder *bitset;
static char *line, *tok, *s1, *category, *g_inpath, *g_outpath;
noreturn void ShowUsage(FILE *f, int rc) {
fprintf(f, "Usage: %s [-o OUTPUT] [INPUT]\n", "Usage",
program_invocation_name);
exit(rc);
}
void GetOpts(int argc, char *argv[]) {
int opt;
g_inpath = "/dev/stdin";
g_outpath = "/dev/stdout";
while ((opt = getopt(argc, argv, "?ho:")) != -1) {
switch (opt) {
case 'o':
g_outpath = optarg;
break;
case 'h':
case '?':
ShowUsage(stdout, EXIT_SUCCESS);
default:
ShowUsage(stderr, EX_USAGE);
}
}
if (argc - optind) {
g_inpath = argv[optind];
}
}
/**
* Builds sparse unicode dataset for combining codepoints, e.g.
*
* UNDERLINING
*
* - COMBINING MACRON BELOW U+0331 (\xCC\xB1)
* e.g. A̱ḆC̱Ḏ (too short) 41CCB1 42CCB1 43CCB1 44CCB1
*
* - COMBINING DOUBLE MACRON BELOW U+035F (\xCD\x9F)
* e.g. A͟B͟C͟D͟ (too long) 41CD9F 42CD9F 43CD9F 44CD9F
*
* - DOUBLE PLUS COMBINING MACRON BELOW 3×U+035F + 1×U+0331
* e.g. A͟B͟C͟Ḏ (just right) 41CCB1 42CCB1 43CCB1 44CD9F
*
* STRIKETHROUGH
*
* - COMBINING LONG STROKE OVERLAY U+0336 (\xCC\xB6)
* e.g. A̶B̶C̶D̶ 41CCB6 42CCB6 43CCB6 44CCB6
*
* - COMBINING SHORT STROKE OVERLAY U+0335 (\xCC\xB5)
* e.g. A̵B̵C̵D̵ 41CCB5 42CCB5 43CCB5 44CCB5
*
* - COMBINING SHORT SOLIDUS OVERLAY U+0337 (\xCC\xB7)
* e.g. A̷B̷C̷D̷ 41CCB7 42CCB7 43CCB7 44CCB7
*
* - COMBINING LONG SOLIDUS OVERLAY U+0338 (\xCC\xB8)
* e.g. A̸B̸C̸D̸ 41CCB8 42CCB8 43CCB8 44CCB8
*
* @see unicode.org/reports/tr11/#Definitions
*/
int main(int argc, char *argv[]) {
GetOpts(argc, argv);
CHECK_NOTNULL(fin = fopen(g_inpath, "r"));
bitset = bitabuilder_new();
while ((getline(&line, &linecap, fin)) != -1) {
tok = line;
s1 = strsep(&tok, ";");
strsep(&tok, ";");
category = strsep(&tok, ";");
if (!s1 || !category) continue;
bit = strtoimax(s1, NULL, 16);
if (bit != 0x00AD &&
((0x1160 <= bit && bit <= 0x11FF) ||
(strcmp(category, "Me") == 0 || strcmp(category, "Mn") == 0 ||
strcmp(category, "Cf") == 0 || strcmp(category, "Sk") == 0))) {
maxbit = max(bit, maxbit);
CHECK(bitabuilder_setbit(bitset, bit));
}
}
CHECK_NOTNULL(fout = fopen(g_outpath, "wb"));
CHECK(bitabuilder_fwrite(bitset, fout));
bitabuilder_free(&bitset);
return fclose(fin) | fclose(fout);
}

104
tool/decode/mkwides.c Normal file
View file

@ -0,0 +1,104 @@
/*-*- 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/bits/safemacros.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "third_party/getopt/getopt.h"
#include "tool/decode/lib/bitabuilder.h"
static FILE *fin, *fout;
static char width, *line;
static size_t linecap, i, x, y;
static struct BitaBuilder *bitset;
static char *g_inpath, *g_outpath;
noreturn void ShowUsage(FILE *f, int rc) {
fprintf(f, "Usage: %s [-o OUTPUT] [INPUT]\n", "Usage",
program_invocation_name);
exit(rc);
}
void GetOpts(int argc, char *argv[]) {
int opt;
g_inpath = "/dev/stdin";
g_outpath = "/dev/stdout";
while ((opt = getopt(argc, argv, "?ho:")) != -1) {
switch (opt) {
case 'o':
g_outpath = optarg;
break;
case 'h':
case '?':
ShowUsage(stdout, EXIT_SUCCESS);
default:
ShowUsage(stderr, EX_USAGE);
}
}
if (argc - optind) {
g_inpath = argv[optind];
}
}
/**
* Converts official UNICODE monospace widths (yup) to a bitset.
*
* (°°) ̲̲̲
* -
*
* 172kB TXT 32kB bits 525 bytes lz4
*
* @note this tool may print binary to stdout
* @see libc/kompressor/lz4decode.c
* @see tool/viz/bing.c
* @see tool/viz/fold.c
* @see unicode.org/reports/tr11/#Definitions
*/
int main(int argc, char *argv[]) {
GetOpts(argc, argv);
bitset = bitabuilder_new();
CHECK_NOTNULL(fin = fopen(g_inpath, "r"));
while ((getline(&line, &linecap, fin)) != -1) {
x = 0;
y = 0;
if (sscanf(line, "%x..%x;%c", &x, &y, &width) != 3) {
if (sscanf(line, "%x;%c", &x, &width) == 2) {
y = x;
} else {
continue;
}
}
CHECK_LE(x, y);
if (width == 'F' /* full-width */ || width == 'W' /* wide */) {
for (i = x; i <= y; ++i) {
CHECK(bitabuilder_setbit(bitset, i));
}
}
}
CHECK_NOTNULL(fout = fopen(g_outpath, "wb"));
CHECK(bitabuilder_fwrite(bitset, fout));
bitabuilder_free(&bitset);
return fclose(fin) | fclose(fout);
}

228
tool/decode/pe2.c Normal file
View file

@ -0,0 +1,228 @@
/*-*- 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/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/conv/conv.h"
#include "libc/nt/struct/imagentheaders.h"
#include "libc/nt/struct/imageoptionalheader.h"
#include "libc/pe.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "third_party/xed/x86.h"
#include "tool/decode/lib/asmcodegen.h"
#include "tool/decode/lib/flagger.h"
#include "tool/decode/lib/peidnames.h"
#include "tool/decode/lib/titlegen.h"
/**
* @fileoverview Portable executable metadata disassembler.
* @see https://www.aldeid.com/wiki/PE-Portable-executable
*/
static const char *path;
static struct NtImageDosHeader *mz;
static size_t mzsize;
static struct XedDecodedInst *ildreal(void *addr) {
static struct XedDecodedInst xedd;
if (xed_instruction_length_decode(
xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_REAL), addr,
XED_MAX_INSTRUCTION_BYTES) != XED_ERROR_NONE ||
!xedd.decoded_length) {
xedd.decoded_length = 1;
}
return &xedd;
}
static void startfile(void) {
showtitle("αcτµαlly pδrταblε εxεcµταblε", "tool/decode/pe", NULL, NULL,
&kModelineAsm);
printf("#include \"libc/pe.h\"\n\n", path);
}
static void showmzheader(void) {
showtitle(basename(path), "dos", "mz header",
"\tMZ = Mark 'Zibo' Joseph Zbikowski\n"
"\te_cblp: bytes on last page\n"
"\te_cp: 512-byte pages in file\n"
"\te_crlc: reloc table entry count\n"
"\te_cparhdr: data segment file offset / 16\n"
"\te_{min,max}alloc: lowers upper bound load / 16\n"
"\te_ss: lower bound on stack segment\n"
"\te_sp: initialize stack pointer\n"
"\te_csum: ∑bₙ checksum don't bother\n"
"\te_ip: initial ip value\n"
"\te_cs: increases cs load lower bound\n"
"\te_lfarlc: reloc table offset\n"
"\te_ovno: overlay number\n"
"\te_lfanew: portable executable header rva",
NULL);
printf("\n");
show(".ascii", format(b1, "%`'.*s", 2, (const char *)&mz->e_magic),
"mz->e_magic");
showshorthex(mz->e_cblp);
showshorthex(mz->e_cp);
showshorthex(mz->e_crlc);
showshorthex(mz->e_cparhdr);
showshorthex(mz->e_minalloc);
showshorthex(mz->e_maxalloc);
showshorthex(mz->e_ss);
showshorthex(mz->e_sp);
showshorthex(mz->e_csum);
showshorthex(mz->e_ip);
showshorthex(mz->e_cs);
showshorthex(mz->e_lfarlc);
showshorthex(mz->e_ovno);
show(".short",
format(b1, "%hn,%hn,%hn,%hn", mz->e_res[0], mz->e_res[1], mz->e_res[2],
mz->e_res[3]),
"mz->e_res");
showshorthex(mz->e_oemid);
showshorthex(mz->e_oeminfo);
show(".short",
format(b1, "%hn,%hn,%hn,%hn,%hn,%hn,%hn,%hn,%hn,%hn", mz->e_res2[0],
mz->e_res2[1], mz->e_res2[2], mz->e_res2[3], mz->e_res2[4],
mz->e_res2[5], mz->e_res2[6], mz->e_res2[7], mz->e_res2[8],
mz->e_res2[9]),
"mz->e_res2");
showinthex(mz->e_lfanew);
printf("\n");
}
static void showdosstub(void) {
unsigned char *p = (unsigned char *)mz + sizeof(struct NtImageDosHeader);
unsigned char *pe = (mz->e_lfanew ? p + mz->e_lfanew : p + mzsize);
pe = min(pe, p + mzsize - XED_MAX_INSTRUCTION_BYTES);
while (p < pe) {
struct XedDecodedInst *inst = ildreal(p);
if (p + inst->decoded_length > pe) break;
printf("\t.byte\t");
for (unsigned i = 0; i < inst->decoded_length; ++i) {
if (i) printf(",");
printf("%#hhx", xed_decoded_inst_get_byte(inst, i));
}
printf("\n");
p += inst->decoded_length;
}
printf("\n");
}
static void showpeoptionalheader(struct NtImageOptionalHeader *opt) {
showtitle(basename(path), "windows", "pe \"optional\" header", NULL, NULL);
printf("\n");
show(".short",
firstnonnull(findnamebyid(kNtPeOptionalHeaderMagicNames, opt->Magic),
format(b1, "%#hx", opt->Magic)),
"opt->Magic");
showint(opt->MajorLinkerVersion);
showint(opt->MinorLinkerVersion);
showinthex(opt->SizeOfCode);
showinthex(opt->SizeOfInitializedData);
showinthex(opt->SizeOfUninitializedData);
showinthex(opt->AddressOfEntryPoint);
showinthex(opt->BaseOfCode);
showint64hex(opt->ImageBase);
showinthex(opt->SectionAlignment);
showinthex(opt->FileAlignment);
showshort(opt->MajorOperatingSystemVersion);
showshort(opt->MinorOperatingSystemVersion);
showshort(opt->MajorImageVersion);
showshort(opt->MinorImageVersion);
showshort(opt->MajorSubsystemVersion);
showshort(opt->MinorSubsystemVersion);
showint(opt->Win32VersionValue);
showinthex(opt->SizeOfImage);
showinthex(opt->SizeOfHeaders);
showinthex(opt->CheckSum);
show(".short",
firstnonnull(findnamebyid(kNtImageSubsystemNames, opt->Subsystem),
format(b1, "%#hx", opt->Subsystem)),
"opt->Subsystem");
show(".short",
firstnonnull(recreateflags(kNtImageDllcharacteristicNames,
opt->DllCharacteristics),
format(b1, "%#hx", opt->DllCharacteristics)),
"opt->DllCharacteristics");
showint64hex(opt->SizeOfStackReserve);
showint64hex(opt->SizeOfStackCommit);
showint64hex(opt->SizeOfHeapReserve);
showint64hex(opt->SizeOfHeapCommit);
showinthex(opt->LoaderFlags);
showinthex(opt->NumberOfRvaAndSizes);
}
static void showpeheader(struct NtImageNtHeaders *pe) {
showtitle(basename(path), "windows", "pe header", NULL, NULL);
printf("\n");
showorg(mz->e_lfanew);
show(".ascii", format(b1, "%`'.*s", 4, (const char *)&pe->Signature),
"pe->Signature");
show(".short",
firstnonnull(
findnamebyid(kNtImageFileMachineNames, pe->FileHeader.Machine),
format(b1, "%#hx", pe->FileHeader.Machine)),
"pe->FileHeader.Machine");
showshort(pe->FileHeader.NumberOfSections);
showinthex(pe->FileHeader.TimeDateStamp);
showinthex(pe->FileHeader.PointerToSymbolTable);
showint(pe->FileHeader.NumberOfSymbols);
showshort(pe->FileHeader.SizeOfOptionalHeader);
show(".short",
firstnonnull(recreateflags(kNtImageCharacteristicNames,
pe->FileHeader.Characteristics),
format(b1, "%#hx", pe->FileHeader.Characteristics)),
"pe->FileHeader.Characteristics");
printf("\n");
showpeoptionalheader(pecheckaddress(mz, mzsize, &pe->OptionalHeader,
pe->FileHeader.SizeOfOptionalHeader));
}
static void showall(void) {
startfile();
showmzheader();
showdosstub();
if (mz->e_lfanew) {
showpeheader(pecomputerva(mz, mzsize, mz->e_lfanew,
sizeof(struct NtImageFileHeader)));
}
}
int main(int argc, char *argv[]) {
int64_t fd;
struct stat st[1];
if (argc != 2) fprintf(stderr, "usage: %s FILE\n", argv[0]), exit(1);
if ((fd = open((path = argv[1]), O_RDONLY)) == -1 || fstat(fd, st) == -1 ||
(mz = mmap(NULL, (mzsize = st->st_size), PROT_READ, MAP_SHARED, fd, 0)) ==
MAP_FAILED) {
fprintf(stderr, "error: %'s %m\n", path);
exit(1);
}
if (mz->e_magic != kNtImageDosSignature) {
fprintf(stderr, "error: %'s not a dos executable\n", path);
exit(1);
}
showall();
munmap(mz, mzsize);
close(fd);
return 0;
}

101
tool/decode/peboff.c Normal file
View file

@ -0,0 +1,101 @@
#include "libc/nt/struct/peb.h"
#include "libc/stdio/stdio.h"
int main() {
printf("InheritedAddressSpace = 0x%x\n",
offsetof(struct NtPeb, InheritedAddressSpace));
printf("ReadImageFileExecOptions = 0x%x\n",
offsetof(struct NtPeb, ReadImageFileExecOptions));
printf("BeingDebugged = 0x%x\n", offsetof(struct NtPeb, BeingDebugged));
printf("Mutant = 0x%x\n", offsetof(struct NtPeb, Mutant));
printf("ImageBaseAddress = 0x%x\n", offsetof(struct NtPeb, ImageBaseAddress));
printf("Ldr = 0x%x\n", offsetof(struct NtPeb, Ldr));
printf("ProcessParameters = 0x%x\n",
offsetof(struct NtPeb, ProcessParameters));
printf("SubSystemData = 0x%x\n", offsetof(struct NtPeb, SubSystemData));
printf("ProcessHeap = 0x%x\n", offsetof(struct NtPeb, ProcessHeap));
printf("FastPebLock = 0x%x\n", offsetof(struct NtPeb, FastPebLock));
printf("KernelCallbackTable = 0x%x\n",
offsetof(struct NtPeb, KernelCallbackTable));
printf("UserSharedInfoPtr = 0x%x\n",
offsetof(struct NtPeb, UserSharedInfoPtr));
printf("SystemReserved = 0x%x\n", offsetof(struct NtPeb, SystemReserved));
printf("__wut6 = 0x%x\n", offsetof(struct NtPeb, __wut6));
printf("__wut7 = 0x%x\n", offsetof(struct NtPeb, __wut7));
printf("TlsExpansionCounter = 0x%x\n",
offsetof(struct NtPeb, TlsExpansionCounter));
printf("TlsBitmap = 0x%x\n", offsetof(struct NtPeb, TlsBitmap));
printf("TlsBitmapBits = 0x%x\n", offsetof(struct NtPeb, TlsBitmapBits));
printf("ReadOnlySharedMemoryBase = 0x%x\n",
offsetof(struct NtPeb, ReadOnlySharedMemoryBase));
printf("ReadOnlyStaticServerData = 0x%x\n",
offsetof(struct NtPeb, ReadOnlyStaticServerData));
printf("AnsiCodePageData = 0x%x\n", offsetof(struct NtPeb, AnsiCodePageData));
printf("OemCodePageData = 0x%x\n", offsetof(struct NtPeb, OemCodePageData));
printf("UnicodeCaseTableData = 0x%x\n",
offsetof(struct NtPeb, UnicodeCaseTableData));
printf("NumberOfProcessors = 0x%x\n",
offsetof(struct NtPeb, NumberOfProcessors));
printf("NtGlobalFlag = 0x%x\n", offsetof(struct NtPeb, NtGlobalFlag));
printf("CriticalSectionTimeout = 0x%x\n",
offsetof(struct NtPeb, CriticalSectionTimeout));
printf("HeapSegmentReserve = 0x%x\n",
offsetof(struct NtPeb, HeapSegmentReserve));
printf("HeapSegmentCommit = 0x%x\n",
offsetof(struct NtPeb, HeapSegmentCommit));
printf("HeapDeCommitTotalFreeThreshold = 0x%x\n",
offsetof(struct NtPeb, HeapDeCommitTotalFreeThreshold));
printf("HeapDeCommitFreeBlockThreshold = 0x%x\n",
offsetof(struct NtPeb, HeapDeCommitFreeBlockThreshold));
printf("NumberOfHeaps = 0x%x\n", offsetof(struct NtPeb, NumberOfHeaps));
printf("MaximumNumberOfHeaps = 0x%x\n",
offsetof(struct NtPeb, MaximumNumberOfHeaps));
printf("ProcessHeaps = 0x%x\n", offsetof(struct NtPeb, ProcessHeaps));
printf("GdiSharedHandleTable = 0x%x\n",
offsetof(struct NtPeb, GdiSharedHandleTable));
printf("ProcessStarterHelper = 0x%x\n",
offsetof(struct NtPeb, ProcessStarterHelper));
printf("GdiDCAttributeList = 0x%x\n",
offsetof(struct NtPeb, GdiDCAttributeList));
printf("LoaderLock = 0x%x\n", offsetof(struct NtPeb, LoaderLock));
printf("OSMajorVersion = 0x%x\n", offsetof(struct NtPeb, OSMajorVersion));
printf("OSMinorVersion = 0x%x\n", offsetof(struct NtPeb, OSMinorVersion));
printf("OSVersion = 0x%x\n", offsetof(struct NtPeb, OSVersion));
printf("OSBuildNumber = 0x%x\n", offsetof(struct NtPeb, OSBuildNumber));
printf("OSCSDVersion = 0x%x\n", offsetof(struct NtPeb, OSCSDVersion));
printf("OSPlatformId = 0x%x\n", offsetof(struct NtPeb, OSPlatformId));
printf("ImageSubsystem = 0x%x\n", offsetof(struct NtPeb, ImageSubsystem));
printf("ImageSubsystemMajorVersion = 0x%x\n",
offsetof(struct NtPeb, ImageSubsystemMajorVersion));
printf("ImageSubsystemMinorVersion = 0x%x\n",
offsetof(struct NtPeb, ImageSubsystemMinorVersion));
printf("ImageProcessAffinityMask = 0x%x\n",
offsetof(struct NtPeb, ImageProcessAffinityMask));
printf("ActiveProcessAffinityMask = 0x%x\n",
offsetof(struct NtPeb, ActiveProcessAffinityMask));
printf("GdiHandleBuffer = 0x%x\n", offsetof(struct NtPeb, GdiHandleBuffer));
printf("PostProcessInitRoutine = 0x%x\n",
offsetof(struct NtPeb, PostProcessInitRoutine));
printf("TlsExpansionBitmap = 0x%x\n",
offsetof(struct NtPeb, TlsExpansionBitmap));
printf("TlsExpansionBitmapBits = 0x%x\n",
offsetof(struct NtPeb, TlsExpansionBitmapBits));
printf("SessionId = 0x%x\n", offsetof(struct NtPeb, SessionId));
printf("AppCompatFlags = 0x%x\n", offsetof(struct NtPeb, AppCompatFlags));
printf("AppCompatFlagsUser = 0x%x\n",
offsetof(struct NtPeb, AppCompatFlagsUser));
printf("pShimData = 0x%x\n", offsetof(struct NtPeb, pShimData));
printf("AppCompatInfo = 0x%x\n", offsetof(struct NtPeb, AppCompatInfo));
printf("CSDVersion = 0x%x\n", offsetof(struct NtPeb, CSDVersion));
printf("ActivationContextData = 0x%x\n",
offsetof(struct NtPeb, ActivationContextData));
printf("ProcessAssemblyStorageMap = 0x%x\n",
offsetof(struct NtPeb, ProcessAssemblyStorageMap));
printf("SystemDefaultActivationContextData = 0x%x\n",
offsetof(struct NtPeb, SystemDefaultActivationContextData));
printf("SystemAssemblyStorageMap = 0x%x\n",
offsetof(struct NtPeb, SystemAssemblyStorageMap));
printf("MinimumStackCommit = 0x%x\n",
offsetof(struct NtPeb, MinimumStackCommit));
return 0;
}

28
tool/decode/unhex.c Normal file
View file

@ -0,0 +1,28 @@
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* @fileoverview Hex to binary converter program.
* Non-hex bytes are ignored. If you've got imposter syndrome you could
* call this a compiler and start coding in hex.
*/
int main() {
int o, t = -1;
while (0 <= (o = getchar()) && o <= 255) {
if (!isxdigit(o)) continue;
int h = hextoint(o);
if (t != -1) putchar(t * 16 + h), h = -1;
t = h;
}
if (ferror(stdout)) return 1;
if (t != -1) return 2;
return 0;
}

209
tool/decode/x86opinfo.c Normal file
View file

@ -0,0 +1,209 @@
/*-*- 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/bits/bits.h"
#include "libc/macros.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "third_party/getopt/getopt.h"
#include "third_party/xed/x86.h"
#include "tool/decode/lib/idname.h"
#include "tool/decode/lib/xederrors.h"
const struct IdName kXedModeNames[] = {
{XED_MACHINE_MODE_LONG_64, "long"},
{XED_MACHINE_MODE_LONG_COMPAT_32, "long32"},
{XED_MACHINE_MODE_LONG_COMPAT_16, "long16"},
{XED_MACHINE_MODE_LEGACY_32, "legacy"},
{XED_MACHINE_MODE_LEGACY_16, "legacy16"},
{XED_MACHINE_MODE_REAL, "real"},
{XED_MACHINE_MODE_UNREAL, "unreal"},
};
enum XedMachineMode g_mode;
struct XedDecodedInst g_xedd;
noreturn void ShowUsage(int rc, FILE *f) {
size_t i;
fputs("Usage: ", f);
fputs(program_invocation_name, f);
fputs(" [-r] [-m MODE] HEX\n MODE ∊ {", f);
fputs(kXedModeNames[0].name, f);
for (i = 1; i < ARRAYLEN(kXedModeNames); ++i) {
fputc(',', f);
fputs(kXedModeNames[i].name, f);
}
fputs("}\n", f);
exit(rc);
}
void SetMachineMode(const char *s) {
size_t i;
for (i = 0; i < ARRAYLEN(kXedModeNames); ++i) {
if (strcasecmp(s, kXedModeNames[i].name) == 0) {
g_mode = kXedModeNames[i].id;
return;
}
}
fputs("error: bad mode\n", stderr);
ShowUsage(EXIT_FAILURE, stderr);
}
void GetOpts(int argc, char *argv[]) {
int opt;
g_mode = XED_MACHINE_MODE_LONG_64;
while ((opt = getopt(argc, argv, "?hrm:")) != -1) {
switch (opt) {
case 'r':
g_mode = XED_MACHINE_MODE_REAL;
break;
case 'm':
SetMachineMode(optarg);
break;
case '?':
case 'h':
ShowUsage(EXIT_SUCCESS, stdout);
default:
ShowUsage(EX_USAGE, stderr);
}
}
}
void CheckHex(const char *s) {
size_t i, n;
if ((n = strlen(s)) % 2 == 1) {
ShowUsage(EX_DATAERR, stderr);
}
for (i = 0; i < n; ++i) {
if (!isxdigit(s[i + 0])) {
ShowUsage(EX_DATAERR, stderr);
}
}
}
void ShowField(const char *name, uint64_t value) {
if (value) {
printf("/\t%-20s = %#lx\n", name, value);
}
}
void ShowOffset(const char *name, uint64_t off) {
printf("/\t%-20s = %#lx\n", name, off);
}
int main(int argc, char *argv[]) {
int err;
size_t i, j, k;
uint8_t buf[XED_MAX_INSTRUCTION_BYTES];
GetOpts(argc, argv);
for (k = 0, i = optind; i < argc; ++i) {
CheckHex(argv[i]);
for (j = 0; argv[i][j]; j += 2) {
if (++k > XED_MAX_INSTRUCTION_BYTES) ShowUsage(EX_DATAERR, stderr);
buf[k - 1] = hextoint(argv[i][j + 0]) << 4 | hextoint(argv[i][j + 1]);
}
}
xed_decoded_inst_zero_set_mode(&g_xedd, g_mode);
if ((err = xed_instruction_length_decode(&g_xedd, buf, k)) !=
XED_ERROR_NONE) {
fputs("XED_ERROR_", stderr);
fputs(findnamebyid(kXedErrorNames, err), stderr);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
#define SHOWOP(F) ShowField(#F, g_xedd.operands.F)
SHOWOP(amd3dnow);
SHOWOP(asz);
SHOWOP(bcrc);
SHOWOP(chip);
SHOWOP(cldemote);
SHOWOP(disp);
SHOWOP(disp_width);
SHOWOP(error);
SHOWOP(esrc);
SHOWOP(first_f2f3);
SHOWOP(has_modrm);
SHOWOP(has_sib);
SHOWOP(hint);
SHOWOP(ild_f2);
SHOWOP(ild_f3);
SHOWOP(ild_seg);
SHOWOP(imm1_bytes);
SHOWOP(imm_width);
SHOWOP(last_f2f3);
SHOWOP(llrc);
SHOWOP(lock);
SHOWOP(map);
SHOWOP(mask);
SHOWOP(max_bytes);
SHOWOP(mod);
SHOWOP(mode);
SHOWOP(mode_first_prefix);
SHOWOP(modep5);
SHOWOP(modep55c);
SHOWOP(modrm_byte);
SHOWOP(nominal_opcode);
SHOWOP(nprefixes);
SHOWOP(nrexes);
SHOWOP(nseg_prefixes);
SHOWOP(osz);
SHOWOP(out_of_bytes);
SHOWOP(pos_disp);
SHOWOP(pos_imm);
SHOWOP(pos_imm1);
SHOWOP(pos_modrm);
SHOWOP(pos_nominal_opcode);
SHOWOP(pos_sib);
SHOWOP(prefix66);
SHOWOP(realmode);
SHOWOP(reg);
SHOWOP(rep);
SHOWOP(rex);
SHOWOP(rexb);
SHOWOP(rexr);
SHOWOP(rexrr);
SHOWOP(rexw);
SHOWOP(rexx);
SHOWOP(rm);
SHOWOP(seg_ovd);
SHOWOP(sibbase);
SHOWOP(sibindex);
SHOWOP(sibscale);
SHOWOP(srm);
SHOWOP(ubit);
SHOWOP(uimm0);
SHOWOP(uimm1);
SHOWOP(vex_prefix);
SHOWOP(vexdest210);
SHOWOP(vexdest3);
SHOWOP(vexdest4);
SHOWOP(vexvalid);
SHOWOP(vl);
SHOWOP(wbnoinvd);
SHOWOP(zeroing);
return 0;
}

38
tool/decode/xor.c Normal file
View file

@ -0,0 +1,38 @@
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
/**
* @fileoverview Hex to binary converter program.
* Non-hex bytes are ignored. If you've got imposter syndrome you could
* call this a compiler and start coding in hex.
*/
int main(int argc, char *argv[]) {
size_t i, j, l;
uint8_t *buf;
if (argc == 1) return 1;
buf = gc(xmalloc((l = strlen(argv[1]) / 2)));
for (j = 0; j < l; ++j) {
buf[j] = 0;
}
for (i = 1; i < argc; ++i) {
for (j = 0; j < l; ++j) {
buf[j] ^= hextoint(argv[i][j + 0]) << 4 | hextoint(argv[i][j + 1]);
}
}
for (j = 0; j < l; ++j) {
putchar("0123456789abcdef"[(buf[j] >> 4) & 0xf]);
putchar("0123456789abcdef"[(buf[j] >> 0) & 0xf]);
}
putchar('\n');
return 0;
}

324
tool/decode/zip.c Normal file
View file

@ -0,0 +1,324 @@
/*-*- 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/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/conv/conv.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/nt/struct/filetime.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/x/x.h"
#include "libc/zip.h"
#include "tool/decode/lib/asmcodegen.h"
#include "tool/decode/lib/disassemblehex.h"
#include "tool/decode/lib/flagger.h"
#include "tool/decode/lib/idname.h"
#include "tool/decode/lib/ntfileflagnames.h"
#include "tool/decode/lib/titlegen.h"
#include "tool/decode/lib/zipnames.h"
/**
* @fileoverview Zip File Disassembler.
*/
nodiscard char *formatdosdate(uint16_t dosdate) {
return xasprintf("%04u-%02u-%02u", ((dosdate >> 9) & 0b1111111) + 1980,
(dosdate >> 5) & 0b1111, dosdate & 0b11111);
}
nodiscard char *formatdostime(uint16_t dostime) {
return xasprintf("%02u:%02u:%02u", (dostime >> 11) & 0b11111,
(dostime >> 5) & 0b111111, (dostime << 1) & 0b111110);
}
void advancepos(uint8_t *map, size_t *pos, size_t off) {
CHECK_GE(off, *pos);
if (off > *pos) {
printf("\n/\t<%s>\n", "LIMBO");
disassemblehex(&map[*pos], off - *pos, stdout);
printf("/\t</%s>\n", "LIMBO");
}
*pos = off;
}
void showgeneralflag(uint16_t generalflag) {
puts("\
/ utf8\n\
/ strong encryption\n\
/ compressed patch data\n\
/ crc and size go after file content\n\
/ {normal,max,fast,superfast}\n\
/ encrypted\n\
/ rrrruuuur");
show(".short", format(b1, "0b%016b", generalflag), "generalflag");
}
void showtimestamp(uint16_t time, uint16_t date) {
show(".short", format(b1, "%#04hx", time),
gc(xasprintf("%s (%s)", "lastmodifiedtime", gc(formatdostime(time)))));
show(".short", format(b1, "%#04hx", date),
gc(xasprintf("%s (%s)", "lastmodifieddate", gc(formatdosdate(date)))));
}
void showcompressmethod(uint16_t compressmethod) {
show(".short",
firstnonnull(findnamebyid(kZipCompressionNames, compressmethod),
format(b1, "%hu", compressmethod)),
"compressionmethod");
}
void showextrantfs(uint8_t *ntfs) {
struct timespec mtime, atime, ctime;
filetimetotimespec(
&mtime, (struct NtFileTime){read32le(ntfs + 8), read32le(ntfs + 12)});
filetimetotimespec(
&atime, (struct NtFileTime){read32le(ntfs + 16), read32le(ntfs + 20)});
filetimetotimespec(
&ctime, (struct NtFileTime){read32le(ntfs + 24), read32le(ntfs + 30)});
show(".long", gc(xasprintf("%d", read32le(ntfs))), "ntfs reserved");
show(".short", gc(xasprintf("0x%04x", read16le(ntfs + 4))),
"ntfs attribute tag value #1");
show(".short", gc(xasprintf("%hu", read16le(ntfs + 6))),
"ntfs attribute tag size");
show(".quad", gc(xasprintf("%lu", read64le(ntfs + 8))),
gc(xasprintf("%s (%s)", "ntfs last modified time",
gc(xiso8601(&mtime)))));
show(".quad", gc(xasprintf("%lu", read64le(ntfs + 16))),
gc(xasprintf("%s (%s)", "ntfs last access time", gc(xiso8601(&atime)))));
show(".quad", gc(xasprintf("%lu", read64le(ntfs + 24))),
gc(xasprintf("%s (%s)", "ntfs creation time", gc(xiso8601(&ctime)))));
}
void showextra(uint8_t *extra) {
switch (ZIP_EXTRA_HEADERID(extra)) {
case kZipExtraNtfs:
showextrantfs(ZIP_EXTRA_CONTENT(extra));
break;
case kZipExtraZip64:
/* TODO */
default:
disassemblehex(ZIP_EXTRA_CONTENT(extra), ZIP_EXTRA_CONTENTSIZE(extra),
stdout);
break;
}
}
void showexternalattributes(uint8_t *cf) {
uint32_t ea;
ea = ZIP_CFILE_EXTERNALATTRIBUTES(cf);
if (ZIP_CFILE_FILEATTRCOMPAT(cf) == kZipOsUnix) {
show(".short", recreateflags(kNtFileFlagNames, ea & 0xffff),
"dos file flags");
show(".short", format(b1, "%#o", ea >> 16), "st_mode");
} else {
show(".long", recreateflags(kNtFileFlagNames, ea), "externalattributes");
}
}
void showextras(uint8_t *extras, uint16_t extrassize) {
int i;
bool first;
uint8_t *p, *pe;
if (extrassize) {
first = true;
for (p = extras, pe = extras + extrassize, i = 0; p < pe;
p += ZIP_EXTRA_SIZE(p), ++i) {
show(".short",
firstnonnull(findnamebyid(kZipExtraNames, ZIP_EXTRA_HEADERID(p)),
gc(xasprintf("0x%04hx", ZIP_EXTRA_HEADERID(p)))),
gc(xasprintf("%s[%d].%s", "extras", i, "headerid")));
show(".short", gc(xasprintf("%df-%df", (i + 2) * 10, (i + 1) * 10)),
gc(xasprintf("%s[%d].%s (%hd %s)", "extras", i, "contentsize",
ZIP_EXTRA_CONTENTSIZE(p), "bytes")));
if (first) {
first = false;
printf("%d:", (i + 1) * 10);
}
showextra(p);
printf("%d:", (i + 2) * 10);
}
}
putchar('\n');
}
void showlocalfileheader(uint8_t *lf, uint16_t idx) {
printf("\n/\t%s #%hu (%zu %s)\n", "local file", idx + 1,
ZIP_LFILE_HDRSIZE(lf), "bytes");
show(".ascii", format(b1, "%`'.*s", 4, lf), "magic");
show(".byte",
firstnonnull(findnamebyid(kZipEraNames, ZIP_LFILE_VERSIONNEED(lf)),
gc(xasprintf("%d", ZIP_LFILE_VERSIONNEED(lf)))),
"pkzip version need");
show(".byte",
firstnonnull(findnamebyid(kZipOsNames, ZIP_LFILE_OSNEED(lf)),
gc(xasprintf("%d", ZIP_LFILE_OSNEED(lf)))),
"os need");
showgeneralflag(ZIP_LFILE_GENERALFLAG(lf));
showcompressmethod(ZIP_LFILE_COMPRESSIONMETHOD(lf));
showtimestamp(ZIP_LFILE_LASTMODIFIEDTIME(lf), ZIP_LFILE_LASTMODIFIEDDATE(lf));
show(".long", format(b1, "%#x", ZIP_LFILE_CRC32(lf)),
gc(xasprintf(
"%s (%#x)", "crc32z",
crc32_z(0, ZIP_LFILE_CONTENT(lf), ZIP_LFILE_COMPRESSEDSIZE(lf)))));
show(".long", "3f-2f",
format(b1, "%s (%u %s)", "compressedsize", ZIP_LFILE_COMPRESSEDSIZE(lf),
"bytes"));
show(".long", format(b1, "%u", ZIP_LFILE_UNCOMPRESSEDSIZE(lf)),
"uncompressedsize");
show(".short", "1f-0f",
format(b1, "%s (%hu %s)", "namesize", ZIP_LFILE_NAMESIZE(lf), "bytes"));
show(
".short", "2f-1f",
format(b1, "%s (%hu %s)", "extrasize", ZIP_LFILE_EXTRASIZE(lf), "bytes"));
printf("0:");
show(".ascii",
format(b1, "%`'s",
gc(strndup(ZIP_LFILE_NAME(lf), ZIP_LFILE_NAMESIZE(lf)))),
"name");
printf("1:");
showextras(ZIP_LFILE_EXTRA(lf), ZIP_LFILE_EXTRASIZE(lf));
printf("2:");
disassemblehex(ZIP_LFILE_CONTENT(lf), ZIP_LFILE_COMPRESSEDSIZE(lf), stdout);
printf("3:\n");
}
void showcentralfileheader(uint8_t *cf) {
printf("\n/\t%s (%zu %s)\n", "central directory file header",
ZIP_CFILE_HDRSIZE(cf), "bytes");
show(".ascii", format(b1, "%`'.*s", 4, cf), "magic");
show(".byte", gc(xasprintf("%d", ZIP_CFILE_VERSIONMADE(cf))), "version made");
show(".byte",
firstnonnull(findnamebyid(kZipOsNames, ZIP_CFILE_FILEATTRCOMPAT(cf)),
gc(xasprintf("%d", ZIP_CFILE_FILEATTRCOMPAT(cf)))),
"file attr compat");
show(".byte",
firstnonnull(findnamebyid(kZipEraNames, ZIP_CFILE_VERSIONNEED(cf)),
gc(xasprintf("%d", ZIP_CFILE_VERSIONNEED(cf)))),
"pkzip version need");
show(".byte",
firstnonnull(findnamebyid(kZipOsNames, ZIP_CFILE_OSNEED(cf)),
gc(xasprintf("%d", ZIP_CFILE_OSNEED(cf)))),
"os need");
showgeneralflag(ZIP_CFILE_GENERALFLAG(cf));
showcompressmethod(ZIP_CFILE_COMPRESSIONMETHOD(cf));
showtimestamp(ZIP_CFILE_LASTMODIFIEDTIME(cf), ZIP_CFILE_LASTMODIFIEDDATE(cf));
show(".long", format(b1, "%#x", ZIP_CFILE_CRC32(cf)), "crc32z");
show(".long", format(b1, "%u", ZIP_CFILE_COMPRESSEDSIZE(cf)),
"compressedsize");
show(".long", format(b1, "%u", ZIP_CFILE_UNCOMPRESSEDSIZE(cf)),
"uncompressedsize");
show(".short", "1f-0f",
format(b1, "%s (%hu %s)", "namesize", ZIP_CFILE_NAMESIZE(cf), "bytes"));
show(
".short", "2f-1f",
format(b1, "%s (%hu %s)", "extrasize", ZIP_CFILE_EXTRASIZE(cf), "bytes"));
show(".short", "3f-2f",
format(b1, "%s (%hu %s)", "commentsize", ZIP_CFILE_COMMENTSIZE(cf),
"bytes"));
show(".short", format(b1, "%hu", ZIP_CFILE_DISK(cf)), "disk");
show(".short",
recreateflags(kZipIattrNames, ZIP_CFILE_INTERNALATTRIBUTES(cf)),
"internalattributes");
showexternalattributes(cf);
show(".long", format(b1, "%u", ZIP_CFILE_OFFSET(cf)), "lfile hdr offset");
printf("0:");
show(".ascii",
format(b1, "%`'s",
gc(strndup(ZIP_CFILE_NAME(cf), ZIP_CFILE_NAMESIZE(cf)))),
"name");
printf("1:");
showextras(ZIP_CFILE_EXTRA(cf), ZIP_CFILE_EXTRASIZE(cf));
printf("2:");
disassemblehex(ZIP_CFILE_COMMENT(cf), ZIP_CFILE_COMMENTSIZE(cf), stdout);
printf("3:\n");
}
void showcentraldirheader(uint8_t *cd) {
printf("\n/\t%s (%zu %s)\n", "end of central directory header",
ZIP_CDIR_HDRSIZE(cd), "bytes");
show(".ascii", format(b1, "%`'.*s", 4, cd), "magic");
show(".short", format(b1, "%hd", ZIP_CDIR_DISK(cd)), "disk");
show(".short", format(b1, "%hd", ZIP_CDIR_STARTINGDISK(cd)), "startingdisk");
show(".short", format(b1, "%hu", ZIP_CDIR_RECORDSONDISK(cd)),
"recordsondisk");
show(".short", format(b1, "%hu", ZIP_CDIR_RECORDS(cd)), "records");
show(".long", format(b1, "%u", ZIP_CDIR_SIZE(cd)), "size");
show(".long", format(b1, "%u", ZIP_CDIR_OFFSET(cd)), "cfile hdrs offset");
show(".short", "1f-0f",
format(b1, "%s (%hu %s)", "commentsize", ZIP_CDIR_COMMENTSIZE(cd),
"bytes"));
printf("0:");
disassemblehex(ZIP_CDIR_COMMENT(cd), ZIP_CDIR_COMMENTSIZE(cd), stdout);
printf("1:\n");
}
void disassemblezip(uint8_t *map, size_t mapsize) {
size_t pos;
uint16_t i;
static int records;
uint8_t *cd, *cf, *lf;
CHECK_NOTNULL((cd = zipfindcentraldir(map, mapsize)));
pos = 0;
records = ZIP_CDIR_RECORDS(cd);
for (i = 0, cf = map + ZIP_CDIR_OFFSET(cd); i < records;
++i, cf += ZIP_CFILE_HDRSIZE(cf)) {
lf = map + ZIP_CFILE_OFFSET(cf);
CHECK_EQ(kZipLfileHdrMagic, ZIP_LFILE_MAGIC(lf));
advancepos(map, &pos, lf - map);
showlocalfileheader(lf, i);
pos = (lf - map) + ZIP_LFILE_SIZE(lf);
}
for (i = 0, cf = map + ZIP_CDIR_OFFSET(cd); i < records;
++i, cf += ZIP_CFILE_HDRSIZE(cf)) {
CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(cf));
advancepos(map, &pos, cf - map);
showcentralfileheader(cf);
pos = (cf - map) + ZIP_CFILE_HDRSIZE(cf);
}
advancepos(map, &pos, cd - map);
showcentraldirheader(cd);
pos = (cd - map) + ZIP_CDIR_HDRSIZE(cd);
advancepos(map, &pos, mapsize);
}
int main(int argc, char *argv[]) {
int fd;
uint8_t *map;
struct stat st;
CHECK_EQ(2, argc);
CHECK_NE(-1, (fd = open(argv[1], O_RDONLY)));
CHECK_NE(-1, fstat(fd, &st));
CHECK_GE(st.st_size, kZipCdirHdrMinSize);
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)));
showtitle("αcτµαlly pδrταblε εxεcµταblε", "tool/decode/zip",
basename(argv[1]), NULL, &kModelineAsm);
disassemblezip(map, st.st_size);
CHECK_NE(-1, munmap(map, st.st_size));
CHECK_NE(-1, close(fd));
return 0;
}

View file

@ -0,0 +1,339 @@
;; ╔──────────────────────────────────────────────────────────────────╗
;; │ To the extent possible under law, Justine Tunney has waived │
;; │ all copyright and related or neighboring rights to this file, │
;; │ as it is written in the following disclaimers: │
;; │ • http://unlicense.org/ │
;; │ • http://creativecommons.org/publicdomain/zero/1.0/ │
;; ╚──────────────────────────────────────────────────────────────────╝
;; Reconfigures GNU Emacs syntax highlighting for GNU Assembler syntax.
(require 'asm-mode)
(defun cosmo-regexpify (x)
(let ((join (lambda (sep lis)
(mapconcat 'identity lis sep))))
(cond ((vectorp x)
(funcall join "" (mapcar 'cosmo-regexpify x)))
((listp x)
(concat "\\(?:"
(funcall join "\\|" (mapcar 'cosmo-regexpify x))
"\\)"))
('t x))))
(defun cosmo-fontify (faces regexp limit predicate)
"Set FACES on REGEXP matches until LIMIT that satisfy PREDICATE."
(while (re-search-forward regexp limit t)
(let (i (face (save-excursion
(backward-char)
(face-at-point)))
(syntax (syntax-ppss)))
(when (funcall predicate face syntax)
(dolist (i faces)
(put-text-property (match-beginning (car i))
(match-end (car i))
'face
(cdr i)))))))
(defconst asm-mode-gas-qualifiers-regexp
(cosmo-regexpify
[("comdat"
":req"
":vararg"
["@" ("function"
"object"
"got"
"size"
"gotoff"
"plt"
"pltoff"
"gotpcrel"
"progbits"
"nobits"
"init_array"
"fini_array")])
"\\>"])
"GNU Assembler section, relocation, macro param qualifiers.")
(defconst asm-mode-doctag-regexp
(cosmo-regexpify
'(["<"
("p" "br"
"b" "/b"
"i" "/i"
"pre" "/pre"
"h3" "h4" "/h3" "/h4"
"ul" "ol" "li" "/ol" "/ul"
"dl" "dt" "dd" "/dl"
"table" "tr" "td" "th" "td" "/table")
">"]
["@"
("param"
"return"
"define"
"see"
"throws"
"returnstwice"
"fileoverview"
"nullable"
"noreturn"
"domain"
"clob"
"since"
"forcealignargpointer"
"mode"
"speed"
"cost"
"todo"
"assume"
"define"
"domain"
"code"
"note"
"protip"
"nxbitsafe"
"preinitsafe"
"asyncsignalsafe"
"notasyncsignalsafe"
"isa"
"sideffect")
"\\>"]))
"Assembly docstring highlighting in Google Java Style.")
(defconst asm-mode-x86-prefix-ops-regexp
(cosmo-regexpify
[(["[lL][oO][cC][kK]\\>[ \t]*"
("[mM][oO][vV][sS]"
"[aA][dD][dDcC]"
"[sS][uUbB][bB]"
"[oO][rR]"
"[aA][nN][dD]"
"[xX][oO][rR]"
"[nN][oO][tT]"
"[nN][eE][gG]"
"[iI][nN][cC]"
"[dD][eE][cC]"
"[bB][tT][sSrRcC]?"
"[xX][aA][dD][dD]"
"[xX][cC][hH][gG]"
["[cC][mM][pP][xX][cC][hH][gG]" ("8[bB]" "16[bB]") "?"])]
["[rR][eE][pP]" ("[eEzZ]" "[nN][eEzZ]") "?\\>[ \t]*"
("[mM][oO][vV][sS]"
"[sS][tT][oO][sS]"
"[sS][cC][aA][sS]"
"[cC][mM][pP][sS]"
"[nN][oO][pP]"
"[rR][eE][tT]"
"[iI][nN][sS]"
"[oO][uU][tT][sS]")])
"[bBwWlLqQ]?\\>"])
"Legal high-level 80x86 prefix instruction combinations.")
(defconst cosmo-asm-font-lock-keywords
(append
`(;; AT&T Fortran-Style Assembler Comment
;;
;; - Valid
;;
;; * /heyho
;; * //heyho
;;
;; - Ignored
;;
;; *  /heyho
;; * code code //heyho
;;
("^/.*$" . font-lock-comment-face)
;; Immediate Argument
;;
;; - Valid
;;
;; * mov $2,%eax
;; * mov $~2+0x2ul,%eax
;; * mov $'c,%eax
;; * mov $'\n,%eax
;;
("[ \t]\\$\\(\\(?:'\\(?:'\\|\\s\"\\|\\s\\.\\|.\\)\\|\\(?:0x[[:xdigit:]]+\\|0b[01]+\\|[1-9][0-9]*\\|0[0-7]*\\)\\(?:[fb]\\|u?l?l?\\)\\|[-*/&^|()%<>~+]\\|[_.[:alpha:]][-_.[:alnum:]]*\\)+\\)"
1 font-lock-constant-face)
(cosmo-asm-doctag-keywords)
;; Static Branch Prediction
;;
;; - Valid
;;
;; * jnz,pt 1f
;; * jnz,pn 1b
;;
;; - Traditionally Implied
;;
;; * jnz,pn 1f
;; * jnz,pt 1b
;;
(",p[tn]" . font-lock-keyword-face)
("\\\\\\(?:@\\|()\\)" . font-lock-function-name-face)
("\\\\\\(\\sw\\|_\\|\\.\\)+\\>" . font-lock-variable-name-face)
(,asm-mode-x86-prefix-ops-regexp . font-lock-keyword-face)
(,(concat "^\\(\\(?:\\sw\\|\\s_\\|\\.\\)+\\):[ \t]*"
"\\(\\(?:\\sw\\|\\.\\)+\\)?")
(1 font-lock-function-name-face)
(2 font-lock-keyword-face nil t))
("^\\((\\sw+)\\)?\\s +\\(\\(\\.?\\sw\\|\\s_\\)+\\(\\.\\sw+\\)*\\)"
2 font-lock-keyword-face)
("^\\(\\.\\(\\sw\\|\\s_\\|\\.\\)+\\)\\>[^:]?"
1 font-lock-keyword-face)
("^.*?\\*/\\(\\.\\(\\sw\\|\\s_\\|\\.\\)+\\)\\>[^:]?"
1 font-lock-keyword-face)
;; it's complicated
(,asm-mode-gas-qualifiers-regexp
. font-lock-type-face))
cpp-font-lock-keywords
'(;; GNU-Style Assembler Comment (Ltd. 80x86 &c.)
;;
;; - Valid
;;
;; * #heyho
;; * # heyho
;; * .org . #heyho
;; * .org . ####euhhcue
;; * .org .# ###euhhcue
;;
;; - Ignored
;;
;; * #if 0
;; * #endif
;; * .ascii "#heyho"
;;
("\\(#.*\\)$" 1 font-lock-comment-face)
("'\\(\\\\?.\\)\\>" 1 font-lock-constant-face)
;; Register Value
;;
;; - Valid
;;
;; * %al
;; * %eax
;;
;; - Ignored
;;
;; * %%al
;;
("%\\sw+" . font-lock-variable-name-face)
;; Hexadecimal Literal
;;
;; - Valid
;;
;; * 0x0123456789abcdef
;; * 0XDEADBEEFu
;; * -0xdeadbeefULL
;;
;; - Ignored
;;
;; * 0x0123456789abcdefg
;;
("\\b\\(0[xX]\\)[[:xdigit:]]+\\([ulUL]*\\)\\b"
(1 font-lock-constant-face)
(2 font-lock-constant-face))
;; Binary Literal
;;
;; - Valid
;;
;; * 0b0101101101
;; * 0B0101101101u
;; * -0b0101101101ULL
;;
;; - Ignored
;;
;; * 0b012
;;
("\\b\\(0[bB]\\)[01]+\\([ulUL]*\\)\\b"
(1 font-lock-constant-face)
(2 font-lock-constant-face))
;; Octal Literal
;;
;; - Valid
;;
;; * 01234567
;; * 01234567l
;; * -01234567ULL
;;
;; - Ignored
;;
;; * 012345678
;;
("\\b\\(0\\)[0-7]+\\([ulUL]*\\)\\b"
(1 font-lock-constant-face)
(2 font-lock-constant-face))
;; Decimal Literal
;;
;; - Valid
;;
;; * 123456789
;; * 123456789l
;; * -01234567ULL
;;
;; - Ignored
;;
;; * 123456789a
;; * 123456789aul
;;
("\\b[1-9][0-9]+\\([ulUL]*\\)\\b"
1 font-lock-constant-face)
;; AT&T Fortran-Style Assembler Comment
;;
;; - Valid
;;
;; * /heyho
;; * //heyho
;;
;; - Ignored
;;
;; *  /heyho
;; * code code //heyho
;;
("^/.*$" . font-lock-comment-face)
;; AT&T-Style Directional Label
;;
;; - Valid
;;
;; * 1f
;; * 99b
;;
("\\b\\([0-9]+\\)[fb]\\b" 1 font-lock-function-name-face)))
"Additional expressions to highlight in Assembler mode.")
(defun cosmo-asm-doctag-keywords (limit)
(cosmo-fontify '((0 . font-lock-constant-face))
asm-mode-doctag-regexp
limit
(lambda (face syntax)
(or (memq face '(font-lock-comment-face))
(and syntax
(nth 4 syntax))))))
(defun cosmo-asm-supplemental-hook ()
"GNU Assembly in Bell Laboratories Style."
(setq asm-comment-char ) ;; Was ESR using TASM?
(font-lock-add-keywords 'asm-mode
'((cosmo-asm-doctag-keywords))
'append)
(set (make-local-variable 'require-final-newline) nil)
(set (make-local-variable 'indent-tabs-mode) t)
(set (make-local-variable 'tab-width) 8))
(progn
(add-hook 'asm-mode-hook 'cosmo-asm-supplemental-hook)
(setq asm-font-lock-keywords cosmo-asm-font-lock-keywords))
(provide 'cosmo-asm-mode)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,232 @@
(defconst cosmo-c-constants-regex
(let (
(c11-constants
'("__func__"
"__STDC_IEC_559__"
"__VA_ARGS__"
"__FILE__"
"__LINE__"
"__DATE__"
"__STDC__"
"__STDC_HOSTED__"
"__STDC_VERSION__"
"__TIME__"
"__STDC_ISO_10646__"
"__STDC_MB_MIGHT_NEQ_WC__"
"__STDC_UTF_16__"
"__STDC_UTF_32__"
"__STDC_ANALYZABLE__"
"__STDC_IEC_559_COMPLEX__"
"__STDC_LIB_EXT1__"
"__STDC_NO_ATOMICS__"
"__STDC_NO_COMPLEX__"
"__STDC_NO_THREADS__"
"__STDC_NO_VLA__"
"__STDC_WANT_LIB_EXT1__"))
(gcc-cpp-412-constants
'("__BASE_FILE__"
"__CHAR_BIT__"
"__FUNCTION__"
"__GNUC_MINOR__"
"__GNUC_PATCHLEVEL__"
"__GNUC__"
"__GNUG__"
"__INCLUDE_LEVEL__"
"__INTMAX_MAX__"
"__INT_MAX__"
"__LONG_LONG_MAX__"
"__LONG_MAX__"
"__SCHAR_MAX__"
"__SHRT_MAX__"
"__DBL_MIN__"
"__DBL_MAX__"
"__FLT_MIN__"
"__FLT_MAX__"
"__WCHAR_MAX__"
"__WCHAR_UNSIGNED__"))
(gcc-cpp-92-constants
'("__x86_64__"
"__amd64__"
"__WINT_MAX__"
"__BIGGEST_ALIGNMENT__"
"__SIZE_MAX__"
"__PTRDIFF_MAX__"
"__UINTMAX_MAX__"
"__SIG_ATOMIC_MAX__"
"__INT8_MAX__"
"__INT16_MAX__"
"__INT32_MAX__"
"__INT64_MAX__"
"__UINT8_MAX__"
"__UINT16_MAX__"
"__UINT32_MAX__"
"__UINT64_MAX__"
"__INT_LEAST8_MAX__"
"__INT_LEAST16_MAX__"
"__INT_LEAST32_MAX__"
"__INT_LEAST64_MAX__"
"__UINT_LEAST8_MAX__"
"__UINT_LEAST16_MAX__"
"__UINT_LEAST32_MAX__"
"__UINT_LEAST64_MAX__"
"__INT_FAST8_MAX__"
"__INT_FAST16_MAX__"
"__INT_FAST32_MAX__"
"__INT_FAST64_MAX__"
"__UINT_FAST8_MAX__"
"__UINT_FAST16_MAX__"
"__UINT_FAST32_MAX__"
"__UINT_FAST64_MAX__"
"__INTPTR_MAX__"
"__UINTPTR_MAX__"
"__WCHAR_MIN__"
"__WINT_MIN__"
"__SIG_ATOMIC_MIN__"
"__SCHAR_WIDTH__"
"__SHRT_WIDTH__"
"__INT_WIDTH__"
"__LONG_WIDTH__"
"__LONG_LONG_WIDTH__"
"__PTRDIFF_WIDTH__"
"__SIG_ATOMIC_WIDTH__"
"__SIZE_WIDTH__"
"__WCHAR_WIDTH__"
"__WINT_WIDTH__"
"__INT_LEAST8_WIDTH__"
"__INT_LEAST16_WIDTH__"
"__INT_LEAST32_WIDTH__"
"__INT_LEAST64_WIDTH__"
"__INT_FAST8_WIDTH__"
"__INT_FAST16_WIDTH__"
"__INT_FAST32_WIDTH__"
"__INT_FAST64_WIDTH__"
"__INTPTR_WIDTH__"
"__INTMAX_WIDTH__"
"__SIZEOF_INT__"
"__SIZEOF_INTMAX__"
"__SIZEOF_UINTMAX__"
"__SIZEOF_LONG__"
"__SIZEOF_LONG_LONG__"
"__SIZEOF_SHORT__"
"__SIZEOF_POINTER__"
"__SIZEOF_FLOAT__"
"__SIZEOF_DOUBLE__"
"__SIZEOF_LONG_DOUBLE__"
"__SIZEOF_SIZE_T__"
"__SIZEOF_WCHAR_T__"
"__SIZEOF_WINT_T__"
"__SIZEOF_PTRDIFF_T__"
"__TIMESTAMP__"))
(limits
'("SIZEOF_SHORT"
"SIZEOF_INT"
"SIZEOF_LONG"
"SIZEOF_LONG_LONG"
"SIZEOF_POINTER"
"SIZEOF_PTRDIFF_T"
"SIZEOF_SIZE_T"
"SIZEOF_WCHAR_T"
"SIZEOF_WINT_T"
"SIZEOF_FLOAT"
"SIZEOF_FLOAT128"
"SIZEOF_DOUBLE"
"SIZEOF_FLOAT80"
"SIZEOF_LONG_DOUBLE"
"SIZEOF_INTMAX"
"SCHAR_MAX"
"SHRT_MAX"
"INT_MAX"
"LONG_MAX"
"LLONG_MAX"
"LONG_LONG_MAX"
"SIZE_MAX"
"INT8_MAX"
"INT16_MAX"
"INT32_MAX"
"INT64_MAX"
"WINT_MAX"
"WCHAR_MAX"
"INTPTR_MAX"
"PTRDIFF_MAX"
"SCHAR_MIN"
"SHRT_MIN"
"UINT_MIN"
"INT_MIN"
"LONG_MIN"
"LLONG_MIN"
"LONG_LONG_MIN"
"SIZE_MIN"
"INT8_MIN"
"INT16_MIN"
"INT32_MIN"
"INT64_MIN"
"INTMAX_MIN"
"INTPTR_MIN"
"WINT_MIN"
"WCHAR_MIN"
"PTRDIFF_MIN"
"USHRT_MAX"
"UINT_MAX"
"ULONG_MAX"
"ULLONG_MAX"
"ULONG_LONG_MAX"
"UINTPTR_MAX"
"UINT8_MAX"
"UINT16_MAX"
"UINT32_MAX"
"UINT64_MAX"
"USHRT_MIN"
"ULONG_MIN"
"ULLONG_MIN"
"ULONG_LONG_MIN"
"UINT8_MIN"
"UINT16_MIN"
"UINT32_MIN"
"UINT64_MIN"
"UINTMAX_MIN"
"UINTPTR_MIN"
"MB_CUR_MAX"
"MB_LEN_MAX"
"INTMAX_MAX"
"UINTMAX_MAX"
"INTMAX_MAX"
"UINTMAX_MAX"
"DBL_MIN"
"DBL_MAX"
"FLT_MIN"
"FLT_MAX"))
(cosmopolitan-constants
'("__SAUCE__"
"PAGESIZE"
"FRAMESIZE"
"BIGPAGESIZE"
"ENV_MAX"
"ARG_MAX"
"CMD_MAX"
"PATH_MAX"
"BUFSIZ"
"CACHELINE"
"CHAR_BIT"
"NAME_MAX"
"NSIG"
"CHILD_MAX"
"OPEN_MAX"
"ATEXIT_MAX"
"__x86__"
"__i386__"))
)
(concat "\\_<"
(regexp-opt (append c11-constants
gcc-cpp-412-constants
gcc-cpp-92-constants
cosmopolitan-constants
limits))
"\\_>")))
(provide 'cosmo-c-constants)

View file

@ -0,0 +1,315 @@
(defconst cosmo-c-keywords-regex
(let (
;; (kar
;; '("case"
;; "do"
;; "return"
;; "struct"
;; "for"
;; "default"
;; "auto"
;; "while"
;; "else"
;; "break"
;; "union"
;; "switch"
;; "continue"
;; "extern"
;; "sizeof"
;; "if"
;; "goto"))
;; (ansi
;; '("static"
;; "sizeof"
;; "if"
;; "typedef"
;; "const"
;; "struct"
;; "for"
;; "union"
;; "switch"
;; "volatile"
;; "do"
;; "return"
;; "goto"
;; "auto"
;; "enum"
;; "else"
;; "break"
;; "extern"
;; "case"
;; "default"
;; "register"
;; "while"
;; "continue"))
(c99
'("inline"
"restrict"
"_Imaginary"
"_Bool"
"_Pragma"
"_Complex"))
(c11
'("_Atomic"
"_Alignas"
"_Alignof"
"_Noreturn"
"_Generic"
"_Thread_local"
"_Static_assert"
"_Complex_I"
"_Imaginary_I"))
;; (cxx17
;; '("this"
;; "thread_local"
;; "private"
;; "catch"
;; "export"
;; "operator"
;; "sizeof"
;; "dynamic_cast"
;; "static_assert"
;; "const_cast"
;; "const"
;; "for"
;; "static_cast"
;; "union"
;; "namespace"
;; "switch"
;; "virtual"
;; "class"
;; "alignas"
;; "continue"
;; "volatile"
;; "template"
;; "mutable"
;; "if"
;; "public"
;; "friend"
;; "do"
;; "inline"
;; "return"
;; "goto"
;; "alignof"
;; "auto"
;; "enum"
;; "typedef"
;; "else"
;; "break"
;; "constexpr"
;; "new"
;; "extern"
;; "using"
;; "throw"
;; "asm"
;; "case"
;; "typeid"
;; "decltype"
;; "reinterpret_cast"
;; "default"
;; "noexcept"
;; "register"
;; "nullptr"
;; "try"
;; "typename"
;; "while"
;; "protected"
;; "static"
;; "explicit"
;; "delete"))
(cosmo
'("__rbx"
"__msabi"
"microarchitecture"
"targetclones"
"winstruct"
"testonly"
"forcealignargpointer"
"textexit"
"externinline"
"noinline"
"donothing"
"byanymeansnecessary"
"threadlocal"
"printfesque"
"flattenout"
"mallocesque"
"vallocesque"
"null"
"paramsnonnull"
"returnspointerwithnoaliases"
"alignas"
"nosideeffect"
"decltype"
"forceinline"
"nocallersavedregisters"
"nothrow"
"nooptimize"
"optimizesize"
"optimizespeed"
"alignof"
"relegated"
"antiquity"
"memcpyesque"
"libcesque"
"artificial"
"returnstwice"
"returnsaligned"
"noprune"
"reallocesque"
"nullterminated"
"unreachable"
"hidden"
"privileged"
"hasatleast"
"nodebuginfo"
"frownedupon"
"noreturn"
"initarray"
"mayalias"
"noinstrument"
"interruptfn"
"nocallback"
"textstartup"
"warnifused"
"attributeallocsize"
"attributeallocalign"
"nodiscard"
"nointerpose"
"compatfn"
"returnsnonnull"
"strftimeesque"
"firstclass"
"preinitarray"
"scanfesque"
"pureconst"
"thatispacked"
"strlenesque"
"textwindows"
"aligned"
"typeof"
"textreal"
"autotype"
"_Section"
"_Vector_size"))
(gnu
'("__inline"
"__alignof"
"__typeof"
"__restrict"
"__const__"
"__label__"
"__noinline__"
"__noclone__"
"__force_align_arg_pointer__"
"__always_inline__"
"__gnu_inline__"
"__alignof__"
"__asm__"
"__attribute__"
"__auto_type"
"__byte__"
"__complex__"
"__imag__"
"__may_alias__"
"__noreturn__"
"__packed__"
"__pointer__"
"__printf__"
"__real__"
"__restrict__"
"__scanf__"
"__strfmon__"
"__strftime__"
"__strong__"
"__target__"
"__transparent_union__"
"__typeof__"
"__volatile__"
"__word__"
"__alias__"
"__aligned__"
"__alloc_align__"
"__alloc_size__"
"__artificial__"
"__assume_aligned__"
"__cold__"
"__constructor__"
"__destructor__"
"__copy__"
"__deprecated__"
"__error__"
"__warning__"
"__externally_visible__"
"__flatten__"
"__format__"
"__gnu_format__"
"__gnu_printf__"
"__gnu_scanf__"
"__format_arg__"
"__hot__"
"__ifunc__"
"__interrupt__"
"__interrupt_handler__"
"__leaf__"
"__no_caller_saved_registers__"
"__malloc__"
"__no_icf__"
"__no_instrument_function__"
"__no_profile_instrument_function__"
"__no_reorder__"
"__no_sanitize__"
"__no_sanitize_address__"
"__no_address_safety_analysis__"
"__no_sanitize_thread__"
"__leaf__"
"__no_sanitize_undefined__"
"__no_split_stack__"
"__no_stack_limit__"
"__noclone__"
"__noipa__"
"__nonnull__"
"__noplt__"
"__nothrow__"
"__optimize__"
"__pure__"
"__patchable_function_entry__"
"__returns_nonnull__"
"__returns_twice__"
"__section__"
"__sentinel__"
"__simd__"
"__target_clones__"
"__unused__"
"__used__"
"__visibility__"
"__warn_unused_result__"
"__params_nonnull__"
"__weak__"
"__vector_size__"
"__mode__"))
(clang
'("__optnone__"
"__nodebug__"))
)
(concat "\\_<"
(regexp-opt (append ;; kar
;; ansi
;; c99
c11
;; cxx17
gnu
clang
cosmo))
"\\_>")))
(provide 'cosmo-c-keywords)

222
tool/emacs/cosmo-c-types.el Normal file
View file

@ -0,0 +1,222 @@
(defconst cosmo-c-types-regex
(let (
;; (kar
;; '("short unsigned int"
;; "int"
;; "long unsigned int"
;; "char"
;; "long"
;; "long signed int"
;; "signed long"
;; "unsigned short int"
;; "short int"
;; "signed short int"
;; "unsigned"
;; "long unsigned"
;; "unsigned short"
;; "short signed int"
;; "short unsigned"
;; "unsigned char"
;; "signed int"
;; "unsigned long"
;; "long int"
;; "unsigned int"
;; "signed short"
;; "unsigned long int"
;; "short signed"
;; "signed long int"
;; "signed char"
;; "long signed"))
(ansi
'("size_t"
"wint_t"
;; "void"
"wchar_t"
;; "long double"
))
;; (kar
;; '("char"
;; "short"
;; "int"
;; "long"
;; "signed"
;; "unsigned"))
;; (ansi
;; '("size_t"
;; "wint_t"
;; "void"
;; "wchar_t"))
(c99
'("bool"
"_Bool"
"unsigned long long int"
"int32_t"
"uint_least64_t"
"long long signed"
"intptr_t"
"uintmax_t"
"long long unsigned int"
"int_fast32_t"
"int16_t"
"int64_t"
"int_fast16_t"
"int_fast64_t"
"errno_t"
"uint_fast32_t"
"int_least8_t"
"uint_least16_t"
"long long signed int"
"long long"
"char16_t"
"uint_least32_t"
"int_least64_t"
"int_least16_t"
"int_fast8_t"
"uint_least8_t"
"uintptr_t"
"ssize_t"
"long long int"
"unsigned long long"
"int8_t"
"long long unsigned"
"signed long long int"
"int_least32_t"
"uint8_t"
"uint_fast64_t"
"intmax_t"
"uint_fast16_t"
"signed long long"
"uint32_t"
"ptrdiff_t"
"char32_t"
"uint64_t"
"uint16_t"
"uint_fast8_t"
"complex float"
"complex double"
"complex long double"))
(c11
'("atomic_uintptr_t"
"atomic_uchar"
"atomic_int_least32_t"
"atomic_uint_least64_t"
"atomic_int_fast32_t"
"atomic_uint_least16_t"
"atomic_short"
"atomic_size_t"
"atomic_uint"
"atomic_char16_t"
"atomic_ullong"
"atomic_uint_fast16_t"
"atomic_int_fast8_t"
"atomic_uint_least32_t"
"atomic_ptrdiff_t"
"atomic_uintmax_t"
"atomic_int_least16_t"
"atomic_long"
"atomic_int"
"atomic_int_fast16_t"
"atomic_uint_least8_t"
"atomic_ushort"
"atomic_int_least8_t"
"atomic_ulong"
"atomic_char32_t"
"atomic_schar"
"atomic_intmax_t"
"atomic_int_least64_t"
"atomic_uint_fast64_t"
"atomic_wchar_t"
"atomic_uint_fast8_t"
"atomic_int_fast64_t"
"atomic_llong"
"atomic_bool"
"atomic_intptr_t"
"atomic_uint_fast32_t"
"atomic_char"))
(gnu
'("__int128"
"unsigned __int128"
"signed __int128"
"_Float16"
"_Complex _Float16"
"_Float16x"
"_Complex _Float16x"
"_Float32"
"_Complex _Float32"
"_Float32x"
"_Complex _Float32x"
"_Float64"
"_Complex _Float64"
"_Float64x"
"_Complex _Float64x"
"_Float128"
"_Complex _Float128"))
(cxx17
'())
(cosmo
'("int_least128_t"
"int_fast128_t"
"bool32"
"int128_t"
"uint128_t"
"axdx_t"))
(x86intrin
'("__v8hu"
"__v16qi"
"__v4su"
"__v8su"
"__v16qu"
"__v16qs"
"__v8hi"
"__v4hi"
"__v2df"
"__v2di"
"__v4si"
"__v8si"
"__m1"
"__v2du"
"__m2"
"__v1di"
"__v4sf"
"__v8sf"
"__v2si"
"__m64"
"__v2sf"
"__v8qi"
"__v32qi"
"__m128"
"__m128d"
"__m128i"
"__m128_u"
"__m128d_u"
"__m128i_u"
"__m256"
"__m256d"
"__m256i"
"__m256_u"
"__m256d_u"
"__m256i_u"))
)
(concat "\\_<"
(regexp-opt (append ;; kar
ansi
c99
c11
gnu
cxx17
cosmo
x86intrin))
"\\_>")))
(provide 'cosmo-c-types)

132
tool/emacs/cosmo-format.el Normal file
View file

@ -0,0 +1,132 @@
;;; cosmo-format.el --- Cosmopolitan Clang-Format Integration
;; Author: Justine Tunney <jtunney@gmail.com>
;; Version: 0.1.0
;; License: Public Domain
;; Keywords: c c++ clang
;; To the extent possible under law, Justine Tunney has waived all
;; copyright and related or neighboring rights to this file, as it is
;; written in the following disclaimers: <http://unlicense.org/> and
;; <http://creativecommons.org/publicdomain/zero/1.0/>
;;; Commentary:
;;
;; This module automates indentation, whitespace, and other stylistic
;; concerns while editing C/C++ source files. The clang-format program,
;; if present on the system, is run each time a buffer is saved.
;;; Installation:
;;
;; Put the following in your .emacs.d/init.el file:
;;
;; (require 'cosmo-format)
;;
;; Put this file in the root of your project:
;;
;; printf '---\nBasedOnStyle: Google\n...\n' >.clang-format
;;
;; Any buffer whose pathname matches `cosmo-format-path-regex' will
;; be formatted automatically on save if:
;;
;; 1. It's able to find the clang-format program, or
;; `cosmo-format-bin' is customized.
;;
;; 2. There's a .clang-format file up the directory tree, or
;; `cosmo-format-arg' is customized; in which case, it is
;; recommended that it be customized buffer locally.
;;
;; For all other cases, there are no latency penalties (i.e. superfluous
;; i/o syscalls) or risks to leaving this enabled globally.
;;; Code:
(defcustom cosmo-format-bin nil
"Explicit command or pathname of clang-format program."
:type 'string
:group 'cosmo-format)
(defcustom cosmo-format-arg nil
"Explicit argument to clang-format program."
:type 'string
:group 'cosmo-format)
(defcustom cosmo-format-modes '(c-mode
c++-mode
java-mode
protobuf-mode)
"List of major-modes that need clang-format."
:type '(repeat symbol)
:group 'cosmo-format)
(defcustom cosmo-format-exts '("c" "cc" "h" "inc" ;; c/c++
"hh" "cpp" "hpp" ;; ms c/c++
"rl" ;; ragel
"proto") ;; protobuf
"List of pathname extensions that need clang-format."
:type '(repeat string)
:group 'cosmo-format)
(defcustom cosmo-format-blacklist '()
"List of files to ignore, matched by basename."
:type '(repeat string)
:group 'cosmo-format)
(defvar cosmo--clang-format-bin)
(defmacro cosmo-memoize (var mvar form)
"Return VAR or evaluate FORM memoized locally to MVAR."
`(cond (,var ,var)
((fboundp (quote ,mvar))
(cond ((eq ,mvar 'null) nil)
(t ,mvar)))
(t (let ((res ,form))
(setq-local ,mvar (or res 'null))
res))))
(defun cosmo--find-clang-format-bin ()
(cosmo-memoize cosmo-format-bin
cosmo--clang-format-bin
(or (executable-find "clang-format-10")
(executable-find "clang-format-9")
(executable-find "clang-format-8")
(executable-find "clang-format-7")
(executable-find "clang-format"))))
(defun cosmo-format ()
"Beautifies source code in current buffer."
(interactive)
(when (and (memq major-mode cosmo-format-modes)
(member (file-name-extension (buffer-file-name))
cosmo-format-exts)
(not (member (file-name-nondirectory (buffer-name))
cosmo-format-blacklist)))
(let ((bin (cosmo--find-clang-format-bin)))
(when bin
(let ((p (point))
(tmp (make-temp-file "cosmo-format"))
(arg (or cosmo-format-arg
(and (locate-dominating-file
(buffer-file-name)
".clang-format")
"-style=file"))))
(when arg
(write-region nil nil tmp)
(let ((buf (get-buffer-create "*clang-format*"))
(exe (cosmo--find-clang-format-bin)))
;; (with-current-buffer buf
;; (set-process-sentinel
;; (call-process exe tmp t nil arg)
;; (lambda (_ _)
;; (display-buffer buf))))
(with-current-buffer buf
(call-process exe tmp t nil arg))
(replace-buffer-contents buf)
(kill-buffer buf)
(delete-file tmp nil))))))))
(add-hook 'before-save-hook 'cosmo-format)
(provide 'cosmo-format)
;;; cosmo-format.el ends here

764
tool/emacs/cosmo-stuff.el Normal file
View file

@ -0,0 +1,764 @@
;; ╔──────────────────────────────────────────────────────────────────╗
;; │ To the extent possible under law, Justine Tunney has waived │
;; │ all copyright and related or neighboring rights to this file, │
;; │ as it is written in the following disclaimers: │
;; │ • http://unlicense.org/ │
;; │ • http://creativecommons.org/publicdomain/zero/1.0/ │
;; ╚──────────────────────────────────────────────────────────────────╝
;; Hodgepodge of copypasta from Justine's Emacs config intended to be
;; helpful to those wanting to configure their own Emacs editor to work
;; pretty well as a Cosmopolitan IDE.
;;; Code:
(require 'asm-mode)
(require 'cc-mode)
(require 'fortran)
(require 'cosmo-c-builtins)
(require 'cosmo-c-constants)
(require 'cosmo-c-keywords)
(require 'cosmo-c-types)
(require 'dired)
(require 'javadown)
(require 'ld-script)
(require 'make-mode)
(setq c-doc-comment-style 'javadown)
(add-to-list 'auto-mode-alist '("\\.x$" . c-mode)) ;; -aux-info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Support Macros
(defmacro setql (var val)
`(set (make-local-variable ',var) ,val))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Support Functions
(defun cosmo-contains (x s &optional icase)
(declare (pure t) (side-effect-free t))
(let ((case-fold-search icase))
(not (null (string-match-p (regexp-quote x) s)))))
(defun cosmo-startswith (x s &optional icase)
(declare (pure t) (side-effect-free t))
(not (null (string-prefix-p x s icase))))
(defun cosmo-endswith (x s &optional icase)
(declare (pure t) (side-effect-free t))
(let ((p (- (length s) (length x))))
(and (>= p 0)
(eq t (compare-strings x nil nil
s p nil icase)))))
(defun cosmo-replace (x y s)
(declare (pure t) (side-effect-free t))
(let ((case-fold-search nil))
(replace-regexp-in-string (regexp-quote x) y s t t)))
(defun cosmo-join (s l)
(declare (pure t) (side-effect-free t))
(mapconcat 'identity l s))
(defun cosmo-lchop (p s)
(declare (pure t) (side-effect-free t))
(if (and (>= (length s) (length p))
(string= p (substring s 0 (length p))))
(substring s (length p))
s))
(defun cosmo-first-that (predicate list)
(declare (pure t))
(when list
(if (funcall predicate (car list))
(car list)
(cosmo-first-that predicate (cdr list)))))
(defun cosmo-file-name-sans-extensions (filename)
(save-match-data
(let (directory
(file (file-name-sans-versions
(file-name-nondirectory filename))))
(if (and (string-match "[.-].*\\'" file)
(not (eq 0 (match-beginning 0))))
(if (setq directory (file-name-directory filename))
(concat directory (substring file 0 (match-beginning 0)))
(substring file 0 (match-beginning 0)))
filename))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; C-c . Jump to symbol definition
;; C-c , Jump back to where we were
;; C-c C-i Show references to symbol
;; TODO(jart): Why doesnt the default-directory variable work?
(defun project-current (&optional maybe-prompt dir)
(expand-file-name
(file-name-directory
(locate-dominating-file
(buffer-name) "Makefile"))))
(defun cosmo-xref-find-references ()
(interactive)
(let ((case-fold-search nil))
(xref-find-references (format "%S" (symbol-at-point)))))
(defun cosmo-xref-find-definitions ()
(interactive)
(let ((case-fold-search nil))
(xref-find-definitions (format "%S" (symbol-at-point)))))
(global-set-key (kbd "M-,") 'xref-pop-marker-stack)
(global-set-key (kbd "M-.") 'cosmo-xref-find-definitions)
(global-set-key (kbd "C-c TAB") 'cosmo-xref-find-references)
(defun stop-asking-questions-etags ()
(set (make-local-variable 'tags-file-name)
(format "%s/TAGS"
(or (locate-dominating-file (buffer-name) "Makefile")
(file-name-directory (buffer-name))))))
(add-hook 'c-mode-common-hook 'stop-asking-questions-etags)
(setq tags-revert-without-query t)
(setq kill-buffer-query-functions ;; disable kill buffer w/ process question
(delq 'process-kill-buffer-query-function kill-buffer-query-functions))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compile buffer and run associated test and/or display assembly
;; C-c C-c Compile
;; M-1 C-c C-c Compile w/ MODE=tiny
;; M-2 C-c C-c Compile w/ MODE=opt
;; M-3 C-c C-c Compile w/ MODE=rel
;; M-4 C-c C-c Compile w/ MODE=dbg
;; M-5 C-c C-c Compile w/ MODE=""
(defun cosmo-intest (&optional file-name)
(let (path root pkg)
(and (setq path (or (buffer-file-name) dired-directory))
(setq root (locate-dominating-file path "Makefile"))
(setq pkg (file-relative-name path root))
(cosmo-startswith "test/" pkg))))
(defun cosmo--make-mode (arg &optional default)
(cond ((eq arg 1) "tiny")
((eq arg 2) "opt")
((eq arg 3) "rel")
((eq arg 4) "dbg")
((eq arg 5) "")
(default default)
((cosmo-intest) "dbg")
(t "")))
(defun cosmo--compile-command (this root kind mode)
(let* ((ext (file-name-extension this)) ;; e.g. "c"
(dir (file-name-directory this)) ;; e.g. "/home/jart/daisy/libc/"
(dots (file-relative-name root dir)) ;; e.g. "../"
(file (file-relative-name this root)) ;; e.g. "libc/crc32c.c"
(name (file-name-sans-extension file)) ;; e.g. "libc/crc32c"
(buddy (format "test/%s_test.c" name))
(runs (format "o/$m/%s.com.runs TESTARGS=-b" name))
(buns (format "o/$m/test/%s_test.com.runs TESTARGS=-b" name)))
(cond ((not (member ext '("c" "cc" "s" "S" "rl" "f")))
(format "m=%s; make -j8 -O MODE=$m SILENT=0 o/$m/%s"
mode
(directory-file-name
(file-name-directory
(file-relative-name this root)))))
((cosmo-contains "_test." (buffer-file-name))
(format "m=%s; make -j8 -O MODE=$m %s"
mode runs))
((file-exists-p (format "%s" buddy))
(format (cosmo-join
" && "
'("m=%s; make -j8 -O o/$m/%s.o MODE=$m SILENT=0"
"objdump -wzCd o/$m/%s.o"
"make -j8 -O MODE=$m %s"))
mode name name buns))
((eq kind 'run)
(format
(cosmo-join
" && "
`("m=%s; f=o/$m/%s.com"
,(concat "make -j8 -O $f MODE=$m SILENT=0")
"./$f"))
mode name))
((and (file-regular-p this)
(file-executable-p this))
(format "./%s" file))
('t
(format
(cosmo-join
" && "
`("m=%s; f=o/$m/%s.o"
,(concat "make -j8 -O $f MODE=$m SILENT=0")
"objdump -wzCd $f"))
mode name)))))
(defun cosmo-compile (arg)
(interactive "P")
(let* ((this (or (buffer-file-name) dired-directory))
(root (locate-dominating-file this "Makefile")))
(when root
(let* ((mode (cosmo--make-mode arg))
(default-directory root)
(compile-command (cosmo--compile-command this root nil mode)))
(compile compile-command)))))
(defun cosmo-compile-hook ()
(local-set-key (kbd "C-c C-c") 'cosmo-compile))
(progn
(add-hook 'makefile-mode-hook 'cosmo-compile-hook)
(add-hook 'asm-mode-hook 'cosmo-compile-hook)
(add-hook 'ld-script-mode-hook 'cosmo-compile-hook)
(add-hook 'dired-mode-hook 'cosmo-compile-hook)
(add-hook 'c-mode-common-hook 'cosmo-compile-hook)
(add-hook 'fortran-mode-hook 'cosmo-compile-hook)
(add-hook 'protobuf-mode-hook 'cosmo-compile-hook))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Display assembly for C/C++ buffer
;;
;; C-c C-a Compile and show assembly, with a native optimized build
;; C-c C-b Compile and show assembly, with a balanced build
;;
;; The ALT key may be used to override the default mode.
;;
;; M-1 C-c C-a Compile w/ MODE=tiny
;; M-2 C-c C-a Compile w/ MODE=opt
;; M-3 C-c C-a Compile w/ MODE=rel
;; M-4 C-c C-a Compile w/ MODE=dbg
;; M-5 C-c C-a Compile w/ MODE=""
;;
(defvar cosmo--assembly-root nil)
(defvar cosmo--assembly-pending nil)
(defun cosmo--scrub (regex replace)
(replace-regexp regex replace nil (point-min) (point-max)))
(defun cosmo--fixup-asm ()
(cosmo--scrub
;; Code like this
;;
;; .section "GNU!GNU!GNU!.must.be.in.every.asm.or.mprotect.breaks"
;;
;; Isn't needed since the underlying issue is addressed by ape/ape.S
;; which generates executable structure.
" .section .note.GNU-stack,.*,@progbits\n"
"")
(cosmo--scrub
;; Code like this
;;
;; and $2047, %edx
;;
;; Should be this
;;
;; and $2047,%edx
;;
;; Per our favored coding style.
", "
",")
(cosmo--scrub
;; Code like this
;;
;; .ascii "foo"
;; .zero 1
;;
;; Should be this
;;
;; .ascii "foo"
;; .zero 1
;;
;; @see -frecord-gcc-switches
"\t\\.ascii\t\"\\([^\"]*\\)\"\n\t\\.zero\t1\n"
"\t.asciz\t\"\\1\"\n")
(cosmo--scrub
;; Code like this
;;
;; movl %eax,a
;; movb b,%bl
;; movb $1,c
;; incb d
;; movq %xmm0,%rax
;;
;; Should be this
;;
;; mov %eax,a
;; mov %bl,b
;; movb $1,c
;; incb d
;; movq %xmm0,%rax # ;_;
;;
;; Because we dislike redundancy and don't want the
;; ibmicroborlandtel crowd feeling too uncomfortable.
(let ((names-a-register
"%[A-Za-z]")
(normal-arithmetic-ops
(regexp-opt
(list
"rcr" "rcl" "ror" "rol" "hlt" "cmc" "div" "sar"
"mov" "shl" "shr" "lea" "cmp" "adc" "sbb" "inc"
"sub" "xor" "add" "and" "or" "not" "btc" "dec"
"mul" "neg" "bt" "bsf" "bsr" "int" "test")))
(prefixes
(regexp-opt
(list
"es" "cs" "ss" "ds" "lock"
"rep" "repnz" "repne" "repz" "repe"
"rex.b" "rex.x" "rex.xb" "rex.r" "rex.rb" "rex.rx"
"rex.rxb" "rex.w" "rex.wb" "rex.wx" "rex.wxb" "rex.wr"
"rex.trb" "rex.wrx" "rex.wrxb"))))
(concat "\\("
"^[ \t]*"
"\\(?:" prefixes " \\|\\)"
normal-arithmetic-ops
"\\)"
"[bwlq]"
"\\("
"[ \t]+"
"\\(?:"
"%[A-Wa-w][^\n]+"
"\\|"
"[^%][^\n,]*,%[A-Za-z][^\n]+"
"\\)"
"\\)"))
"\\1\\2")
;; Scroll first function to top of display.
(goto-char (point-min))
(when (search-forward-regexp
"\t\\.type[^\n]*@function" nil t)
(recenter-top-bottom 'top)))
(defun cosmo--assembly-compilation-finished (compilebuf msg)
(when cosmo--assembly-pending
(let ((asm-gcc (car cosmo--assembly-pending))
(asm-clang (cdr cosmo--assembly-pending))
width
asm-gcc-buffer
asm-gcc-window
asm-clang-buffer
asm-clang-window)
(setq cosmo--assembly-pending nil)
(when (not (cosmo-contains "finished" msg))
(error "making assembly failed: %s" msg))
(let ((f (format "%s/%s" cosmo--assembly-root asm-gcc)))
(when (or (null asm-gcc) (not (file-exists-p f)))
(error "asm-gcc not found: %s" f)))
(let ((f (format "%s/%s" cosmo--assembly-root asm-clang)))
(when (or (null asm-gcc) (not (file-exists-p f)))
(error "asm-gcc not found: %s" f)))
(delete-other-windows)
(setq width (window-total-width))
(setq asm-gcc-buffer (find-file-noselect asm-gcc t nil nil))
(setq asm-clang-buffer (find-file-noselect asm-clang t nil nil))
(setq asm-clang-window (split-window-right (- width (/ width 4))))
(setq asm-gcc-window (split-window-right (- (- width (/ width 4)) (/ width 4))))
(set-window-buffer asm-gcc-window asm-gcc-buffer)
(set-window-buffer asm-clang-window asm-clang-buffer)
(other-window 1)
(cosmo--fixup-asm)
(save-buffer)
(bury-buffer (current-buffer))
(other-window 1)
(cosmo--fixup-asm)
(save-buffer)
(bury-buffer (current-buffer))
(other-window 1)
(bury-buffer compilebuf))))
(defun cosmo--purge-file (path)
(let ((b (find-file-noselect path t nil nil)))
(when b
(with-current-buffer b
(save-buffer))
(when (not (kill-buffer b))
(error "couldn't kill asm buffer"))))
(delete-file path t))
(defun cosmo--assembly (arg extra-make-flags)
(let* ((this (expand-file-name (or (buffer-file-name) dired-directory)))
(root (expand-file-name (locate-dominating-file this "Makefile"))))
(when root
(let* ((mode (cosmo--make-mode arg "opt"))
(ext (file-name-extension this))
(dir (file-name-directory this))
(dots (file-relative-name root dir))
(file (file-relative-name this root))
(name (file-name-sans-extension file))
(asm-gcc (format "o/%s/%s-gcc.asm" mode name))
(asm-clang (format "o/%s/%s-clang.asm" mode name)))
(when (not (member ext '("c" "cc" "f")))
(error "don't know how to show assembly for non c/c++ source file"))
(let* ((default-directory root)
(compile-command
(format "make %s SILENT=0 -j8 -O MODE=%s %s %s"
(or extra-make-flags "") mode asm-gcc asm-clang)))
(save-buffer)
(set-visited-file-modtime (current-time))
(cosmo--purge-file asm-gcc)
(cosmo--purge-file asm-clang)
(save-buffer)
(setq cosmo--assembly-root root)
(setq cosmo--assembly-pending (cons asm-gcc asm-clang))
(let ((errormsg (shell-command-to-string (format "touch %s" file))))
(when (not (equal "" errormsg))
(error errormsg)))
(revert-buffer :ignore-auto :noconfirm)
(compile compile-command))))))
(defun cosmo-assembly (arg)
(interactive "P")
(setq arg (or arg 2))
(cond ((not (eq 0 (logand 8 arg)))
(cosmo--assembly (setq arg (logand (lognot 8)))
"SILENT=0 COPTS='-ffast-math -O3 -funsafe-math-optimizations -fsched2-use-superblocks'"))
(t (cosmo--assembly arg "SILENT=0 COPTS='-O3'"))))
(defun cosmo-assembly-native (arg)
(interactive "P")
(setq arg (or arg 3))
(cond ((not (eq 0 (logand 8 arg)))
(cosmo--assembly
(setq arg (logand (lognot 8)))
"SILENT=0 CCFLAGS=--verbose COPTS='$(IEEE_MATH)' TARGET_ARCH='-march=znver2'"))
(t
(cosmo--assembly
arg
"SILENT=0 CCFLAGS=--verbose COPTS='$(MATHEMATICAL) -O3' TARGET_ARCH='-march=znver2'"))))
(defun cosmo-assembly-icelake (arg)
(interactive "P")
(setq arg (or arg 3))
(cond ((not (eq 0 (logand 8 arg)))
(cosmo--assembly
(setq arg (logand (lognot 8)))
"SILENT=0 CCFLAGS=--verbose COPTS='$(MATHEMATICAL) -O3' TARGET_ARCH='-march=icelake-client'"))
(t
(cosmo--assembly
arg
"SILENT=0 CCFLAGS=--verbose COPTS='$(MATHEMATICAL) -O3' TARGET_ARCH='-march=icelake-client'"))))
(defun cosmo-assembly-balanced (arg)
(interactive "P")
(cosmo--assembly (or arg 5) "CFLAGS='-O2 -ftrapv' SILENT=0"))
(defun cosmo-mca (arg)
(interactive "P")
(let (code
delete
cleanup
(inhibit-message t)
(inhibit-read-only t)
(term (getenv "TERM"))
(prog (executable-find "llvm-mca-10"))
(buf1 (get-buffer-create "*mca*"))
(buf2 (generate-new-buffer "*mca*")))
(setenv "TERM" "xterm-256color")
(setq cleanup
(lambda ()
(setenv term)
(kill-buffer buf2)
(when delete (delete-file delete))))
(condition-case exc
(progn
(if (not (buffer-modified-p))
(setq code (buffer-file-name))
(setq code (make-temp-file "mca.s"))
(write-region nil nil code)
(setq delete code))
(with-current-buffer buf2
(insert "\n")
(setq rc (call-process prog nil t nil
"-mcpu=skylake"
"-mtriple=x86_64-pc-linux-gnu"
"--bottleneck-analysis"
"-instruction-info"
"-iterations=8"
"-all-stats"
"-all-views"
"-timeline"
code)))
(with-current-buffer buf1
(replace-buffer-contents buf2)
(if (eq rc 0)
(fundamental-mode)
(compilation-mode))
(xterm-color-colorize-buffer)
(display-buffer (current-buffer))))
('error
(funcall cleanup)
(error exc)))
(funcall cleanup)))
(defun cosmo-assemble-hook ()
(add-to-list 'compilation-finish-functions
'cosmo--assembly-compilation-finished)
(local-set-key (kbd "C-c C-a") 'cosmo-assembly)
(local-set-key (kbd "C-c C-b") 'cosmo-assembly-balanced)
(local-set-key (kbd "C-c C-n") 'cosmo-assembly-native)
(local-set-key (kbd "C-c C-i") 'cosmo-assembly-icelake))
(defun cosmo-mca-hook ()
;; (local-set-key (kbd "C-c C-h") 'cosmo-mca)
)
(progn
(add-hook 'asm-mode-hook 'cosmo-mca-hook)
(add-hook 'makefile-mode-hook 'cosmo-assemble-hook)
(add-hook 'asm-mode-hook 'cosmo-assemble-hook)
(add-hook 'ld-script-mode-hook 'cosmo-assemble-hook)
(add-hook 'dired-mode-hook 'cosmo-assemble-hook)
(add-hook 'c-mode-common-hook 'cosmo-assemble-hook)
(add-hook 'fortran-mode-hook 'cosmo-assemble-hook)
(add-hook 'protobuf-mode-hook 'cosmo-assemble-hook))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Run buffer.
;; C-c C-r Run
;; M-1 C-c C-r Run w/ MODE=tiny
;; M-2 C-c C-r Run w/ MODE=opt
;; M-3 C-c C-r Run w/ MODE=rel
;; M-4 C-c C-r Run w/ MODE=dbg
;; M-5 C-c C-r Run w/ MODE=""
(defun cosmo-run (arg)
(interactive "P")
(let* ((this (or (buffer-file-name) dired-directory))
(root (or (locate-dominating-file this "Makefile") default-directory))
(file (file-relative-name this root)))
(when root
(let ((default-directory root))
(save-buffer)
(cond ((memq major-mode '(c-mode c++-mode asm-mode fortran-mode))
(let* ((mode (cosmo--make-mode arg))
(compile-command (cosmo--compile-command this root 'run mode)))
(compile compile-command)))
((eq major-mode 'sh-mode)
(compile (format "sh %s" file)))
((eq major-mode 'python-mode)
(compile (format "python %s" file)))
('t
(error "cosmo-run: unknown major mode")))))))
(progn
(define-key asm-mode-map (kbd "C-c C-r") 'cosmo-run)
(define-key c-mode-base-map (kbd "C-c C-r") 'cosmo-run)
(define-key fortran-mode-map (kbd "C-c C-r") 'cosmo-run)
(define-key sh-mode-map (kbd "C-c C-r") 'cosmo-run)
(define-key python-mode-map (kbd "C-c C-r") 'cosmo-run))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Debug buffer.
;; C-c C-d Run in GDB/GUD
;; M-1 C-c C-d Run in GDB/GUD w/ MODE=tiny
;; M-2 C-c C-d Run in GDB/GUD w/ MODE=opt
;; M-3 C-c C-d Run in GDB/GUD w/ MODE=rel
;; M-4 C-c C-d Run in GDB/GUD w/ MODE=dbg
;; M-5 C-c C-d Run in GDB/GUD w/ MODE=""
(defun cosmo-debug (arg)
(interactive "P")
(let* ((this (or (buffer-file-name) dired-directory))
(root (locate-dominating-file this "Makefile")))
(when root
(let* ((mode (cosmo--make-mode arg "dbg"))
(name (file-relative-name this root))
(next (file-name-sans-extension name))
(exec (format "o/%s/%s.com.dbg" mode next))
(default-directory root)
(compile-command (cosmo--compile-command this root nil mode)))
(compile compile-command)
(gdb (format "gdb -q -nh -i=mi %s -ex run" exec))))))
(progn
(define-key asm-mode-map (kbd "C-c C-d") 'cosmo-debug)
(define-key c-mode-base-map (kbd "C-c C-d") 'cosmo-debug))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; C-c C-t Toggle buffer between source file and unit test file.
(defun cosmo-toggle-buddy ()
(interactive)
(let* ((this (or (buffer-file-name) dired-directory))
(root (locate-dominating-file this "Makefile")))
(when root
(let* ((name (file-relative-name this root))
(dir (file-name-directory this))
(pkgname (file-name-nondirectory (substring dir 0 -1)))
(dots (file-relative-name root dir))
(notest (cosmo-file-name-sans-extensions
(cosmo-replace "_test" "" (cosmo-lchop "test/" name))))
(buddy
(cond ((and (cosmo-startswith "test/" dir)
(cosmo-endswith "/test.mk" name))
(message (format "%s/%s.mk" (substring dir 5) pkgname))
(format "%s/%s.mk" (substring dir 5) pkgname))
((cosmo-startswith "test/" name)
(cosmo-first-that
'file-exists-p
(list (concat dots notest ".s")
(concat dots notest ".S")
(concat dots notest ".f")
(concat dots notest ".F")
(concat dots notest ".c")
(concat dots notest ".cc")
(concat dots notest ".rl")
(concat dots notest ".ncabi.c")
(concat dots notest ".hookabi.c")
(concat dots notest ".h"))))
(t
(format "%stest/%s_test.c"
dots (cosmo-file-name-sans-extensions name))))))
(when buddy
(find-file buddy))))))
(progn
(global-set-key (kbd "C-c C-t") 'cosmo-toggle-buddy))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; C-c C-h Add Include Line
(defun cosmo-add-include ()
(interactive)
(let* ((no-whine t)
(tag-file "HTAGS")
(this (buffer-name))
(case-fold-search nil)
(search (thing-at-point 'symbol))
(buffer (find-file-noselect (format "%s/%s"
(locate-dominating-file
this tag-file)
tag-file)
no-whine))
(header (with-current-buffer buffer
(revert-buffer :ignore-auto :noconfirm)
(save-excursion
(goto-char 0)
(when (re-search-forward
(concat "\177" search "\001") nil t)
(when (re-search-backward "\f\n\\([^,]*\\)," nil t)
(match-string 1))))))
(root (locate-dominating-file this "Makefile"))
(name (file-relative-name this root)))
(when header
(when (not (equal header name))
(save-excursion
(goto-char 0)
(re-search-forward "#include" nil t)
(re-search-forward "^$")
(re-search-backward "#include" nil t)
(beginning-of-line)
(insert (concat "#include \"" header "\"\n"))))
(message header))))
(progn
(define-key asm-mode-map (kbd "C-c C-h") 'cosmo-add-include)
(define-key c-mode-base-map (kbd "C-c C-h") 'cosmo-add-include))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; C-c C-o Show Optimization Report
(defun cosmo-show-optinfo (arg)
(interactive "P")
(let* ((mode (cosmo--make-mode arg "opt"))
(this (or (buffer-file-name) dired-directory))
(root (locate-dominating-file this "Makefile")))
(when root
(let* ((name (file-relative-name this root))
(buddy
(format "%s/o/%s/%s.optinfo.gz"
root mode (cosmo-file-name-sans-extensions name))))
(when buddy
(find-file buddy))))))
(defun cosmo-lisp-is-the-worst-this-is-so-tiring ()
(define-key c-mode-base-map (kbd "C-c C-o") 'cosmo-show-optinfo))
(add-hook 'c-mode-common-hook 'cosmo-lisp-is-the-worst-this-is-so-tiring)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Cosmopolitan Extended Language Keyword Definitions
(defun cosmo-keywords-hook ()
(font-lock-add-keywords
nil `((,cosmo-c-keywords-regex . font-lock-keyword-face)
(,cosmo-c-builtins-regex . font-lock-builtin-face)
(,cosmo-c-constants-regex . font-lock-constant-face)
(,cosmo-c-types-regex . font-lock-type-face))))
(add-hook 'c-mode-common-hook 'cosmo-keywords-hook)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Symbol naming convention conversion
;; e.g. STARTF_USESHOWWINDOW -> kNtStartfUseshowwindow
(defun cosmo--ms-to-google-const (s)
(declare (pure t) (side-effect-free t))
(let ((d (downcase (substring s 1))))
(cosmo-replace
"kNtNt" "kNt"
(concat
"kNt"
(substring s 0 1)
(replace-regexp-in-string
"_\\([A-Za-z]\\)"
(lambda (m)
(upcase (match-string 1 m)))
d)))))
;; e.g. kNtStartfUseshowwindow -> STARTF_USESHOWWINDOW
(defun cosmo--google-to-ms-const (s)
(declare (pure t) (side-effect-free t))
(upcase (replace-regexp-in-string
"\\(.\\)\\([A-Z]\\)"
(lambda (m)
(upcase (concat
(match-string 1 m) "_"
(match-string 2 m))))
(substring s 3)
t)))
(defun cosmo-toggle-ms-const ()
(interactive)
(let* ((case-fold-search nil)
(bounds (if (use-region-p)
(cons (region-beginning) (region-end))
(bounds-of-thing-at-point 'symbol)))
(text (buffer-substring-no-properties (car bounds) (cdr bounds))))
(when bounds
(let ((fn (if (or (cosmo-contains "_" text)
(equal text (upcase text)))
'cosmo--ms-to-google-const
'cosmo--google-to-ms-const)))
(delete-region (car bounds) (cdr bounds))
(insert (funcall fn text))))))
;; (define-key c-mode-base-map (kbd "C-c C-l") 'cosmo-toggle-ms-const)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CP/M EOF for the lulz
(defun cosmo-before-save ()
(cond ((memq major-mode '(c-mode asm-mode))
(set (make-local-variable 'require-final-newline)
(not (equal (buffer-substring-no-properties
(- (point-max) 1) (point-max))
"\x1a"))))))
(add-hook 'before-save-hook 'cosmo-before-save)
(provide 'cosmo-stuff)
;;; cosmo-stuff.el ends here

53
tool/emacs/javadown.el Normal file
View file

@ -0,0 +1,53 @@
;;; javadown.el --- C/C++ Markdown Javadocs
;; Copyright 2019 Justine Tunney
;; Author: Justine Tunney
;; Version: 0.1
;; 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.
;;; Commentary:
;;
;; Unintentionally empty.
;;; Code:
(require 'cc-fonts)
(defconst javadown-font-lock-doc-comments
`(;; e.g. ### Implementation Details
("^\\(/\\*\\)?\\(\\s \\|\\*\\)*\\(#+[^\n\r]+\\)"
3 ,c-doc-markup-face-name prepend nil)
;; e.g. {@code param}
("{@[a-z]+[^}\n\r]*}"
0 ,c-doc-markup-face-name prepend nil)
;; e.g. @param name does stuff
("^\\(/\\*\\)?\\(\\s \\|\\*\\)*\\(@[a-z]+\\)"
3 ,c-doc-markup-face-name prepend nil)
;; e.g. <video src=doge.mp4>
(,(concat "</?\\sw"
"\\("
(concat "\\sw\\|\\s \\|[=\n\r*.:]\\|"
"\"[^\"]*\"\\|'[^']*'")
"\\)*>")
0 ,c-doc-markup-face-name prepend nil)))
(defconst javadown-font-lock-keywords
`((,(lambda (limit)
(c-font-lock-doc-comments "/\\*\\*" limit
javadown-font-lock-doc-comments)))))
(provide 'javadown)
;;; javadown.el ends here

486
tool/emacs/key.py Normal file
View file

@ -0,0 +1,486 @@
import os,sys,re
kar_types = frozenset([
"char",
"signed char",
"unsigned char",
"int",
"signed",
"signed int",
"unsigned",
"unsigned int",
"short",
"short int",
"short signed",
"short signed int",
"short unsigned",
"short unsigned int",
"signed short",
"signed short int",
"unsigned short",
"unsigned short int",
"long",
"long int",
"long signed",
"long signed int",
"long unsigned",
"long unsigned int",
"signed long",
"signed long int",
"unsigned long",
"unsigned long int",
"float",
"double",
])
ansi_types = frozenset([
"void",
"wchar_t",
"wint_t",
"size_t",
"long double",
])
c99_types = frozenset([
"long long",
"long long int",
"long long signed",
"long long signed int",
"long long unsigned",
"long long unsigned int",
"signed long long",
"signed long long int",
"unsigned long long",
"unsigned long long int",
"char16_t",
"char32_t",
"errno_t",
"int8_t",
"int16_t",
"int32_t",
"int64_t",
"int_fast8_t",
"int_fast16_t",
"int_fast32_t",
"int_fast64_t",
"int_least16_t",
"int_least32_t",
"int_least64_t",
"int_least8_t",
"uint_fast16_t",
"uint_fast32_t",
"uint_fast64_t",
"uint_fast8_t",
"uint_least16_t",
"uint_least32_t",
"uint_least64_t",
"uint_least8_t",
"intmax_t",
"intptr_t",
"ptrdiff_t",
"ssize_t",
"uint16_t",
"uint32_t",
"uint64_t",
"uint64_t",
"uint8_t",
"uintmax_t",
"uintptr_t",
])
c11_types = frozenset([
"atomic_bool",
"atomic_char",
"atomic_schar",
"atomic_uchar",
"atomic_short",
"atomic_ushort",
"atomic_int",
"atomic_uint",
"atomic_long",
"atomic_ulong",
"atomic_llong",
"atomic_ullong",
"atomic_char16_t",
"atomic_char32_t",
"atomic_wchar_t",
"atomic_int_least8_t",
"atomic_uint_least8_t",
"atomic_int_least16_t",
"atomic_uint_least16_t",
"atomic_int_least32_t",
"atomic_uint_least32_t",
"atomic_int_least64_t",
"atomic_uint_least64_t",
"atomic_int_fast8_t",
"atomic_uint_fast8_t",
"atomic_int_fast16_t",
"atomic_uint_fast16_t",
"atomic_int_fast32_t",
"atomic_uint_fast32_t",
"atomic_int_fast64_t",
"atomic_uint_fast64_t",
"atomic_intptr_t",
"atomic_uintptr_t",
"atomic_size_t",
"atomic_ptrdiff_t",
"atomic_intmax_t",
"atomic_uintmax_t",
])
gnu_types = frozenset([
"__int128",
"signed __int128",
"unsigned __int128",
])
cxx17_types = frozenset([
"bool",
])
cosmo_types = frozenset([
"bool32",
"int128_t",
"int_fast128_t",
"int_least128_t",
"uint128_t",
])
x86intrin_types = frozenset([
"__m1",
"__m2",
"__m64",
"__m128",
"__m128_u",
"__m128d",
"__m128d_u",
"__m128i",
"__m128i_u",
"__v16qi",
"__v16qs",
"__v16qu",
"__v1di",
"__v2df",
"__v2di",
"__v2du",
"__v2sf",
"__v2si",
"__v4hi",
"__v4sf",
"__v4si",
"__v4su",
"__v8hi",
"__v8hu",
"__v8qi",
])
################################################################################
kar_kws = frozenset([
"auto",
"if",
"break",
"case",
"while",
"continue",
"default",
"return",
"do",
"signed",
"else",
"sizeof",
"extern",
"struct",
"switch",
"for",
"goto",
"union",
])
ansi_kws = frozenset([
"auto",
"if",
"break",
"case",
"volatile",
"while",
"const",
"register",
"continue",
"default",
"return",
"do",
"double",
"signed",
"else",
"sizeof",
"static",
"extern",
"struct",
"float",
"switch",
"for",
"typedef",
"goto",
"union",
"typedef",
"enum",
])
c99_kws= frozenset([
"_Bool",
"_Complex",
"_Imaginary",
"inline",
"restrict",
])
c11_kws = frozenset([
"_Alignas",
"_Alignof",
"_Atomic",
"_Generic",
"_Noreturn",
"_Static_assert",
"_Thread_local",
])
cxx17_kws = frozenset([
"alignas",
"alignof",
"asm",
"auto",
"bool",
"break",
"case",
"catch",
"class",
"const",
"const_cast",
"constexpr",
"continue",
"decltype",
"default",
"delete",
"do",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
"float",
"for",
"friend",
"goto",
"if",
"inline",
"mutable",
"namespace",
"new",
"noexcept",
"nullptr",
"operator",
"private",
"protected",
"public",
"register",
"reinterpret_cast",
"return",
"short",
"signed",
"sizeof",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
"thread_local",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
"union",
"using",
"virtual",
"virtual",
"volatile",
"volatile",
"while",
])
cosmo_kws = frozenset([
"pass",
"alignas",
"aligned",
"alignof",
"artificial",
"attributeallocalign",
"attributeallocsize",
"autotype",
"byanymeansnecessary",
"compatfn",
"decltype",
"externinline",
"firstclass",
"flattenout",
"forcealignargpointer",
"forceinline",
"frownedupon",
"hasatleast",
"hidden",
"initarray",
"interruptfn",
"mallocesque",
"mayalias",
"memcpyesque",
"nocallback",
"nodebuginfo",
"nodiscard",
"noinline",
"noinstrument",
"nointerpose",
"nooptimize",
"noprune",
"noreturn",
"nosideeffect",
"nothrow",
"nothrow",
"null",
"nullterminated",
"paramsnonnull",
"preinitarray",
"printfesque",
"privileged",
"pureconst",
"reallocesque",
"relegated",
"returnsnonnull",
"returnspointerwithnoaliases",
"returnstwice",
"scanfesque",
"strftimeesque",
"strlenesque",
"testonly",
"textexit",
"textreal",
"textstartup",
"textwindows",
"thatispacked",
"threadlocal",
"typeof",
"unreachable",
"warnifused",
"winstruct",
"nocallersavedregisters",
"pass",
"alignas",
"aligned",
"alignof",
"artificial",
"attributeallocalign",
"attributeallocsize",
"autotype",
"byanymeansnecessary",
"compatfn",
"decltype",
"externinline",
"firstclass",
"flattenout",
"forcealignargpointer",
"forceinline",
"frownedupon",
"hasatleast",
"hidden",
"initarray",
"interruptfn",
"mallocesque",
"mayalias",
"memcpyesque",
"nocallback",
"nodebuginfo",
"nodiscard",
"noinline",
"noinstrument",
"nointerpose",
"nooptimize",
"noprune",
"noreturn",
"nosideeffect",
"nothrow",
"nothrow",
"null",
"nullterminated",
"paramsnonnull",
"preinitarray",
"printfesque",
"privileged",
"pureconst",
"reallocesque",
"relegated",
"returnsnonnull",
"returnspointerwithnoaliases",
"returnstwice",
"scanfesque",
"strftimeesque",
"strlenesque",
"testonly",
"textexit",
"textreal",
"textstartup",
"textwindows",
"thatispacked",
"threadlocal",
"typeof",
"unreachable",
"warnifused",
"winstruct",
"nocallersavedregisters",
])
################################################################################
typegroups = (("kar", kar_types),
("ansi", ansi_types),
("c99", c99_types),
("c11", c11_types),
("gnu", gnu_types),
("cxx17", cxx17_types),
("cosmo", cosmo_types),
("x86intrin", x86intrin_types))
kwgroups = (("kar", kar_kws),
("ansi", ansi_kws),
("c99", c99_kws),
("c11", c11_kws),
("cxx17", cxx17_kws),
("cosmo", cosmo_kws))
types = reduce(lambda a,b: a|b[1], typegroups, set())
kws = reduce(lambda a,b: a|b[1], kwgroups, set())
################################################################################
for name, gg, nonono in (("cosmo-c-types", typegroups, kws),
("cosmo-c-keywords", kwgroups, types)):
first = True
sys.stdout.write("""\
(defconst %s-regex
(let (""" % name)
for k, vs in gg:
sys.stdout.write(("""%s(%s (list %s))
""" % ("" if first else "\n ", k, """
""".join('"%s"' % repr(s)[1:][:-1]
for s in vs - nonono))).rstrip())
first = False
sys.stdout.write(""")
(concat "\\<" (regexp-opt (append """)
sys.stdout.write("""
""".join(k for k,_ in gg))
sys.stdout.write(""")) "\\>")))\n\n""")

185
tool/emacs/ld-script.el Normal file
View file

@ -0,0 +1,185 @@
;;; ld-script.el --- GNU linker script editing mode for Emacs
;; Copyright (C) 2001-2018 Free Software Foundation, Inc.
;; Author: Masatake YAMATO<jet@gyve.org>
;; Keywords: languages, faces
;; This file is part of GNU Emacs.
;; GNU Emacs 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, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Major mode for editing GNU linker (ld) scripts.
;;; Code:
;; Custom
(defgroup ld-script nil
"GNU linker script code editing commands for Emacs."
:prefix "ld-script-"
:group 'languages)
(defvar ld-script-location-counter-face 'ld-script-location-counter)
(defface ld-script-location-counter
'((t :weight bold :inherit font-lock-builtin-face))
"Face for location counter in GNU ld script."
:group 'ld-script)
;; Syntax rules
(defvar ld-script-mode-syntax-table
(let ((st (make-syntax-table)))
(modify-syntax-entry ?\ "-" st)
(modify-syntax-entry ?{ "(}" st)
(modify-syntax-entry ?} "){" st)
(modify-syntax-entry ?\( "()" st)
(modify-syntax-entry ?\) ")(" st)
(modify-syntax-entry ?\[ "(]" st)
(modify-syntax-entry ?\] ")[" st)
(modify-syntax-entry ?_ "_" st)
(modify-syntax-entry ?. "_" st)
(modify-syntax-entry ?\\ "\\" st)
(modify-syntax-entry ?: "." st)
(modify-syntax-entry ?, "." st)
(modify-syntax-entry ?? "." st)
(modify-syntax-entry ?= "." st)
(modify-syntax-entry ?* ". 23" st)
(modify-syntax-entry ?/ ". 14" st)
(modify-syntax-entry ?+ "." st)
(modify-syntax-entry ?- "." st)
(modify-syntax-entry ?! "." st)
(modify-syntax-entry ?~ "." st)
(modify-syntax-entry ?% "." st)
(modify-syntax-entry ?< "." st)
(modify-syntax-entry ?> "." st)
(modify-syntax-entry ?& "." st)
(modify-syntax-entry ?| "." st)
(modify-syntax-entry ?\" "\"" st)
st)
"Syntax table used while in `ld-script-mode'.")
;; Font lock keywords
;; (The section number comes from ld's info.)
(defvar ld-script-keywords
'(
;; 3.4.1 Setting the Entry Point
"ENTRY"
;; 3.4.2 Commands Dealing with Files
"INCLUDE" "INPUT" "GROUP" "AS_NEEDED" "OUTPUT" "SEARCH_DIR" "STARTUP"
;; 3.4.3 Commands Dealing with Object File Formats
"OUTPUT_FORMAT" "TARGET"
;; 3.4.4 Assign alias names to memory regions
"REGION_ALIAS"
;; 3.4.5 Other Linker Script Commands
"ASSERT" "EXTERN" "FORCE_COMMON_ALLOCATION"
"INHIBIT_COMMON_ALLOCATION" "INSERT" "AFTER" "BEFORE"
"NOCROSSREFS" "NOCROSSREFS_TO" "OUTPUT_ARCH" "LD_FEATURE"
;; 3.5.2 HIDDEN
"HIDDEN"
;; 3.5.3 PROVIDE
"PROVIDE"
;; 3.5.4 PROVIDE_HIDDEN
"PROVIDE_HIDDEN"
;; 3.6 SECTIONS Command
"SECTIONS"
;; 3.6.4.2 Input Section Wildcard Patterns
"SORT" "SORT_NONE" "SORT_BY_NAME" "SORT_BY_ALIGNMENT"
"SORT_BY_INIT_PRIORITY"
;; 3.6.4.3 Input Section for Common Symbols
"COMMON"
;; 3.6.4.4 Input Section and Garbage Collection
"KEEP"
;; 3.6.5 Output Section Data
"BYTE" "SHORT" "LONG" "QUAD" "SQUAD" "FILL"
;; 3.6.6 Output Section Keywords
"CREATE_OBJECT_SYMBOLS" "CONSTRUCTORS"
"__CTOR_LIST__" "__CTOR_END__" "__DTOR_LIST__" "__DTOR_END__"
;; 3.6.7 Output Section Discarding
;; See `ld-script-font-lock-keywords'
;; 3.6.8.1 Output Section Type
"NOLOAD" "DSECT" "COPY" "INFO" "OVERLAY"
;; 3.6.8.2 Output Section LMA
"AT"
;; 3.6.8.4 Forced Input Alignment
"SUBALIGN"
;; 3.6.8.5 Output Section Constraint
"ONLY_IF_RO" "ONLY_IF_RW"
;; 3.6.8.7 Output Section Phdr
":PHDR"
;; 3.7 MEMORY Command
"MEMORY"
;; 3.8 PHDRS Command
"PHDRS" "FILEHDR" "FLAGS"
"PT_NULL" "PT_LOAD" "PT_DYNAMIC" "PT_INTERP" "PT_NOTE" "PT_SHLIB" "PT_PHDR"
"PT_GNU_STACK"
;; 3.9 VERSION Command
"VERSION")
"Keywords used of GNU ld script.")
;; 3.10.2 Symbolic Constants
;; 3.10.9 Builtin Functions
(defvar ld-script-builtins
'("CONSTANT"
"MAXPAGESIZE"
"COMMONPAGESIZE"
"ABSOLUTE"
"ADDR"
"ALIGN"
"ALIGNOF"
"BLOCK"
"DATA_SEGMENT_ALIGN"
"DATA_SEGMENT_END"
"DATA_SEGMENT_RELRO_END"
"DEFINED"
"LENGTH" "len" "l"
"LOADADDR"
"LOG2CEIL"
"MAX"
"MIN"
"NEXT"
"ORIGIN" "org" "o"
"SEGMENT_START"
"SIZEOF"
"SIZEOF_HEADERS"
"sizeof_headers")
"Builtin functions of GNU ld script.")
(defvar ld-script-font-lock-keywords
(append
`((,(concat "\\_<" (regexp-opt ld-script-keywords) "\\_>")
0 font-lock-keyword-face)
(,(concat "\\_<" (regexp-opt ld-script-builtins) "\\_>")
0 font-lock-builtin-face)
;; 3.6.7 Output Section Discarding
;; 3.6.4.1 Input Section Basics
;; 3.6.8.7 Output Section Phdr
("/DISCARD/\\|EXCLUDE_FILE\\|:NONE" . font-lock-warning-face)
("\\W\\(\\.\\)\\W" 1 ld-script-location-counter-face)
)
cpp-font-lock-keywords)
"Default font-lock-keywords for `ld-script-mode'.")
;;;###autoload
(define-derived-mode ld-script-mode prog-mode "LD-Script"
"A major mode to edit GNU ld script files"
(set (make-local-variable 'comment-start) "/* ")
(set (make-local-variable 'comment-end) " */")
(set (make-local-variable 'font-lock-defaults)
'(ld-script-font-lock-keywords nil)))
(provide 'ld-script)
;;; ld-script.el ends here

View file

@ -0,0 +1,11 @@
(require 'compile)
(define-derived-mode optinfo-mode compilation-mode "Optimization Info"
(let ((root (locate-dominating-file (buffer-file-name) "Makefile")))
(when root
(setq-local default-directory root))))
(auto-compression-mode t)
(add-to-list 'auto-mode-alist '("\\.optinfo\\(\\|\\.gz\\)$" . optinfo-mode))
(provide 'optinfo-mode)

224
tool/emacs/protobuf-mode.el Normal file
View file

@ -0,0 +1,224 @@
;;; protobuf-mode.el --- major mode for editing protocol buffers.
;; Author: Alexandre Vassalotti <alexandre@peadrop.com>
;; Created: 23-Apr-2009
;; Version: 0.3
;; Package-Version: 3.10.0
;; Keywords: google protobuf languages
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions are
;; met:
;;
;; * Redistributions of source code must retain the above copyright
;; notice, this list of conditions and the following disclaimer.
;; * Redistributions in binary form must reproduce the above
;; copyright notice, this list of conditions and the following disclaimer
;; in the documentation and/or other materials provided with the
;; distribution.
;; * Neither the name of Google Inc. nor the names of its
;; contributors may be used to endorse or promote products derived from
;; this software without specific prior written permission.
;;
;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;;; Commentary:
;; Installation:
;; - Put `protobuf-mode.el' in your Emacs load-path.
;; - Add this line to your .emacs file:
;; (require 'protobuf-mode)
;;
;; You can customize this mode just like any mode derived from CC Mode. If
;; you want to add customizations specific to protobuf-mode, you can use the
;; `protobuf-mode-hook'. For example, the following would make protocol-mode
;; use 2-space indentation:
;;
;; (defconst my-protobuf-style
;; '((c-basic-offset . 2)
;; (indent-tabs-mode . nil)))
;;
;; (add-hook 'protobuf-mode-hook
;; (lambda () (c-add-style "my-style" my-protobuf-style t)))
;;
;; Refer to the documentation of CC Mode for more information about
;; customization details and how to use this mode.
;;
;; TODO:
;; - Make highlighting for enum values work properly.
;; - Fix the parser to recognize extensions as identifiers and not
;; as casts.
;; - Improve the parsing of option assignment lists. For example:
;; optional int32 foo = 1 [(my_field_option) = 4.5];
;; - Add support for fully-qualified identifiers (e.g., with a leading ".").
;;; Code:
(require 'cc-mode)
(eval-when-compile
(and (= emacs-major-version 24)
(>= emacs-minor-version 4)
(require 'cl))
(require 'cc-langs)
(require 'cc-fonts))
;; This mode does not inherit properties from other modes. So, we do not use
;; the usual `c-add-language' function.
(eval-and-compile
(put 'protobuf-mode 'c-mode-prefix "protobuf-"))
;; The following code uses of the `c-lang-defconst' macro define syntactic
;; features of protocol buffer language. Refer to the documentation in the
;; cc-langs.el file for information about the meaning of the -kwds variables.
(c-lang-defconst c-primitive-type-kwds
protobuf '("double" "float" "int32" "int64" "uint32" "uint64" "sint32"
"sint64" "fixed32" "fixed64" "sfixed32" "sfixed64" "bool"
"string" "bytes" "group"))
(c-lang-defconst c-modifier-kwds
protobuf '("required" "optional" "repeated"))
(c-lang-defconst c-class-decl-kwds
protobuf '("message" "enum" "service"))
(c-lang-defconst c-constant-kwds
protobuf '("true" "false"))
(c-lang-defconst c-other-decl-kwds
protobuf '("package" "import"))
(c-lang-defconst c-other-kwds
protobuf '("default" "max"))
(c-lang-defconst c-identifier-ops
;; Handle extended identifiers like google.protobuf.MessageOptions
protobuf '((left-assoc ".")))
;; The following keywords do not fit well in keyword classes defined by
;; cc-mode. So, we approximate as best we can.
(c-lang-defconst c-type-list-kwds
protobuf '("extensions" "to" "reserved"))
(c-lang-defconst c-typeless-decl-kwds
protobuf '("extend" "rpc" "option" "returns"))
;; Here we remove default syntax for loops, if-statements and other C
;; syntactic features that are not supported by the protocol buffer language.
(c-lang-defconst c-brace-list-decl-kwds
;; Remove syntax for C-style enumerations.
protobuf nil)
(c-lang-defconst c-block-stmt-1-kwds
;; Remove syntax for "do" and "else" keywords.
protobuf nil)
(c-lang-defconst c-block-stmt-2-kwds
;; Remove syntax for "for", "if", "switch" and "while" keywords.
protobuf nil)
(c-lang-defconst c-simple-stmt-kwds
;; Remove syntax for "break", "continue", "goto" and "return" keywords.
protobuf nil)
(c-lang-defconst c-paren-stmt-kwds
;; Remove special case for the "(;;)" in for-loops.
protobuf nil)
(c-lang-defconst c-label-kwds
;; Remove case label syntax for the "case" and "default" keywords.
protobuf nil)
(c-lang-defconst c-before-label-kwds
;; Remove special case for the label in a goto statement.
protobuf nil)
(c-lang-defconst c-cpp-matchers
;; Disable all the C preprocessor syntax.
protobuf nil)
(c-lang-defconst c-decl-prefix-re
;; Same as for C, except it does not match "(". This is needed for disabling
;; the syntax for casts.
protobuf "\\([\{\};,]+\\)")
;; Add support for variable levels of syntax highlighting.
(defconst protobuf-font-lock-keywords-1 (c-lang-const c-matchers-1 protobuf)
"Minimal highlighting for protobuf-mode.")
(defconst protobuf-font-lock-keywords-2 (c-lang-const c-matchers-2 protobuf)
"Fast normal highlighting for protobuf-mode.")
(defconst protobuf-font-lock-keywords-3 (c-lang-const c-matchers-3 protobuf)
"Accurate normal highlighting for protobuf-mode.")
(defvar protobuf-font-lock-keywords protobuf-font-lock-keywords-3
"Default expressions to highlight in protobuf-mode.")
;; Our syntax table is auto-generated from the keyword classes we defined
;; previously with the `c-lang-const' macro.
(defvar protobuf-mode-syntax-table nil
"Syntax table used in protobuf-mode buffers.")
(or protobuf-mode-syntax-table
(setq protobuf-mode-syntax-table
(funcall (c-lang-const c-make-mode-syntax-table protobuf))))
(defvar protobuf-mode-abbrev-table nil
"Abbreviation table used in protobuf-mode buffers.")
(defvar protobuf-mode-map nil
"Keymap used in protobuf-mode buffers.")
(or protobuf-mode-map
(setq protobuf-mode-map (c-make-inherited-keymap)))
(easy-menu-define protobuf-menu protobuf-mode-map
"Protocol Buffers Mode Commands"
(cons "Protocol Buffers" (c-lang-const c-mode-menu protobuf)))
;;;###autoload (add-to-list 'auto-mode-alist '("\\.proto\\'" . protobuf-mode))
;;;###autoload
(defun protobuf-mode ()
"Major mode for editing Protocol Buffers description language.
The hook `c-mode-common-hook' is run with no argument at mode
initialization, then `protobuf-mode-hook'.
Key bindings:
\\{protobuf-mode-map}"
(interactive)
(kill-all-local-variables)
(set-syntax-table protobuf-mode-syntax-table)
(setq major-mode 'protobuf-mode
mode-name "Protocol-Buffers"
local-abbrev-table protobuf-mode-abbrev-table
abbrev-mode t)
(use-local-map protobuf-mode-map)
(c-initialize-cc-mode t)
(if (fboundp 'c-make-emacs-variables-local)
(c-make-emacs-variables-local))
(c-init-language-vars protobuf-mode)
(c-common-init 'protobuf-mode)
(easy-menu-add protobuf-menu)
(c-run-mode-hooks 'c-mode-common-hook 'protobuf-mode-hook)
(c-update-modeline))
(provide 'protobuf-mode)
;;; protobuf-mode.el ends here

50
tool/hash/crctab.c Normal file
View file

@ -0,0 +1,50 @@
/*-*- 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/bits/bits.h"
#include "libc/conv/conv.h"
#include "libc/macros.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* @fileoverview CRC Lookup Table Generator
* @see http://reveng.sourceforge.net/crc-catalogue/17plus.htm
*/
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s POLYNOMIAL\n", argv[0]);
exit(1);
}
static uint32_t tab[256];
crc32init(tab, strtoimax(argv[1], NULL, 0));
for (unsigned i = 0; i < ARRAYLEN(tab); ++i) {
if (i > 0) {
printf(",");
if (i % 6 == 0) {
printf("\n");
}
}
printf("0x%08x", tab[i]);
}
printf("\n");
return 0;
}

46
tool/hash/hash.mk Normal file
View file

@ -0,0 +1,46 @@
#-*-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_HASH
TOOL_HASH_SRCS := $(wildcard tool/hash/*.c)
TOOL_HASH_COMS = $(TOOL_HASH_OBJS:%.o=%.com)
TOOL_HASH_OBJS = \
$(TOOL_HASH_SRCS:%=o/$(MODE)/%.zip.o) \
$(TOOL_HASH_SRCS:%.c=o/$(MODE)/%.o)
TOOL_HASH_BINS = \
$(TOOL_HASH_COMS) \
$(TOOL_HASH_COMS:%=%.dbg)
TOOL_HASH_DIRECTDEPS = \
LIBC_CONV \
LIBC_FMT \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STUBS \
LIBC_STDIO \
LIBC_STR
TOOL_HASH_DEPS := \
$(call uniq,$(foreach x,$(TOOL_HASH_DIRECTDEPS),$($(x))))
o/$(MODE)/tool/hash/hash.pkg: \
$(TOOL_HASH_OBJS) \
$(foreach x,$(TOOL_HASH_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/tool/hash/%.com.dbg: \
$(TOOL_HASH_DEPS) \
o/$(MODE)/tool/hash/%.o \
o/$(MODE)/tool/hash/hash.pkg \
$(CRT) \
$(APE)
@$(APELINK)
$(TOOL_HASH_OBJS): \
$(BUILD_FILES) \
tool/hash/hash.mk
.PHONY: o/$(MODE)/tool/hash
o/$(MODE)/tool/hash: $(TOOL_HASH_BINS) $(TOOL_HASH_CHECKS)

78
tool/net/dig.c Normal file
View file

@ -0,0 +1,78 @@
/*-*- 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/dns/dns.h"
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ai.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/sock.h"
#include "tool/decode/lib/flagger.h"
#include "tool/decode/lib/idname.h"
#include "tool/decode/lib/socknames.h"
void lookup(const char *name) {
struct addrinfo hints = (struct addrinfo){.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
.ai_flags = AI_NUMERICSERV};
struct addrinfo *addrs = NULL;
if (getaddrinfo(name, "80", &hints, &addrs) == -1) {
perror("getaddrinfo");
exit(1);
}
if (addrs) {
for (struct addrinfo *addr = addrs; addr; addr = addr->ai_next) {
const unsigned char *ip =
addr->ai_family == AF_INET
? (const unsigned char *)&((struct sockaddr_in *)addr->ai_addr)
->sin_addr
: (const unsigned char *)"\0\0\0\0";
printf("%s = %s\n", "ai_flags",
recreateflags(kAddrInfoFlagNames, addr->ai_flags), addr->ai_flags);
printf("%s = %s (%d)\n", "ai_family",
findnamebyid(kAddressFamilyNames, addr->ai_family),
addr->ai_family);
printf("%s = %s (%d)\n", "ai_socktype",
findnamebyid(kSockTypeNames, addr->ai_socktype),
addr->ai_socktype);
printf("%s = %s (%d)\n", "ai_protocol",
findnamebyid(kProtocolNames, addr->ai_protocol),
addr->ai_protocol);
printf("%s = %d\n", "ai_addrlen", addr->ai_addrlen);
printf("%s = %hhu.%hhu.%hhu.%hhu\n", "ai_addr", ip[0], ip[1], ip[2],
ip[3]);
printf("%s = %s\n", "ai_canonname", addr->ai_canonname);
}
freeaddrinfo(addrs);
} else {
fprintf(stderr, "%s: %s\n", name, "no results");
}
}
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; ++i) {
lookup(argv[i]);
}
return 0;
}

300
tool/net/echoserver.c Normal file
View file

@ -0,0 +1,300 @@
/*-*- 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/calls/calls.h"
#include "libc/calls/struct/iovec.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/interruptiblecall.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/msg.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/sol.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
/**
* @fileoverview Asynchronous TCP/UDP Echo Server.
*
* make -j8 o/default/tool/net/echoserver.com
* o/default/tool/net/echoserver.com udp:0.0.0.0:7 tcp:0.0.0.0:7
*/
enum SocketKind {
kSocketServer,
kSocketClient,
};
struct Message {
struct iovec data;
struct sockaddr_in dest;
uint32_t destsize;
};
struct Messages {
size_t i, n;
struct Message *p;
};
struct Socket {
int64_t fd;
enum SocketKind kind;
int type;
int protocol;
struct sockaddr_in addr;
struct Messages egress; /* LIFO */
};
struct Sockets {
size_t i, n;
struct Socket *p;
};
struct Polls {
size_t i, n;
struct pollfd *p;
};
struct Sockets g_sockets;
struct Polls g_polls;
nodiscard char *DescribeAddress(struct sockaddr_in *addr) {
char ip4buf[16];
return xasprintf("%s:%hu",
inet_ntop(addr->sin_family, &addr->sin_addr.s_addr, ip4buf,
sizeof(ip4buf)),
ntohs(addr->sin_port));
}
nodiscard char *DescribeSocket(struct Socket *s) {
return xasprintf("%s:%s", s->protocol == IPPROTO_UDP ? "udp" : "tcp",
gc(DescribeAddress(&s->addr)));
}
noreturn void ShowUsageAndExit(bool iserror) {
FILE *f = iserror ? stderr : stdout;
int rc = iserror ? EXIT_FAILURE : EXIT_SUCCESS;
fprintf(f, "%s: %s %s\n", "Usage", g_argv[0], "PROTOCOL:ADDR:PORT...");
exit(rc);
}
void GetFlags(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "h")) != -1) {
switch (opt) {
case 'h':
ShowUsageAndExit(false);
default:
ShowUsageAndExit(true);
}
}
if (optind == argc) ShowUsageAndExit(true);
}
void AddSocket(const struct Socket *s) {
struct pollfd pfd;
pfd.fd = s->fd;
pfd.events = POLLIN;
pfd.revents = 0;
CHECK_NE(-1L, append(&g_sockets, s));
CHECK_NE(-1L, append(&g_polls, &pfd));
}
void RemoveSocket(size_t i) {
DCHECK_LT(i, g_sockets.i);
LOGF("removing: %s", gc(DescribeSocket(&g_sockets.p[i])));
CHECK_NE(-1, close(g_sockets.p[i].fd));
while (g_sockets.p[i].egress.i) {
free(g_sockets.p[i].egress.p[g_sockets.p[i].egress.i - 1].data.iov_base);
}
memcpy(&g_sockets.p[i], &g_sockets.p[i + 1],
(intptr_t)&g_sockets.p[g_sockets.i] - (intptr_t)&g_sockets.p[i + 1]);
memcpy(&g_polls.p[i], &g_polls.p[i + 1],
(intptr_t)&g_polls.p[g_polls.i] - (intptr_t)&g_polls.p[i + 1]);
g_sockets.i--;
g_polls.i--;
}
void GetListeningAddressesFromCommandLine(int argc, char *argv[]) {
int i;
for (i = optind; i < argc; ++i) {
struct Socket server;
memset(&server, 0, sizeof(server));
char scheme[4];
unsigned char *ip4 = (unsigned char *)&server.addr.sin_addr.s_addr;
uint16_t port;
if (sscanf(argv[i], "%3s:%hhu.%hhu.%hhu.%hhu:%hu", scheme, &ip4[0], &ip4[1],
&ip4[2], &ip4[3], &port) != 6) {
fprintf(stderr, "error: bad ip4 uri\n");
ShowUsageAndExit(true);
}
server.fd = -1;
server.kind = kSocketServer;
server.addr.sin_family = AF_INET;
server.addr.sin_port = htons(port);
if (strcasecmp(scheme, "tcp") == 0) {
server.type = SOCK_STREAM;
server.protocol = IPPROTO_TCP;
} else if (strcasecmp(scheme, "udp") == 0) {
server.type = SOCK_DGRAM;
server.protocol = IPPROTO_UDP;
} else {
fprintf(stderr, "%s: %s\n", "error", "bad scheme (should be tcp or udp)");
ShowUsageAndExit(true);
}
AddSocket(&server);
}
}
void BeginListeningForIncomingTraffic(void) {
size_t i;
for (i = 0; i < g_sockets.i; ++i) {
int yes = 1;
struct Socket *s = &g_sockets.p[i];
CHECK_NE(-1L,
(g_polls.p[i].fd = s->fd = socket(
s->addr.sin_family, s->type | SOCK_NONBLOCK, s->protocol)));
CHECK_NE(-1L,
setsockopt(s->fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)));
CHECK_NE(-1, bind(s->fd, &s->addr, sizeof(s->addr)));
if (s->protocol == IPPROTO_TCP) {
CHECK_NE(-1, listen(s->fd, 1));
}
uint32_t addrsize = sizeof(s->addr);
CHECK_NE(-1, getsockname(s->fd, &s->addr, &addrsize));
LOGF("listening on %s", gc(DescribeSocket(s)));
}
}
void AcceptConnection(size_t i) {
struct Socket *server = &g_sockets.p[i];
struct Socket client;
memset(&client, 0, sizeof(client));
client.kind = kSocketClient;
client.type = server->type;
client.protocol = server->protocol;
uint32_t addrsize = sizeof(client.addr);
CHECK_NE(-1L, (client.fd = accept4(server->fd, &client.addr, &addrsize,
SOCK_NONBLOCK)));
LOGF("%s accepted %s", gc(DescribeSocket(server)),
gc(DescribeSocket(&client)));
AddSocket(&client);
}
bool ReceiveData(size_t i) {
ssize_t got;
struct Message msg;
bool isudp = g_sockets.p[i].protocol == IPPROTO_UDP;
memset(&msg, 0, sizeof(msg));
msg.destsize = sizeof(msg.dest);
msg.data.iov_len = PAGESIZE;
msg.data.iov_base = xmalloc(msg.data.iov_len);
CHECK_NE(-1L, (got = recvfrom(g_sockets.p[i].fd, msg.data.iov_base,
msg.data.iov_len, 0, isudp ? &msg.dest : NULL,
isudp ? &msg.destsize : NULL)));
if (0 < got && got <= msg.data.iov_len) {
LOGF("%s received %lu bytes from %s", gc(DescribeSocket(&g_sockets.p[i])),
got, gc(DescribeAddress(&msg.dest)));
msg.data.iov_base = xrealloc(msg.data.iov_base, (msg.data.iov_len = got));
append(&g_sockets.p[i].egress, &msg);
g_polls.p[i].events |= POLLOUT;
return true;
} else {
RemoveSocket(i);
free_s(&msg.data.iov_base);
return false;
}
}
void SendData(size_t i) {
ssize_t sent;
struct Socket *s = &g_sockets.p[i];
struct Message *msg = &s->egress.p[s->egress.i - 1];
bool isudp = s->protocol == IPPROTO_UDP;
DCHECK(s->egress.i);
CHECK_NE(-1L, (sent = sendto(s->fd, msg->data.iov_base, msg->data.iov_len, 0,
isudp ? &msg->dest : NULL,
isudp ? msg->destsize : 0)));
LOGF("%s sent %lu bytes to %s", gc(DescribeSocket(s)), msg->data.iov_len,
gc(DescribeAddress(&msg->dest)));
if (!(msg->data.iov_len -= min((size_t)sent, (size_t)msg->data.iov_len))) {
free_s(&msg->data.iov_base);
if (!--s->egress.i) {
g_polls.p[i].events &= ~POLLOUT;
}
}
}
void HandleSomeNetworkTraffic(void) {
size_t i;
int eventcount;
CHECK_GE((eventcount = poll(g_polls.p, g_polls.i, -1)), 0);
for (i = 0; eventcount && i < g_sockets.i; ++i) {
if (!g_polls.p[i].revents) continue;
--eventcount;
if (g_polls.p[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
CHECK_EQ(kSocketClient, g_sockets.p[i].kind);
RemoveSocket(i);
} else {
if (g_polls.p[i].revents & POLLIN) {
if (g_sockets.p[i].kind == kSocketServer &&
g_sockets.p[i].protocol == IPPROTO_TCP) {
AcceptConnection(i);
} else {
if (!ReceiveData(i)) continue;
}
}
if (g_polls.p[i].revents & POLLOUT) {
SendData(i);
}
}
}
}
void EchoServer(void) {
for (;;) HandleSomeNetworkTraffic();
}
int main(int argc, char *argv[]) {
STATIC_YOINK("isatty");
GetFlags(argc, argv);
GetListeningAddressesFromCommandLine(argc, argv);
BeginListeningForIncomingTraffic();
struct InterruptibleCall icall;
memset(&icall, 0, sizeof(icall));
interruptiblecall(&icall, (void *)EchoServer, 0, 0, 0, 0);
fputc('\r', stderr);
LOGF("%s", "shutting down...");
size_t i;
for (i = g_sockets.i; i; --i) RemoveSocket(i - 1);
return 0;
}

56
tool/net/net.mk Normal file
View file

@ -0,0 +1,56 @@
#-*-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_NET
TOOL_NET_FILES := $(wildcard tool/net/*)
TOOL_NET_SRCS = $(filter %.c,$(TOOL_NET_FILES))
TOOL_NET_HDRS = $(filter %.h,$(TOOL_NET_FILES))
TOOL_NET_COMS = $(TOOL_NET_OBJS:%.o=%.com)
TOOL_NET_OBJS = \
$(TOOL_NET_SRCS:%=o/$(MODE)/%.zip.o) \
$(TOOL_NET_SRCS:%.c=o/$(MODE)/%.o)
TOOL_NET_BINS = \
$(TOOL_NET_COMS) \
$(TOOL_NET_COMS:%=%.dbg)
TOOL_NET_DIRECTDEPS = \
LIBC_CALLS \
LIBC_DNS \
LIBC_FMT \
LIBC_LOG \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_UNICODE \
LIBC_X \
THIRD_PARTY_GETOPT \
TOOL_DECODE_LIB
TOOL_NET_DEPS := \
$(call uniq,$(foreach x,$(TOOL_NET_DIRECTDEPS),$($(x))))
o/$(MODE)/tool/net/net.pkg: \
$(TOOL_NET_OBJS) \
$(foreach x,$(TOOL_NET_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/tool/net/%.com.dbg: \
$(TOOL_NET_DEPS) \
o/$(MODE)/tool/net/%.o \
o/$(MODE)/tool/net/net.pkg \
$(CRT) \
$(APE)
@$(APELINK)
$(TOOL_NET_OBJS): \
$(BUILD_FILES) \
tool/net/net.mk
.PHONY: o/$(MODE)/tool/net
o/$(MODE)/tool/net: $(TOOL_NET_BINS) $(TOOL_NET_CHECKS)

230
tool/scripts/asmexpr Executable file
View file

@ -0,0 +1,230 @@
#!/bin/sh
#
# OVERVIEW
#
# Micro-experiment assembler.
#
# EXAMPLES
#
# asmexpr 'mov $-4096,%rax' 'not %rax' 'bsr %rax,%rax'
# asmexpr 'mov $0,%ecx' 'vmovd %ecx,%xmm1' 'vpbroadcastb %xmm1,%ymm1' 'mov $0x20202032489001ff,%rax' 'vmovq %rax,%xmm0' 'vpcmpgtb %ymm1,%ymm0,%ymm2'
c=/tmp/asmexpr.c
s1=/tmp/asmexpr1.s
s2=/tmp/asmexpr2.s
s3=/tmp/asmexpr3.s
x=/tmp/asmexpr.exe
cat <<EOF >$s1
.comm rsp,8
.globl funk
funk: push %rbp
mov %rsp,%rbp
push %rbx
push %r12
push %r13
push %r14
push %r15
mov %rsp,rsp(%rip)
xor %eax,%eax
xor %ebx,%ebx
xor %ecx,%ecx
xor %edx,%edx
xor %edi,%edi
xor %esi,%esi
xor %r8d,%r8d
xor %r9d,%r9d
xor %r10d,%r10d
xor %r11d,%r11d
xor %r12d,%r12d
xor %r13d,%r13d
xor %r14d,%r14d
xor %r15d,%r15d
vzeroall
nop
nop
nop
nop
nop
nop
EOF
cat <<EOF >$s2
.comm a,8
.comm b,8
.comm c,8
.comm x,8
.comm y,8
.comm z,8
EOF
for i; do
cat <<EOF >>$s2
$i
EOF
done
cat <<EOF >$s3
.comm rsp,8
.comm regs,14*8
.comm flags,4
nop
nop
nop
nop
nop
nop
cld
mov rsp(%rip),%rsp
push %rbx
lea regs(%rip),%rbx
mov %rax,0(%rbx)
pop %rax
mov %rax,8(%rbx)
mov %rcx,16(%rbx)
mov %rdx,24(%rbx)
mov %rdi,32(%rbx)
mov %rsi,40(%rbx)
mov %r8,48(%rbx)
mov %r9,56(%rbx)
mov %r10,64(%rbx)
mov %r11,72(%rbx)
mov %r12,80(%rbx)
mov %r13,88(%rbx)
mov %r14,96(%rbx)
mov %r15,104(%rbx)
vmovaps %ymm0,0x0a0(%rbx)
vmovaps %ymm1,0x0c0(%rbx)
vmovaps %ymm2,0x0e0(%rbx)
vmovaps %ymm3,0x100(%rbx)
vmovaps %ymm4,0x120(%rbx)
vmovaps %ymm5,0x140(%rbx)
vmovaps %ymm6,0x160(%rbx)
vmovaps %ymm7,0x180(%rbx)
vmovaps %ymm8,0x1a0(%rbx)
vmovaps %ymm9,0x1c0(%rbx)
vmovaps %ymm10,0x1e0(%rbx)
vmovaps %ymm11,0x200(%rbx)
vmovaps %ymm12,0x220(%rbx)
vmovaps %ymm13,0x240(%rbx)
vmovaps %ymm14,0x260(%rbx)
vmovaps %ymm15,0x280(%rbx)
pushf
pop %rax
mov %eax,flags(%rip)
pop %r15
pop %r14
pop %r13
pop %r12
pop %rbx
pop %rbp
vzeroupper
ret
EOF
cat <<EOF >$c
#include <stdio.h>
#include <string.h>
struct GodHatesFlags {
unsigned c : 1; /* bit 0: carry flag */
unsigned v : 1; /* bit 1: V flag: was 8085 signed-number overflow */
unsigned p : 1; /* bit 2: parity flag */
unsigned r : 1; /* bit 3: always zero */
unsigned a : 1; /* bit 4: auxiliary flag (nibble carry) */
unsigned k : 1; /* bit 5: K is for Kompressor (K = V flag ⊕ sgn(result)) */
unsigned z : 1; /* bit 6: zero flag */
unsigned s : 1; /* bit 7: sign flag */
unsigned t : 1; /* bit 8: it's a trap flag */
unsigned i : 1; /* bit 9: interrupt enable flag */
unsigned d : 1; /* bit 10: direction flag */
unsigned o : 1; /* bit 11: overflow flag */
unsigned pl : 2; /* b12-13: i/o privilege level (80286+) */
unsigned nt : 1; /* bit 14: nested task flag (80286+) */
unsigned pc : 1; /* bit 15: oldskool flag */
unsigned blah : 16;
unsigned blah2 : 32;
};
char *DescribeFlags(struct GodHatesFlags flags) {
static char buf[256];
buf[0] = 0;
if (flags.c) strcat(buf, "CF ");
if (flags.p) strcat(buf, "PF ");
if (flags.a) strcat(buf, "AF ");
if (flags.z) strcat(buf, "ZF ");
if (flags.s) strcat(buf, "SF ");
if (flags.t) strcat(buf, "TF ");
if (flags.i) strcat(buf, "IF ");
if (flags.d) strcat(buf, "DF ");
if (flags.o) strcat(buf, "OF ");
strcat(buf, "IOPL-");
switch (flags.pl) {
case 0:
strcat(buf, "0");
break;
case 1:
strcat(buf, "1");
break;
case 2:
strcat(buf, "2");
break;
case 3:
strcat(buf, "3");
break;
default:
__builtin_unreachable();
}
strcat(buf, " ");
if (flags.nt) strcat(buf, "NT ");
if (flags.r || flags.k || flags.pc) {
strcat(buf, "[WOW: ");
if (flags.v) strcat(buf, "VF ");
if (flags.k) strcat(buf, "KF ");
if (flags.r) strcat(buf, "RF ");
if (flags.pc) strcat(buf, "PC ");
strcat(buf, "] ");
}
return &buf[0];
}
void funk();
struct GodHatesFlags flags;
struct {
long gen[14];
long __pad[6];
unsigned long ymms[16][4];
} regs;
static const char regnames[][4] = {"rax", "rbx", "rcx", "rdx", "rdi", "rsi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
int main() {
funk();
printf("flags %45s\n\n", DescribeFlags(flags));
for (unsigned i = 0; i < 14; ++i) {
if (regs.gen[i]) {
printf("%s s 0x%08x %20d\\n", regnames[i], (signed)(regs.gen[i]), (signed)(regs.gen[i]));
printf(" u 0x%08x %20u\\n", (unsigned)(regs.gen[i]), (unsigned)(regs.gen[i]));
printf(" sll 0x%016llx %20lld\\n", (signed long long)(regs.gen[i]), (signed long long)(regs.gen[i]));
printf(" ull 0x%016llx %20llu\\n", (unsigned long long)(regs.gen[i]), (unsigned long long)(regs.gen[i]));
printf("\n");
}
}
for (unsigned i = 0; i < 16; ++i) {
if (regs.ymms[i][0] || regs.ymms[i][1] || regs.ymms[i][2] || regs.ymms[i][3]) {
printf("ymm%d%s %016lx%016lx%016lx%016lx\\n", i, i < 10 ? " " : "", regs.ymms[i][3], regs.ymms[i][2], regs.ymms[i][1], regs.ymms[i][0]);
}
}
return 0;
}
EOF
cc -c -g -o $c.o $c &&
cc -c -g -o $s1.o $s1 &&
cc -c -g -o $s2.o $s2 &&
cc -c -g -o $s3.o $s3 &&
cc -g -o $x $c $s1.o $s2.o $s3.o && {
echo
objdump -d $s2.o | sed 1,7d
echo
$x
}
exit

View file

@ -0,0 +1,8 @@
#-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐
#───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘
for f; do
if [ $(nm $f | grep ' U ' | wc -l) -eq 0 ]; then
echo $f
fi
done

4
tool/scripts/ressurect-file Executable file
View file

@ -0,0 +1,4 @@
#-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐
#───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘
git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "${1:?FILE}"

12
tool/tool.mk Normal file
View file

@ -0,0 +1,12 @@
#-*-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───────────────────────┘
.PHONY: o/$(MODE)/tool
o/$(MODE)/tool: \
o/$(MODE)/tool/build \
o/$(MODE)/tool/debug \
o/$(MODE)/tool/decode \
o/$(MODE)/tool/hash \
o/$(MODE)/tool/net \
o/$(MODE)/tool/viz \
o/$(MODE)/tool/cc

121
tool/viz/ascii2utf8.c Normal file
View file

@ -0,0 +1,121 @@
/*-*- 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/errno.h"
#include "libc/stdio/stdio.h"
/**
@fileoverview Converts ASCII combining marks to UNICODE.
Getting started:
nroff -mandoc -rLL=72n -rLT=78n -Tutf8 <manpage.1 |
tool/viz/ascii2utf8.com
ASCII Bold:
- CHAR BACKSPACE CHAR c ++ "\b" ++ c
e.g. AABBCCDD 410841 420842 430843 440844
ASCII Underlines:
- CHAR BACKSPACE LOW LINE c ++ "\b_"
e.g. A_B_C_D_ 41085F 42085F 43085F 44085F
UNICODE Underlines:
- COMBINING LOW LINE U+0332 (\xCC\xB1)
e.g. A̲B̲C̲D̲ (ugly notches) 41CCB2 42CCB2 43CCB2 44CCB2
- COMBINING MACRON BELOW U+0331 (\xCC\xB1)
e.g. A̱ḆC̱Ḏ (too short) 41CCB1 42CCB1 43CCB1 44CCB1
- COMBINING DOUBLE MACRON BELOW U+035F (\xCD\x9F)
e.g. A͟B͟C͟D͟ (too long) 41CD9F 42CD9F 43CD9F 44CD9F
- DOUBLE PLUS COMBINING MACRON BELOW 3×U+035F + 1×U+0331
e.g. A͟B͟C͟Ḏ (too narrow) 41CCB1 42CCB1 43CCB1 44CD9F
- DOUBLE PLUS COMBINING MACRON LOW LINE [it's complicated]
e.g. A͟B͟C͟D̲ (𝑓𝑙𝑎𝑤𝑙𝑒𝑠𝑠) 41CD9F 42CD9F 43CD9F 44CCB2
*/
const wint_t kBackspace = '\b';
const wint_t kCombiningLowLine = L'\u0332';
const wint_t kCombiningDoubleMacronBelow = L'\u035f';
forceinline int PutChar(wint_t (*buf)[3], size_t *i, wint_t *cc, FILE *out) {
if (fputwc((*buf)[0], out) == -1) return -1;
if (*cc != -1) {
if (fputwc(*cc, out) == -1) return -1;
*cc = -1;
}
(*buf)[0] = (*buf)[1];
(*buf)[1] = (*buf)[2];
--*i;
return 0;
}
int CombineAsciiMarks(FILE *in, FILE *out) {
wint_t buf[3], wc = 0, cc = -1;
size_t i = 0;
for (;;) {
while (i < 3) {
if ((wc = fgetwc(in)) == -1) goto InputBreak;
buf[i++] = wc;
}
if (buf[1] == '\b' && cc == -1) {
if (buf[0] == buf[2]) { /* bold */
if (L'0' <= buf[0] && buf[0] <= L'9') {
buf[0] = L'𝟬' + (buf[0] - L'0');
i = 1;
} else if (L'A' <= buf[0] && buf[0] <= L'Z') {
buf[0] = L'𝐀' + (buf[0] - L'A');
i = 1;
} else if ('a' <= buf[0] && buf[0] <= L'z') {
buf[0] = L'𝗮' + (buf[0] - L'a');
i = 1;
} else {
i = 1;
}
} else if (buf[2] == '_') { /* underline */
cc = kCombiningLowLine;
i = 1;
} else if (buf[0] == '_') {
cc = kCombiningLowLine;
buf[0] = buf[2];
i = 1;
}
}
if (i == 3) {
if (PutChar(&buf, &i, &cc, out) == -1) goto OutputBreak;
}
}
InputBreak:
while (i) {
if (PutChar(&buf, &i, &cc, out) == -1) goto OutputBreak;
}
OutputBreak:
return (fclose(in) | fclose(out)) != -1 ? 0 : -1;
}
int main(int argc, char *argv[]) {
return CombineAsciiMarks(stdin, stdout) != -1 ? 0 : errno;
}

265
tool/viz/basicidea.c Normal file
View file

@ -0,0 +1,265 @@
/*-*- 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 "dsp/core/core.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/winsize.h"
#include "libc/dce.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/termios.h"
#include "libc/x/x.h"
#define SQR(X) ((X) * (X))
#define DIST(X, Y) ((X) - (Y))
static int want24bit_;
const int kXtermCube[] = {0, 0137, 0207, 0257, 0327, 0377};
static int rgbdist(int a, int b, int c, int x, int y, int z) {
return SQR(DIST(a, x)) + SQR(DIST(b, y)) + SQR(DIST(c, z));
}
static int uncube(int x) {
return x < 48 ? 0 : x < 115 ? 1 : (x - 35) / 40;
}
static int DivideIntRound(int x, int y) {
return (x + y / 2) / y;
}
static int XtermQuantizeLuma(int Y) {
return DivideIntRound(Y - 8, 10);
}
static int XtermDequantizeLuma(int qY) {
if (0 < qY && qY < 24) {
return (qY * 10) + 8;
} else if (qY > 0) {
return 255;
} else {
return 0;
}
}
static int XtermEncodeLuma(int qY) {
if (0 < qY && qY < 24) {
return qY + 232;
} else if (qY > 0) {
return 231;
} else {
return 16;
}
}
static int XtermQuantizeChroma(int c) {
return DivideIntRound(c - 55, 40);
}
static int XtermDequantizeChroma(int qc) {
if (0 < qc && qc < 6) {
return (qc * 40) + 55;
} else if (qc > 0) {
return 255;
} else {
return 0;
}
}
static int XtermEncodeChromaComponent(int qC) {
if (0 < qC && qC < 6) {
return qC;
} else if (qC > 0) {
return 5;
} else {
return 0;
}
}
static int XtermEncodeChroma(int qR, int qG, int qB) {
int xt;
xt = 16;
xt += XtermEncodeChromaComponent(qR) * 6 * 6;
xt += XtermEncodeChromaComponent(qG) * 6;
xt += XtermEncodeChromaComponent(qB) * 1;
return xt;
}
/**
* Quantizes 24-bit sRGB to xterm256 code range [16,256).
*/
static int rgb2xterm256(unsigned char R, unsigned char G, unsigned char B) {
double y, r, g, b, yr, yg, yb, ry, gy, by, gamma;
int Y, qY, cY, qRY, qGY, qBY, qR, qG, qB, cR, cG, cB, xt;
gamma = 2.4;
yr = 871024 / 4096299.;
yg = 8788810 / 12288897.;
yb = 887015 / 12288897.;
r = rgb2linpc(R / 255., gamma);
g = rgb2linpc(G / 255., gamma);
b = rgb2linpc(B / 255., gamma);
y = yr * r + yg * g + yb * b;
ry = (r - y) / (1 - yr + yg + yb);
gy = (g - y) / (1 - yg + yr + yb);
by = (b - y) / (1 - yb + yg + yr);
Y = round(rgb2stdpc(y, gamma) * 255);
qRY = round(rgb2stdpc(ry, gamma) * 6 + 3);
qGY = round(rgb2stdpc(gy, gamma) * 6 + 3);
qBY = round(rgb2stdpc(by, gamma) * 6 + 3);
qY = XtermQuantizeLuma(Y);
qR = XtermQuantizeChroma(qRY);
qG = XtermQuantizeChroma(qGY);
qB = XtermQuantizeChroma(qBY);
cY = XtermDequantizeLuma(qY);
cR = XtermDequantizeChroma(qRY);
cG = XtermDequantizeChroma(qGY);
cB = XtermDequantizeChroma(qBY);
#if 0
LOGF("RGB(%3d,%3d,%3d) rgb(%f,%f,%f) y=%f", R, G, B, r, g, b, y);
LOGF("RGB(%3d,%3d,%3d) yΔrgb(%f,%f,%f) XCUBE(%d,%d,%d)", R, G, B, ry, gy, by,
qRY, qGY, qBY);
LOGF("RGB(%3d,%3d,%3d) cRGB(%d,%d,%d) cY=%d qY=%d Y=%d", R, G, B, cR, cG, cB,
cY, qY, Y);
#endif
if (rgbdist(cR, cG, cB, R, G, B) <= rgbdist(cY, cY, cY, R, G, B)) {
xt = XtermEncodeChroma(qR, qG, qB);
} else {
xt = XtermEncodeLuma(qY);
}
/* LOGF("xt=%d", xt); */
return xt;
}
/**
* Prints raw packed 8-bit RGB data from memory.
*/
static void PrintImage(long yn, long xn, unsigned char RGB[yn][xn][4]) {
long y, x;
for (y = 0; y < yn; y += 2) {
if (y) printf("\r\n");
for (x = 0; x < xn; ++x) {
if (want24bit_) {
printf("\033[48;2;%hhu;%hhu;%hhu;38;2;%hhu;%hhu;%hhum▄",
RGB[y + 0][x][0], RGB[y + 0][x][1], RGB[y + 0][x][2],
RGB[y + 1][x][0], RGB[y + 1][x][1], RGB[y + 1][x][2]);
} else {
printf(
"\033[48;5;%hhu;38;5;%hhum▄",
rgb2xterm256(RGB[y + 0][x][0], RGB[y + 0][x][1], RGB[y + 0][x][2]),
rgb2xterm256(RGB[y + 1][x][0], RGB[y + 1][x][1], RGB[y + 1][x][2]));
}
}
}
if (IsWindows()) {
printf("\033[0m\r\n");
} else {
printf("\033[0m\r");
}
}
/**
* Determines dimensions of teletypewriter.
*/
static void GetTermSize(unsigned *out_rows, unsigned *out_cols) {
struct winsize ws;
ws.ws_row = 20;
ws.ws_col = 80;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
*out_rows = ws.ws_row;
*out_cols = ws.ws_col;
}
static void ReadAll(int fd, void *buf, size_t n) {
char *p;
ssize_t rc;
size_t got;
p = buf;
do {
CHECK_NE(-1, (rc = read(fd, p, n)));
got = rc;
CHECK(!(!got && n));
p += got;
n -= got;
} while (n);
}
static void LoadImageOrDie(const char *path, size_t size, long yn, long xn,
unsigned char RGB[yn][xn][4]) {
int pid, ws, fds[3];
const char *convert;
if (isempty((convert = getenv("CONVERT"))) &&
!(IsWindows() && access((convert = "\\msys64\\mingw64\\bin\\convert.exe"),
X_OK) != -1) &&
!(convert = commandv("convert"))) {
fputs("'convert' command not found\r\n"
"please install imagemagick\r\n",
stderr);
exit(1);
}
fds[0] = STDIN_FILENO;
fds[1] = -1;
fds[2] = STDERR_FILENO;
pid = spawnve(0, fds, convert,
(char *const[]){"convert", path, "-resize",
gc(xasprintf("%ux%u!", xn, yn)), "-colorspace",
"RGB", "-depth", "8", "rgba:-", NULL},
environ);
CHECK_NE(-1, pid);
ReadAll(fds[1], RGB, size);
CHECK_NE(-1, close(fds[1]));
CHECK_NE(-1, waitpid(pid, &ws, 0));
CHECK_EQ(0, WEXITSTATUS(ws));
}
int main(int argc, char *argv[]) {
int i;
void *rgb;
size_t size;
unsigned yn, xn;
cancolor();
GetTermSize(&yn, &xn);
yn *= 2;
size = yn * xn * 4;
CHECK_NOTNULL((rgb = valloc(size)));
for (i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-t") == 0) {
want24bit_ = 1;
} else {
LoadImageOrDie(argv[i], size, yn, xn, rgb);
PrintImage(yn, xn, rgb);
}
}
return 0;
}

44
tool/viz/bin2asm.c Normal file
View file

@ -0,0 +1,44 @@
/*-*- 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/stdio/stdio.h"
#include "libc/str/str.h"
#define COLS 8
int main(int argc, char *argv[]) {
int c, col = 0;
unsigned char ch;
char16_t glyphs[COLS + 1];
while ((c = getchar()) != -1) {
if (col == 0) {
printf("\t.byte\t");
memset(glyphs, 0, sizeof(glyphs));
}
ch = c & 0xff;
glyphs[col] = kCp437[ch];
if (col) putchar(',');
printf("0x%02x", ch);
if (++col == COLS) {
col = 0;
printf("\t#%hs\n", glyphs);
}
}
return 0;
}

93
tool/viz/bing.c Normal file
View file

@ -0,0 +1,93 @@
/*-*- 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/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "third_party/getopt/getopt.h"
/**
* @fileoverview Bing: Binary Glyphs.
* Intended for oldskool data science.
*/
int ispipe_;
int newlines_;
noreturn void ShowUsage(FILE *f, int rc) {
fputs(program_invocation_name, f);
fputs(": [-p] [-n] [FILE...]\n", f);
exit(rc);
}
void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "hpn")) != -1) {
switch (opt) {
case 'p':
ispipe_ = true;
break;
case 'n':
newlines_ = true;
break;
case 'h':
ShowUsage(stdout, EXIT_SUCCESS);
default:
ShowUsage(stderr, EX_USAGE);
}
}
}
void bing(FILE *f) {
int c, c2;
while ((c = fgetc(f)) != -1) {
c2 = c == '\n' && newlines_ ? '\n' : kCp437[c & 0xff];
if (ispipe_) {
fputc(c, stdout);
fputwc(c2, stderr);
} else {
fputwc(c2, stdout);
}
}
fputc('\n', ispipe_ ? stderr : stdout);
}
int main(int argc, char *argv[]) {
FILE *f;
size_t i;
GetOpts(argc, argv);
if (optind < argc) {
for (i = optind; i < argc; ++i) {
if (!(f = fopen(argv[i], "rb"))) {
perror(argv[i]);
exit(EXIT_FAILURE);
}
bing(f);
fclose(f);
}
} else {
bing(stdin);
}
return 0;
}

104
tool/viz/comma.c Normal file
View file

@ -0,0 +1,104 @@
/*-*- 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/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "third_party/getopt/getopt.h"
#define USAGE \
" [FLAGS] [PATH|FLEXDEC...] [<<<FLEXDEC...\\n...]\n\
Turns numbers into decimal with commas.\n\
\n\
Values are tokenized by spaces. Anything that isn't an integer is\n\
passed through. We parse integers the same way the C compiler does\n\
so 0x,0b,0,etc. prefixes are fine. Unicode spacing characters are\n\
supported.\n\
\n\
Flags:\n\
-o PATH output path\n\
-F STR\n\
-T STR sets field tokenization charset [default is whitespace]\n\
-w FLEXDEC\n\
-n FLEXDEC set fixed number of columns [default is variable, based\n\
on line breaks]\n\
-?\n\
-h shows this information\n\
\n"
static size_t linecap_;
static FILE *in_, *out_;
static const char16_t *fieldtoks_;
static char *inpath_, *outpath_, *line_;
void PrintUsage(int rc, FILE *f) {
fputs("Usage: ", f);
fputs(program_invocation_name, f);
fputs(USAGE, f);
exit(rc);
}
void GetOpts(int *argc, char *argv[]) {
int opt;
outpath_ = "-";
fieldtoks_ = u" \t\v\n\r\f  ";
while ((opt = getopt(*argc, argv, "?ho:F:T:w:n:")) != -1) {
switch (opt) {
case 'o':
outpath_ = optarg;
break;
case 'F':
case 'T':
break;
case '?':
case 'h':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
if (optind == *argc) {
argv[(*argc)++] = "-";
}
}
void ProcessFile(void) {
while ((getline(&line_, &linecap_, in_)) != -1) {
// TODO(jart)
}
}
int main(int argc, char *argv[]) {
size_t i;
GetOpts(&argc, argv);
CHECK_NOTNULL((out_ = fopen(outpath_, "w")));
for (i = optind; i < argc; ++i) {
CHECK_NOTNULL((in_ = fopen((inpath_ = argv[i]), "r")));
ProcessFile();
CHECK_NE(-1, fclose_s(&in_));
}
CHECK_NE(-1, fclose_s(&out_));
free(line_);
return 0;
}

322
tool/viz/cpuid.c Normal file
View file

@ -0,0 +1,322 @@
/*-*- 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/log/log.h"
#include "libc/nexgen32e/cpuid4.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/nexgen32e/rdtscp.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/nexgen32e/x86info.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "tool/decode/lib/idname.h"
#include "tool/decode/lib/x86idnames.h"
#define RED (cancolor() ? "\x1b[91m" : "")
#define GREEN (cancolor() ? "\x1b[32m" : "")
#define RESET (cancolor() ? "\x1b[0m" : "")
#define CANIUSE(FEATURE) caniuse(#FEATURE, X86_HAVE(FEATURE))
#define SHOW(CONSTANT) show(#CONSTANT, CONSTANT)
static void caniuse(const char *feature, bool present) {
printf("%-20s%s%s%s\n", feature, present ? GREEN : RED,
present ? "present" : "unavailable", RESET);
}
static void show(const char *constant, long value) {
printf("%-20s%#lx\n", constant, value);
}
static void showvendor(void) {
printf("%.*s%.*s%.*s", 4, &KCPUIDS(0H, EBX), 4, &KCPUIDS(0H, EDX), 4,
&KCPUIDS(0H, ECX));
}
static void showmodel(void) {
if (getx86processormodel(kX86ProcessorModelKey)) {
printf(" %s",
findnamebyid(kX86MarchNames,
getx86processormodel(kX86ProcessorModelKey)->march));
}
}
static void showspeed(void) {
if (KCPUIDS(16H, EAX)) {
printf(" %.1f%s", KCPUIDS(16H, EAX) / 1000.0, "ghz");
}
}
static void showstrata(void) {
if (getx86processormodel(kX86ProcessorModelKey)) {
printf(" (%s %s)",
findnamebyid(kX86GradeNames,
getx86processormodel(kX86ProcessorModelKey)->grade),
"Grade");
}
}
void showcachesizes(void) {
unsigned i;
CPUID4_ITERATE(i, {
printf("%-19s%s%s %u-way %,7u byte cache w/%s %,5u sets of %u byte lines "
"shared across %u threads\n",
gc(xasprintf("Level %u%s", CPUID4_CACHE_LEVEL,
CPUID4_CACHE_TYPE == 1
? " data"
: CPUID4_CACHE_TYPE == 2 ? " code" : "")),
CPUID4_IS_FULLY_ASSOCIATIVE ? " fully-associative" : "",
CPUID4_COMPLEX_INDEXING ? " complexly-indexed" : "",
CPUID4_WAYS_OF_ASSOCIATIVITY, CPUID4_CACHE_SIZE_IN_BYTES,
CPUID4_PHYSICAL_LINE_PARTITIONS > 1
? gc(xasprintf(" %u physically partitioned"))
: "",
CPUID4_NUMBER_OF_SETS, CPUID4_SYSTEM_COHERENCY_LINE_SIZE,
CPUID4_MAX_THREADS_SHARING_CACHE);
});
}
int main(int argc, char *argv[]) {
long tsc_aux;
showvendor();
showmodel();
showspeed();
showstrata();
printf("\n");
if (X86_HAVE(HYPERVISOR)) {
unsigned eax, ebx, ecx, edx;
asm("push\t%%rbx\n\t"
"cpuid\n\t"
"mov\t%%ebx,%1\n\t"
"pop\t%%rbx"
: "=a"(eax), "=rm"(ebx), "=c"(ecx), "=d"(edx)
: "0"(0x40000000), "2"(0));
printf("Running inside %.4s%.4s%.4s (eax=%#x)\n", &ebx, &ecx, &edx, eax);
}
printf("\n");
SHOW(kX86CpuStepping);
SHOW(kX86CpuModelid);
SHOW(kX86CpuFamilyid);
SHOW(kX86CpuType);
SHOW(kX86CpuExtmodelid);
SHOW(kX86CpuExtfamilyid);
printf("\n");
tsc_aux = rdpid();
show("TSC_AUX", tsc_aux);
show(" → core", TSC_AUX_CORE(tsc_aux));
show(" → node", TSC_AUX_NODE(tsc_aux));
printf("\n");
printf("Caches\n");
printf("──────\n");
showcachesizes();
printf("\n");
printf("Features\n");
printf("────────\n");
CANIUSE(ACC);
CANIUSE(ACPI);
CANIUSE(ADX);
CANIUSE(AES);
CANIUSE(APIC);
CANIUSE(ARCH_CAPABILITIES);
CANIUSE(AVX);
printf("%-20s%s%s%s%s\n", "AVX2", X86_HAVE(AVX2) ? GREEN : RED,
X86_HAVE(AVX2) ? "present" : "unavailable", RESET,
(!X86_HAVE(AVX2) && ({
unsigned eax, ebx, ecx, edx;
asm("push\t%%rbx\n\t"
"cpuid\n\t"
"mov\t%%ebx,%1\n\t"
"pop\t%%rbx"
: "=a"(eax), "=rm"(ebx), "=c"(ecx), "=d"(edx)
: "0"(7), "2"(0));
(void)eax;
(void)ecx;
(void)edx;
!!(ebx & (1u << 5));
}))
? " (disabled by operating system)"
: "");
CANIUSE(AVX512BW);
CANIUSE(AVX512CD);
CANIUSE(AVX512DQ);
CANIUSE(AVX512ER);
CANIUSE(AVX512F);
CANIUSE(AVX512IFMA);
CANIUSE(AVX512PF);
CANIUSE(AVX512VBMI);
CANIUSE(AVX512VL);
CANIUSE(AVX512_4FMAPS);
CANIUSE(AVX512_4VNNIW);
CANIUSE(AVX512_BF16);
CANIUSE(AVX512_BITALG);
CANIUSE(AVX512_VBMI2);
CANIUSE(AVX512_VNNI);
CANIUSE(AVX512_VP2INTERSECT);
CANIUSE(AVX512_VPOPCNTDQ);
CANIUSE(BMI);
CANIUSE(BMI2);
CANIUSE(CID);
CANIUSE(CLDEMOTE);
CANIUSE(CLFLUSH);
CANIUSE(CLFLUSHOPT);
CANIUSE(CLWB);
CANIUSE(CMOV);
CANIUSE(CQM);
CANIUSE(CX16);
CANIUSE(CX8);
CANIUSE(DCA);
CANIUSE(DE);
CANIUSE(DS);
CANIUSE(DSCPL);
CANIUSE(DTES64);
CANIUSE(ERMS);
CANIUSE(EST);
CANIUSE(F16C);
CANIUSE(FDP_EXCPTN_ONLY);
CANIUSE(FLUSH_L1D);
CANIUSE(FMA);
CANIUSE(FPU);
CANIUSE(FSGSBASE);
CANIUSE(FXSR);
CANIUSE(GBPAGES);
CANIUSE(GFNI);
CANIUSE(HLE);
CANIUSE(HT);
CANIUSE(HYPERVISOR);
CANIUSE(IA64);
CANIUSE(INTEL_PT);
CANIUSE(INTEL_STIBP);
CANIUSE(INVPCID);
CANIUSE(LA57);
CANIUSE(LM);
CANIUSE(MCA);
CANIUSE(MCE);
CANIUSE(MD_CLEAR);
CANIUSE(MMX);
CANIUSE(MOVBE);
CANIUSE(MOVDIR64B);
CANIUSE(MOVDIRI);
CANIUSE(MP);
CANIUSE(MPX);
CANIUSE(MSR);
CANIUSE(MTRR);
CANIUSE(MWAIT);
CANIUSE(NX);
CANIUSE(OSPKE);
CANIUSE(OSXSAVE);
CANIUSE(PAE);
CANIUSE(PAT);
CANIUSE(PBE);
CANIUSE(PCID);
CANIUSE(PCLMUL);
CANIUSE(PCONFIG);
CANIUSE(PDCM);
CANIUSE(PGE);
CANIUSE(PKU);
CANIUSE(PN);
CANIUSE(POPCNT);
CANIUSE(PSE);
CANIUSE(PSE36);
CANIUSE(RDPID);
CANIUSE(RDRND);
CANIUSE(RDSEED);
CANIUSE(RDTSCP);
CANIUSE(RDT_A);
CANIUSE(RTM);
CANIUSE(SDBG);
CANIUSE(SELFSNOOP);
CANIUSE(SEP);
CANIUSE(SHA);
CANIUSE(SMAP);
CANIUSE(SMEP);
CANIUSE(SMX);
CANIUSE(SPEC_CTRL);
CANIUSE(SPEC_CTRL_SSBD);
CANIUSE(SSE);
CANIUSE(SSE2);
CANIUSE(SSE3);
CANIUSE(SSE4_1);
CANIUSE(SSE4_2);
CANIUSE(SSSE3);
CANIUSE(SYSCALL);
CANIUSE(TM2);
CANIUSE(TME);
CANIUSE(TSC);
CANIUSE(TSC_ADJUST);
CANIUSE(TSC_DEADLINE_TIMER);
CANIUSE(TSX_FORCE_ABORT);
CANIUSE(UMIP);
CANIUSE(VAES);
CANIUSE(VME);
CANIUSE(VMX);
CANIUSE(VPCLMULQDQ);
CANIUSE(WAITPKG);
CANIUSE(X2APIC);
CANIUSE(XSAVE);
CANIUSE(XTPR);
CANIUSE(ZERO_FCS_FDS);
printf("\n");
printf("AMD Stuff\n");
printf("─────────\n");
CANIUSE(3DNOW);
CANIUSE(3DNOWEXT);
CANIUSE(3DNOWPREFETCH);
CANIUSE(ABM);
CANIUSE(BPEXT);
CANIUSE(CMP_LEGACY);
CANIUSE(CR8_LEGACY);
CANIUSE(EXTAPIC);
CANIUSE(FMA4);
CANIUSE(FXSR_OPT);
CANIUSE(IBS);
CANIUSE(LAHF_LM);
CANIUSE(LWP);
CANIUSE(MISALIGNSSE);
CANIUSE(MMXEXT);
CANIUSE(MWAITX);
CANIUSE(NODEID_MSR);
CANIUSE(OSVW);
CANIUSE(OVERFLOW_RECOV);
CANIUSE(PERFCTR_CORE);
CANIUSE(PERFCTR_LLC);
CANIUSE(PERFCTR_NB);
CANIUSE(PTSC);
CANIUSE(SKINIT);
CANIUSE(SMCA);
CANIUSE(SSE4A);
CANIUSE(SUCCOR);
CANIUSE(SVM);
CANIUSE(TBM);
CANIUSE(TCE);
CANIUSE(TOPOEXT);
CANIUSE(WDT);
CANIUSE(XOP);
return 0;
}

149
tool/viz/deathstar.c Normal file
View file

@ -0,0 +1,149 @@
#include "dsp/tty/tty.h"
#include "libc/calls/struct/termios.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/sig.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
/**
* @fileoverview demo code borrowed from Rosetta Code.
*/
#define FRAMERATE 23.976
struct Sphere {
double cx, cy, cz, r;
};
const char *kShades[] = {
"\e[48;5;232m ", "\e[48;5;233m ", "\e[48;5;234m ", "\e[48;5;235m ",
"\e[48;5;236m ", "\e[48;5;237m ", "\e[48;5;238m ", "\e[48;5;239m ",
"\e[48;5;240m ", "\e[48;5;241m ", "\e[48;5;242m ", "\e[48;5;243m ",
"\e[48;5;244m ", "\e[48;5;245m ", "\e[48;5;246m ", "\e[48;5;247m ",
"\e[48;5;248m ", "\e[48;5;249m ", "\e[48;5;250m ", "\e[48;5;251m ",
"\e[48;5;252m ", "\e[48;5;253m ", "\e[48;5;254m ", "\e[48;5;255m ",
};
jmp_buf jb_;
double light_[3] = {-50, 0, 50};
struct Sphere pos_ = {20, 20, 20, 20};
struct Sphere neg_ = {1, 1, -6, 20};
static void OnCtrlC(int sig) {
longjmp(jb_, 1);
}
static void Normalize(double v[3]) {
double len;
len = 1 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
v[0] *= len;
v[1] *= len;
v[2] *= len;
}
static double Dot(const double x[3], const double y[3]) {
return fabs(x[0] * y[0] + x[1] * y[1] + x[2] * y[2]);
}
/* check if a ray (x,y, -inf)->(x, y, inf) hits a sphere; if so, return
the intersecting z values. z1 is closer to the eye */
static int HitSphere(struct Sphere *s, double x, double y, double z[2]) {
double zsq;
x -= s->cx;
y -= s->cy;
zsq = s->r * s->r - (x * x + y * y);
if (zsq < 0) {
return 0;
} else {
zsq = sqrt(zsq);
z[0] = s->cz - zsq;
z[1] = s->cz + zsq;
return 1;
}
}
static void DrawSphere(double k, double ambient) {
int i, j, hit_result;
double x, y, vec[3], zb[2], zs[2];
for (i = floor(pos_.cy - pos_.r); i <= ceil(pos_.cy + pos_.r); i++) {
y = i + .5;
for (j = floor(pos_.cx - 2 * pos_.r); j <= ceil(pos_.cx + 2 * pos_.r);
j++) {
x = .5 * (j - pos_.cx) + .5 + pos_.cx;
if (!HitSphere(&pos_, x, y, zb)) {
/* ray lands in blank space, draw bg */
hit_result = 0;
} else if (!HitSphere(&neg_, x, y, zs)) {
/* ray hits pos_ sphere but not neg_, draw pos_ sphere surface */
hit_result = 1;
} else if (zs[0] > zb[0]) {
/* ray hits both, but pos_ front surface is closer */
hit_result = 1;
} else if (zs[1] > zb[1]) {
/* pos_ sphere surface is inside neg_ sphere, show bg */
hit_result = 0;
} else if (zs[1] > zb[0]) {
/* back surface on neg_ sphere is inside pos_ sphere,
the only place where neg_ sphere surface will be shown */
hit_result = 2;
} else {
hit_result = 1;
}
switch (hit_result) {
case 0:
fputs("\e[0m ", stdout);
continue;
case 1:
vec[0] = x - pos_.cx;
vec[1] = y - pos_.cy;
vec[2] = zb[0] - pos_.cz;
break;
default:
vec[0] = neg_.cx - x;
vec[1] = neg_.cy - y;
vec[2] = neg_.cz - zs[1];
break;
}
Normalize(vec);
fputs(
kShades[MIN(ARRAYLEN(kShades) - 1,
MAX(0, lround((1 - (pow(Dot(light_, vec), k) + ambient)) *
(ARRAYLEN(kShades) - 1))))],
stdout);
}
fputs("\e[0m\n", stdout);
}
fflush(stdout);
}
int main() {
double ang;
struct termios old;
if (cancolor()) {
ttyhidecursor(fileno(stdout));
if (!setjmp(jb_)) {
xsigaction(SIGINT, OnCtrlC, 0, 0, NULL);
ang = 0;
for (;;) {
printf("\e[H");
light_[1] = cos(ang * 2);
light_[2] = cos(ang);
light_[0] = sin(ang);
Normalize(light_);
ang += .05;
DrawSphere(1.5, .01);
usleep(1. / FRAMERATE * 1e6);
}
}
ttyshowcursor(fileno(stdout));
return 0;
} else {
return 1;
}
}

619
tool/viz/derasterize.c Normal file
View file

@ -0,0 +1,619 @@
/*-*- 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 "dsp/tty/itoa8.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/termios.h"
#include "libc/conv/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/str/tpenc.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/madv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/x/x.h"
#include "third_party/avir/lanczos.h"
#include "third_party/getopt/getopt.h"
#include "third_party/stb/stb_image.h"
#include "third_party/stb/stb_image_resize.h"
#define HELPTEXT \
"\n\
NAME\n\
\n\
derasterize - convert pictures to text using unicode ANSI art\n\
\n\
SYNOPSIS\n\
\n\
derasterize [FLAGS] [PNG|JPG|ETC]...\n\
\n\
DESCRIPTION\n\
\n\
This program converts pictures into unicode text and ANSI colors so\n\
that images can be displayed within a terminal. It performs lots of\n\
AVX2 optimized math to deliver the best quality on modern terminals\n\
with 24-bit color support, e.g. Kitty, Gnome Terminal, CMD.EXE, etc\n\
\n\
The default output if fullscreen but can be changed:\n\
-w X\n\
-x X\n\
If X is positive, hardcode the width in tty cells to X\n\
If X is negative, remove as much from the fullscreen width\n\
X may be specified as base 10 decimal, octal, binary, or hex\n\
-h Y\n\
-y Y\n\
If Y is positive, hardcode the height in tty cells to Y\n\
If Y is negative, remove as much from the fullscreen height\n\
May be specified as base 10 decimal, octal, binary, or hex\n\
-m\n\
Use ImageMagick `convert` command to load/scale graphics\n\
-?\n\
-H\n\
Show this help information\n\
\n\
EXAMPLES\n\
\n\
$ ./derasterize.com samples/wave.png > wave.uaart\n\
$ cat wave.uaart\n\
\n\
AUTHORS\n\
\n\
Csdvrx <csdvrx@outlook.com>\n\
Justine Tunney <jtunney@gmail.com>\n\
"
int m_; /* -m [use imagemagick] */
int x_; /* -x WIDTH [in flexidecimal] */
int y_; /* -y HEIGHT [in flexidecimal] */
#define BEST 0
#define FAST 1
#define FASTER 2
#define MODE BEST
#if MODE == BEST
#define MC 9u /* log2(#) of color combos to consider */
#define GN 35u /* # of glyphs to consider */
#elif MODE == FAST
#define MC 6u
#define GN 35u
#elif MODE == FASTER
#define MC 4u
#define GN 25u
#endif
#define CN 3u /* # channels (rgb) */
#define YS 8u /* row stride -or- block height */
#define XS 4u /* column stride -or- block width */
#define GT 44u /* total glyphs */
#define BN (YS * XS) /* # scalars in block/glyph plane */
#define PHIPRIME 0x9E3779B1u
extern const uint32_t kGlyphs[];
extern const char16_t kRunes[];
/*───────────────────────────────────────────────────────────────────────────│─╗
derasterize § encoding
*/
/**
* Formats Thompson-Pike variable length integer to array.
*
* @param p needs at least 8 bytes
* @return p + number of bytes written, cf. mempcpy
* @note no NUL-terminator is added
*/
static char *tptoa(char *p, wchar_t x) {
unsigned long w;
for (w = tpenc(x); w; w >>= 010) *p++ = w & 0xff;
return p;
}
/*───────────────────────────────────────────────────────────────────────────│─╗
derasterize § colors
*/
static float frgb2lin(float x) {
float r1, r2;
r1 = x / 12.92f;
r2 = pow((x + 0.055) / (1 + 0.055), 2.4);
return x < 0.04045f ? r1 : r2;
}
static float frgb2std(float x) {
float r1, r2;
r1 = x * 12.92f;
r2 = 1.055 * pow(x, 1 / 2.4) - 0.055;
return x < 0.0031308f ? r1 : r2;
}
/**
* Converts 8-bit RGB samples to floating point.
*/
static void rgb2float(unsigned n, float *f, const unsigned char *u) {
unsigned i;
for (i = 0; i < n; ++i) f[i] = u[i];
for (i = 0; i < n; ++i) f[i] /= 255;
}
/**
* Converts floating point RGB samples to 8-bit.
*/
static void float2rgb(unsigned n, unsigned char *u, float *f) {
unsigned i;
for (i = 0; i < n; ++i) f[i] *= 256;
for (i = 0; i < n; ++i) f[i] = roundf(f[i]);
for (i = 0; i < n; ++i) u[i] = MAX(0, MIN(255, f[i]));
}
/**
* Converts standard RGB to linear RGB.
*
* This makes subtraction look good by flattening out the bias curve
* that PC display manufacturers like to use.
*/
static noinline void rgb2lin(unsigned n, float *f, const unsigned char *u) {
unsigned i;
rgb2float(n, f, u);
for (i = 0; i < n; ++i) f[i] = frgb2lin(f[i]);
}
/**
* Converts linear RGB to standard RGB.
*/
static noinline void rgb2std(unsigned n, unsigned char *u, float *f) {
unsigned i;
for (i = 0; i < n; ++i) f[i] = frgb2std(f[i]);
float2rgb(n, u, f);
}
/*───────────────────────────────────────────────────────────────────────────│─╗
derasterize § blocks
*/
struct Cell {
char16_t rune;
unsigned char bg[CN], fg[CN];
};
/**
* Serializes ANSI background, foreground, and UNICODE glyph to wire.
*/
static char *celltoa(char *p, struct Cell cell) {
*p++ = 033;
*p++ = '[';
*p++ = '4';
*p++ = '8';
*p++ = ';';
*p++ = '2';
*p++ = ';';
p = itoa8(p, cell.bg[0]);
*p++ = ';';
p = itoa8(p, cell.bg[1]);
*p++ = ';';
p = itoa8(p, cell.bg[2]);
*p++ = ';';
*p++ = '3';
*p++ = '8';
*p++ = ';';
*p++ = '2';
*p++ = ';';
p = itoa8(p, cell.fg[0]);
*p++ = ';';
p = itoa8(p, cell.fg[1]);
*p++ = ';';
p = itoa8(p, cell.fg[2]);
*p++ = 'm';
p = tptoa(p, cell.rune);
return p;
}
/**
* Picks 2**MC unique (bg,fg) pairs from product of lb.
*/
static unsigned combinecolors(unsigned char bf[1u << MC][2],
const unsigned char bl[CN][YS * XS]) {
uint64_t hv, ht[(1u << MC) * 2];
unsigned i, j, n, b, f, h, hi, bu, fu;
memset(ht, 0, sizeof(ht));
for (n = b = 0; b < BN && n < (1u << MC); ++b) {
bu = bl[2][b] << 020 | bl[1][b] << 010 | bl[0][b];
hi = 0;
hi = (((bu >> 000) & 0xff) + hi) * PHIPRIME;
hi = (((bu >> 010) & 0xff) + hi) * PHIPRIME;
hi = (((bu >> 020) & 0xff) + hi) * PHIPRIME;
for (f = b + 1; f < BN && n < (1u << MC); ++f) {
fu = bl[2][f] << 020 | bl[1][f] << 010 | bl[0][f];
h = hi;
h = (((fu >> 000) & 0xff) + h) * PHIPRIME;
h = (((fu >> 010) & 0xff) + h) * PHIPRIME;
h = (((fu >> 020) & 0xff) + h) * PHIPRIME;
h = h & 0xffff;
h = MAX(1, h);
hv = 0;
hv <<= 030;
hv |= fu;
hv <<= 030;
hv |= bu;
hv <<= 020;
hv |= h;
for (i = 0;; ++i) {
j = (h + i * (i + 1) / 2) & (ARRAYLEN(ht) - 1);
if (!ht[j]) {
ht[j] = hv;
bf[n][0] = b;
bf[n][1] = f;
n++;
break;
} else if (ht[j] == hv) {
break;
}
}
}
}
return n;
}
/**
* Computes distance between synthetic block and actual.
*/
#define ADJUDICATE(SYMBOL, ARCH) \
ARCH static float SYMBOL(unsigned b, unsigned f, unsigned g, \
const float lb[CN][YS * XS]) { \
unsigned i, k, gu; \
float p[BN], q[BN], fu, bu, r; \
memset(q, 0, sizeof(q)); \
for (k = 0; k < CN; ++k) { \
gu = kGlyphs[g]; \
bu = lb[k][b]; \
fu = lb[k][f]; \
for (i = 0; i < BN; ++i) p[i] = (gu & (1u << i)) ? fu : bu; \
for (i = 0; i < BN; ++i) p[i] -= lb[k][i]; \
for (i = 0; i < BN; ++i) p[i] *= p[i]; \
for (i = 0; i < BN; ++i) q[i] += p[i]; \
} \
r = 0; \
for (i = 0; i < BN; ++i) q[i] = sqrtf(q[i]); \
for (i = 0; i < BN; ++i) r += q[i]; \
return r; \
}
ADJUDICATE(adjudicate$avx2, microarchitecture("avx2,fma"))
ADJUDICATE(adjudicate$avx, microarchitecture("avx"))
ADJUDICATE(adjudicate$default, )
static float (*adjudicate$hook)(unsigned, unsigned, unsigned,
const float[CN][YS * XS]);
static float adjudicate2(unsigned b, unsigned f, unsigned g,
const float lb[CN][YS * XS]) {
if (!adjudicate$hook) {
if (X86_HAVE(AVX2) && X86_HAVE(FMA)) {
adjudicate$hook = adjudicate$avx2;
} else if (X86_HAVE(AVX)) {
adjudicate$hook = adjudicate$avx;
} else {
adjudicate$hook = adjudicate$default;
}
}
return adjudicate$hook(b, f, g, lb);
}
static float adjudicate(unsigned b, unsigned f, unsigned g,
const float lb[CN][YS * XS]) {
unsigned i, k, gu;
float p[BN], q[BN], fu, bu, r;
memset(q, 0, sizeof(q));
for (k = 0; k < CN; ++k) {
gu = kGlyphs[g];
bu = lb[k][b];
fu = lb[k][f];
for (i = 0; i < BN; ++i) p[i] = (gu & (1u << i)) ? fu : bu;
for (i = 0; i < BN; ++i) p[i] -= lb[k][i];
for (i = 0; i < BN; ++i) p[i] *= p[i];
for (i = 0; i < BN; ++i) q[i] += p[i];
}
r = 0;
for (i = 0; i < BN; ++i) q[i] = sqrtf(q[i]);
for (i = 0; i < BN; ++i) r += q[i];
return r;
}
/**
* Converts tiny bitmap graphic into unicode glyph.
*/
static struct Cell derasterize(unsigned char block[CN][YS * XS]) {
struct Cell cell;
unsigned i, n, b, f, g;
float r, best, lb[CN][YS * XS];
unsigned char bf[1u << MC][2];
rgb2lin(CN * YS * XS, lb[0], block[0]);
n = combinecolors(bf, block);
best = -1u;
cell.rune = 0;
for (i = 0; i < n; ++i) {
b = bf[i][0];
f = bf[i][1];
for (g = 0; g < GN; ++g) {
r = adjudicate(b, f, g, lb);
if (r < best) {
best = r;
cell.rune = kRunes[g];
cell.bg[0] = block[0][b];
cell.bg[1] = block[1][b];
cell.bg[2] = block[2][b];
cell.fg[0] = block[0][f];
cell.fg[1] = block[1][f];
cell.fg[2] = block[2][f];
if (!r) return cell;
}
}
}
return cell;
}
/*───────────────────────────────────────────────────────────────────────────│─╗
derasterize § graphics
*/
/**
* Turns packed 8-bit RGB graphic into ANSI UNICODE text.
*/
static char *RenderImage(char *v, unsigned yn, unsigned xn,
const unsigned char srgb[yn][YS][xn][XS][CN]) {
unsigned y, x, i, j, k;
unsigned char copy[YS][XS][CN] aligned(32);
unsigned char block[CN][YS * XS] aligned(32);
DCHECK_ALIGNED(32, v);
DCHECK_ALIGNED(32, srgb);
for (y = 0; y < yn; ++y) {
if (y) {
*v++ = 033;
*v++ = '[';
*v++ = '0';
*v++ = 'm';
*v++ = '\n';
}
for (x = 0; x < xn; ++x) {
for (i = 0; i < YS; ++i) {
memcpy(copy[i], srgb[y][i][x], XS * CN);
}
for (i = 0; i < YS; ++i) {
for (j = 0; j < XS; ++j) {
for (k = 0; k < CN; ++k) {
block[k][i * XS + j] = copy[i][j][k];
}
}
}
v = celltoa(v, derasterize(block));
}
}
return v;
}
/*───────────────────────────────────────────────────────────────────────────│─╗
derasterize § systems
*/
static void PrintImage(unsigned yn, unsigned xn,
const unsigned char rgb[yn][YS][xn][XS][CN]) {
size_t size;
char *v, *vt;
size = yn * (xn * (32 + (2 + (1 + 3) * 3) * 2 + 1 + 3)) * 1 + 5 + 1;
size = ROUNDUP(size, FRAMESIZE);
CHECK_NE(MAP_FAILED, (vt = mapanon(size)));
v = RenderImage(vt, yn, xn, rgb);
*v++ = '\r';
*v++ = 033;
*v++ = '[';
*v++ = '0';
*v++ = 'm';
CHECK_NE(-1, xwrite(1, vt, v - vt));
CHECK_NE(-1, munmap(vt, size));
}
/**
* Determines dimensions of teletypewriter.
*/
static void GetTermSize(unsigned out_rows[1], unsigned out_cols[1]) {
struct winsize ws;
ws.ws_row = 24;
ws.ws_col = 80;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
}
out_rows[0] = ws.ws_row;
out_cols[0] = ws.ws_col;
}
static int ReadAll(int fd, void *data, size_t size) {
char *p;
ssize_t rc;
size_t got, n;
p = data;
n = size;
do {
if ((rc = read(fd, p, n)) == -1) return -1;
assert((got = rc) || !n);
p += got;
n -= got;
} while (n);
return 0;
}
/**
* Loads and scales image via ImageMagick `convert` command.
*
* @param path is filename of graphic
* @param yn is desired height
* @param xn is desired width
* @param rgb is memory allocated by caller for image
*/
static void LoadFileViaImageMagick(const char *path, unsigned yn, unsigned xn,
unsigned char rgb[yn][YS][xn][XS][CN]) {
const char *convert;
int pid, ws, fds[3] = {STDIN_FILENO, -1, STDERR_FILENO};
if (!(convert = commandv("convert"))) {
fputs("error: `convert` command not found\n"
"try: apt-get install imagemagick\n",
stderr);
exit(EXIT_FAILURE);
}
CHECK_NE(-1,
(pid = spawnve(
0, fds, convert,
(char *const[]){"convert", path, "-resize",
xasprintf("%ux%u!", xn * XS, yn * YS), "-depth",
"8", "-colorspace", "sRGB", "rgb:-", NULL},
environ)));
CHECK_NE(-1, ReadAll(fds[STDOUT_FILENO], rgb, yn * YS * xn * XS * CN));
CHECK_NE(-1, close(fds[STDOUT_FILENO]));
CHECK_NE(-1, waitpid(pid, &ws, 0));
CHECK_EQ(0, WEXITSTATUS(ws));
}
static void LoadFile(const char *path, size_t yn, size_t xn, void *rgb) {
struct stat st;
size_t data2size, data3size;
void *map, *data, *data2, *data3;
int fd, gotx, goty, channels_in_file;
CHECK_NE(-1, (fd = open(path, O_RDONLY)), "%s", path);
CHECK_NE(-1, fstat(fd, &st));
CHECK_GT(st.st_size, 0);
CHECK_LE(st.st_size, INT_MAX);
LOGIFNEG1(fadvise(fd, 0, 0, MADV_WILLNEED | MADV_SEQUENTIAL));
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)));
CHECK_NOTNULL((data = stbi_load_from_memory(map, st.st_size, &gotx, &goty,
&channels_in_file, CN)),
"%s", path);
CHECK_NE(-1, munmap(map, st.st_size));
CHECK_NE(-1, close(fd));
#if 1
stbir_resize_uint8(data, gotx, goty, 0, rgb, xn * XS, yn * YS, 0, CN);
#else
CHECK_EQ(CN, 3);
data2size = ROUNDUP(sizeof(float) * goty * gotx * CN, FRAMESIZE);
data3size = ROUNDUP(sizeof(float) * yn * YS * xn * XS * CN, FRAMESIZE);
CHECK_NE(MAP_FAILED, (data2 = mapanon(data2size)));
CHECK_NE(MAP_FAILED, (data3 = mapanon(data3size)));
rgb2lin(goty * gotx * CN, data2, data);
lanczos3(yn * YS, xn * XS, data3, goty, gotx, data2, gotx * 3);
rgb2std(yn * YS * xn * XS * CN, rgb, data3);
CHECK_NE(-1, munmap(data2, data2size));
CHECK_NE(-1, munmap(data3, data3size));
#endif
free(data);
}
static int ParseNumberOption(const char *arg) {
long x;
x = strtol(arg, NULL, 0);
if (!(1 <= x && x <= INT_MAX)) {
fprintf(stderr, "invalid flexidecimal: %s\n\n", arg);
exit(EXIT_FAILURE);
}
return x;
}
static void PrintUsage(int rc, FILE *f) {
fputs(HELPTEXT, f);
exit(rc);
}
static void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "?Hmx:y:w:h:")) != -1) {
switch (opt) {
case 'w':
case 'x':
x_ = ParseNumberOption(optarg);
break;
case 'h':
case 'y':
y_ = ParseNumberOption(optarg);
break;
case 'm':
m_ = 1;
break;
case '?':
case 'H':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
int main(int argc, char *argv[]) {
int i;
void *rgb;
size_t size;
char *option;
unsigned yd, xd;
__fast_math();
showcrashreports();
cancolor();
GetOpts(argc, argv);
// if sizes are given, 2 cases:
// - positive values: use that as the target size
// - negative values: add, for ex to offset the command prompt size
GetTermSize(&yd, &xd);
if (y_ <= 0) {
y_ += yd;
}
if (x_ <= 0) {
x_ += xd;
}
// FIXME: on the conversion stage should do 2Y because of halfblocks
// printf( "filename >%s<\tx >%d<\ty >%d<\n\n", filename, x_, y_);
size = y_ * YS * x_ * XS * CN;
CHECK_NE(MAP_FAILED, (rgb = mapanon(ROUNDUP(size, FRAMESIZE))));
for (i = optind; i < argc; ++i) {
if (!argv[i]) continue;
if (m_) {
LoadFileViaImageMagick(argv[i], y_, x_, rgb);
} else {
LoadFile(argv[i], y_, x_, rgb);
}
PrintImage(y_, x_, rgb);
}
munmap(rgb, ROUNDUP(size, FRAMESIZE));
return 0;
}

43
tool/viz/double2int.c Normal file
View file

@ -0,0 +1,43 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/conv/conv.h"
#include "libc/limits.h"
#include "libc/macros.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
#include "third_party/dtoa/dtoa.h"
void double2int(const char *s) {
double b64;
uint64_t u64;
b64 = strtod(s, NULL);
memcpy(&u64, &b64, 8);
printf("0x%016lx\n", u64);
}
int main(int argc, char *argv[]) {
int i;
for (i = 1; i < argc; ++i) {
double2int(argv[i]);
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show more