diff --git a/Makefile b/Makefile index 8068a608..bc14e259 100644 --- a/Makefile +++ b/Makefile @@ -71,13 +71,13 @@ GNUMAKEFLAGS += --output-sync .PHONY: all o bins check test depend tags all: o -o: o/$(MODE)/ape \ - o/$(MODE)/dsp \ - o/$(MODE)/net \ - o/$(MODE)/libc \ - o/$(MODE)/test \ - o/$(MODE)/tool \ - o/$(MODE)/examples \ +o: o/$(MODE)/ape \ + o/$(MODE)/dsp \ + o/$(MODE)/net \ + o/$(MODE)/libc \ + o/$(MODE)/test \ + o/$(MODE)/tool \ + o/$(MODE)/examples \ o/$(MODE)/third_party PKGS = @@ -137,8 +137,6 @@ include dsp/tty/tty.mk # ├──online include libc/dns/dns.mk # │ include libc/crypto/crypto.mk # │ include net/http/http.mk #─┘ -include third_party/chibicc/chibicc.mk -include third_party/chibicc/test/test.mk include third_party/lemon/lemon.mk include third_party/linenoise/linenoise.mk include third_party/editline/editline.mk @@ -155,6 +153,8 @@ include third_party/m4/m4.mk include third_party/lz4cli/lz4cli.mk include third_party/bzip2/bzip2.mk include tool/build/lib/buildlib.mk +include third_party/chibicc/chibicc.mk +include third_party/chibicc/test/test.mk include tool/build/emucrt/emucrt.mk include tool/build/emubin/emubin.mk include tool/build/build.mk diff --git a/libc/calls/getsid.c b/libc/calls/getsid.c new file mode 100644 index 00000000..ab83e843 --- /dev/null +++ b/libc/calls/getsid.c @@ -0,0 +1,28 @@ +/*-*- 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/internal.h" + +/** + * Creates session and sets the process group id. + */ +uint32_t getsid(int pid) { + return getsid$sysv(pid); +} diff --git a/libc/calls/internal.h b/libc/calls/internal.h index eb04a344..f52f75cb 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -180,8 +180,10 @@ i64 write$sysv(i32, const void *, u64) hidden; int getitimer$sysv(i32, struct itimerval *) hidden; int setresgid$sysv(uint32_t, uint32_t, uint32_t) hidden; int setresuid$sysv(uint32_t, uint32_t, uint32_t) hidden; +int setsid$sysv(void) hidden; u32 getgid$sysv(void) hidden; u32 getpid$sysv(void) hidden; +u32 getsid$sysv(int) hidden; u32 gettid$sysv(void) hidden; u32 getuid$sysv(void) hidden; void *mmap$sysv(void *, u64, u32, u32, i64, i64) hidden; diff --git a/libc/calls/setegid.S b/libc/calls/setegid.S index b0e40365..4730333a 100644 --- a/libc/calls/setegid.S +++ b/libc/calls/setegid.S @@ -26,6 +26,7 @@ / @see setgid(), getauxval(AT_SECURE) setegid:push %rbp mov %rsp,%rbp + .profilable mov %edi,%esi mov $-1,%edi call setregid diff --git a/libc/calls/seteuid.S b/libc/calls/seteuid.S index fb07c2f6..e9bad68c 100644 --- a/libc/calls/seteuid.S +++ b/libc/calls/seteuid.S @@ -26,6 +26,7 @@ / @see setuid(), getauxval(AT_SECURE) seteuid:push %rbp mov %rsp,%rbp + .profilable mov %edi,%esi mov $-1,%edi call setreuid diff --git a/libc/str/strncpy.thunk.S b/libc/calls/setsid.c similarity index 85% rename from libc/str/strncpy.thunk.S rename to libc/calls/setsid.c index 3fc23217..0d7508bd 100644 --- a/libc/str/strncpy.thunk.S +++ b/libc/calls/setsid.c @@ -1,5 +1,5 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- 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 │ │ │ @@ -17,8 +17,12 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" -__strncpy: - jmp strncpy - .endfn __strncpy,globl +/** + * Creates session and sets the process group id. + */ +int setsid(void) { + return setsid$sysv(); +} diff --git a/libc/calls/sigenter.S b/libc/calls/sigenter.S index c66d906f..ce702596 100644 --- a/libc/calls/sigenter.S +++ b/libc/calls/sigenter.S @@ -34,20 +34,15 @@ __sigenter: push %rbp mov %rsp,%rbp - push %rbx - push %rbx - ezlea _base,bx .profilable - ezlea g_sighandrvas,ax and $NSIG-1,%edi - mov (%rax,%rdi,4),%eax + mov g_sighandrvas(,%rdi,4),%eax cmp $kSigactionMinRva,%eax jl 2f - add %rbx,%rax + lea _base(%rax),%eax call *%rax mov $1,%eax -1: pop %rbx - leave +1: leave ret 2: xor %eax,%eax jmp 1b diff --git a/libc/integral/c.inc b/libc/integral/c.inc index 7a0520dc..1d4e55ba 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -920,6 +920,9 @@ typedef uint64_t uintmax_t; #pragma GCC diagnostic ignored /* tidy */ "-Wunused-but-set-variable" #pragma GCC diagnostic ignored /* tidy */ "-Wunused-but-set-parameter" #endif /* GCC6+ */ +#if __GNUC__ >= 8 +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif /* GCC8+ */ #if __GNUC__ + 0 >= 9 #pragma GCC diagnostic ignored /* "always true" breaks dce */ "-Waddress" #endif /* GCC9+ */ diff --git a/libc/runtime/valist.c b/libc/runtime/valist.c index 57f64511..18d924b4 100644 --- a/libc/runtime/valist.c +++ b/libc/runtime/valist.c @@ -17,16 +17,16 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.h" #include "libc/runtime/valist.h" -static void *__va_arg_mem(struct __va *ap, unsigned long sz, unsigned align) { - void *r = ap->overflow_arg_area; - if (align > 8) r = (void *)(((unsigned long)r + 15) / 16 * 16); - ap->overflow_arg_area = (void *)(((unsigned long)r + sz + 7) / 8 * 8); +static void *__va_arg_mem(struct __va *ap, size_t sz, size_t align) { + void *r = (void *)ROUNDUP((intptr_t)ap->overflow_arg_area, align); + ap->overflow_arg_area = (void *)ROUNDUP((intptr_t)r + sz, 8); return r; } -void *__va_arg(struct __va *ap, unsigned long sz, unsigned align, unsigned k) { +void *__va_arg(struct __va *ap, size_t sz, unsigned align, unsigned k) { void *r; switch (k) { case 0: diff --git a/libc/runtime/valist.h b/libc/runtime/valist.h index 28ffc9f5..def8a2dd 100644 --- a/libc/runtime/valist.h +++ b/libc/runtime/valist.h @@ -4,13 +4,13 @@ COSMOPOLITAN_C_START_ struct __va { - unsigned int gp_offset; - unsigned int fp_offset; + uint32_t gp_offset; + uint32_t fp_offset; void *overflow_arg_area; void *reg_save_area; }; -void *__va_arg(struct __va *, unsigned long, unsigned, unsigned); +void *__va_arg(struct __va *, size_t, unsigned, unsigned); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/str/str.h b/libc/str/str.h index 238e3b63..3085ff40 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -238,9 +238,6 @@ char *strsignal(int) returnsnonnull libcesque; ╚────────────────────────────────────────────────────────────────────────────│*/ #if defined(__GNUC__) && !defined(__STRICT_ANSI__) -char *__strncpy(char *, const char *, size_t) memcpyesque; -#define strncpy(DEST, SRC, N) __strncpy(DEST, SRC, N) /* pacify bad warning */ - #define explicit_bzero(STR, BYTES) \ do { \ void *Str; \ diff --git a/libc/str/strncpy.c b/libc/str/strncpy.c index 32c29cd0..91ee8457 100644 --- a/libc/str/strncpy.c +++ b/libc/str/strncpy.c @@ -33,10 +33,11 @@ * @see stpncpy(), memccpy() * @asyncsignalsafe */ -char *(strncpy)(char *dest, const char *src, size_t stride) { - char *p; - if ((p = memccpy(dest, src, '\0', stride))) { - memset(p, 0, dest + stride - p); +char *strncpy(char *dest, const char *src, size_t stride) { + size_t i; + for (i = 0; i < stride; ++i) { + if (!(dest[i] = src[i])) break; } + memset(dest + i, 0, stride - i); return dest; } diff --git a/libc/str/tolower.c b/libc/str/tolower.c index 8e71c6bd..38f44c82 100644 --- a/libc/str/tolower.c +++ b/libc/str/tolower.c @@ -19,6 +19,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" +/** + * Converts character to lower case. + */ int tolower(int c) { return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c; } diff --git a/libc/str/toupper.c b/libc/str/toupper.c index 3217f0bd..b91df86a 100644 --- a/libc/str/toupper.c +++ b/libc/str/toupper.c @@ -19,6 +19,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" +/** + * Converts character to upper case. + */ int toupper(int c) { return 'a' <= c && c <= 'z' ? c - ('a' - 'A') : c; } diff --git a/libc/str/towlower.c b/libc/str/towlower.c index 48bb7db7..71e1939e 100644 --- a/libc/str/towlower.c +++ b/libc/str/towlower.c @@ -19,6 +19,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" +/** + * Converts wide character to lower case. + */ wint_t towlower(wint_t wc) { return tolower(wc); } diff --git a/libc/str/towupper.c b/libc/str/towupper.c index 0d91e7c5..7e4eb03f 100644 --- a/libc/str/towupper.c +++ b/libc/str/towupper.c @@ -19,6 +19,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" +/** + * Converts wide character to upper case. + */ wint_t towupper(wint_t wc) { return toupper(wc); } diff --git a/libc/str/wcscasecmp.c b/libc/str/wcscasecmp.c index 91dace19..d1acac54 100644 --- a/libc/str/wcscasecmp.c +++ b/libc/str/wcscasecmp.c @@ -31,6 +31,6 @@ int wcscasecmp(const wchar_t *a, const wchar_t *b) { size_t i = 0; unsigned x, y; if (a == b) return 0; - while ((x = tolower(a[i])) == (y = tolower(b[i])) && b[i]) ++i; + while ((x = towlower(a[i])) == (y = towlower(b[i])) && b[i]) ++i; return x - y; } diff --git a/libc/sysv/calls/getsid-sysv.s b/libc/sysv/calls/getsid-sysv.s new file mode 100644 index 00000000..460ad3ea --- /dev/null +++ b/libc/sysv/calls/getsid-sysv.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.inc" +.scall getsid$sysv 0x00ff01362136007c globl hidden diff --git a/libc/sysv/calls/getsid.s b/libc/sysv/calls/getsid.s deleted file mode 100644 index 2c3340d0..00000000 --- a/libc/sysv/calls/getsid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.inc" -.scall getsid 0x00ff01362136007c globl diff --git a/libc/sysv/calls/setsid-sysv.s b/libc/sysv/calls/setsid-sysv.s new file mode 100644 index 00000000..43a59bc3 --- /dev/null +++ b/libc/sysv/calls/setsid-sysv.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.inc" +.scall setsid$sysv 0x0093009320930070 globl hidden diff --git a/libc/sysv/calls/setsid.s b/libc/sysv/calls/setsid.s deleted file mode 100644 index 6aff18ac..00000000 --- a/libc/sysv/calls/setsid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.inc" -.scall setsid 0x0093009320930070 globl diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 90fcf2fc..91fecbb6 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -145,8 +145,8 @@ scall 'getuid$sysv' 0x0018001820180066 globl hidden scall 'getgid$sysv' 0x002f002f202f0068 globl hidden scall 'getppid$sysv' 0x002700272027006e globl hidden scall getpgrp 0x005100512051006f globl -scall setsid 0x0093009320930070 globl -scall getsid 0x00ff01362136007c globl +scall 'setsid$sysv' 0x0093009320930070 globl hidden +scall 'getsid$sysv' 0x00ff01362136007c globl hidden scall getpgid 0x00cf00cf20970079 globl scall setpgid 0x005200522052006d globl scall geteuid 0x001900192019006b globl diff --git a/test/libc/str/strcpy_test.c b/test/libc/str/strcpy_test.c index 3772e267..f670b7d8 100644 --- a/test/libc/str/strcpy_test.c +++ b/test/libc/str/strcpy_test.c @@ -27,6 +27,32 @@ TEST(strcpy, test) { EXPECT_STREQ("hello there what's up", strcpy(buf, "hello there what's up")); } +TEST(strncpy, test) { + char b[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + strncpy(b, "key", 8); + ASSERT_EQ('k', b[0]); + ASSERT_EQ('e', b[1]); + ASSERT_EQ('y', b[2]); + ASSERT_EQ('\0', b[3]); + ASSERT_EQ('\0', b[4]); + ASSERT_EQ('\0', b[5]); + ASSERT_EQ('\0', b[6]); + ASSERT_EQ('\0', b[7]); +} + +TEST(strncpy, testSameLength_isNotNulTerminated) { + char b[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + strncpy(b, "keyword", 7); + ASSERT_EQ('k', b[0]); + ASSERT_EQ('e', b[1]); + ASSERT_EQ('y', b[2]); + ASSERT_EQ('w', b[3]); + ASSERT_EQ('o', b[4]); + ASSERT_EQ('r', b[5]); + ASSERT_EQ('d', b[6]); + ASSERT_EQ(7, b[7]); +} + BENCH(strcpy, bench) { extern char *strcpy_(char *, const char *) asm("strcpy"); static char buf[1024], buf2[1024]; diff --git a/third_party/chibicc/as.c b/third_party/chibicc/as.c new file mode 100644 index 00000000..7089d05e --- /dev/null +++ b/third_party/chibicc/as.c @@ -0,0 +1,1642 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/bits/popcnt.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" +#include "libc/elf/def.h" +#include "libc/fmt/conv.h" +#include "libc/log/log.h" +#include "libc/macros.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/bsr.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/x/x.h" +#include "third_party/gdtoa/gdtoa.h" +#include "tool/build/lib/elfwriter.h" + +/* WORK IN PROGRESS */ + +#define OSZ 0x66 +#define ASZ 0x67 +#define REX 0x40 // byte +#define REXB 0x41 // src +#define REXX 0x42 // index +#define REXR 0x44 // dest +#define REXW 0x48 // quad +#define HASASZ 0x00010000 +#define HASBASE 0x00020000 +#define HASINDEX 0x00040000 +#define ISRIP 0x00080000 +#define ISREG 0x00100000 + +#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p)) +#define IS(P, N, S) (N == strlen(S) && !strncasecmp(P, S, strlen(S))) + +struct Strings { + size_t n; + char **p; +}; + +struct Assembler { + int i; + int section; + int previous; + struct Ints { + size_t n; + long *p; + } ints; + struct Floats { + size_t n; + long double *p; + } floats; + struct Slices { + size_t n; + struct Slice { + size_t n; + char *p; + } * p; + } slices; + struct Sauces { + size_t n; + struct Sauce { + const char *path; + int line; + } * p; + } sauces; + struct Things { + size_t n; + struct Thing { + enum ThingType { + TT_INT, + TT_FLOAT, + TT_SLICE, + TT_PUNCT, + TT_FORWARD, + TT_BACKWARD, + } t; + int i; + int s; + } * p; + } things; + struct Sections { + size_t n; + struct Section { + const char *name; + int flags; + int type; + int align; + struct Slice binary; + } * p; + } sections; + struct Symbols { + size_t n; + struct Symbol { + int name; + int section; + int stb; + int stv; + int type; + long location; + long size; + } * p; + } symbols; + struct Labels { + size_t n; + struct Label { + int s; + int id; + int section; + long location; + } * p; + } labels; +}; + +static const char kPrefixByte[30] = { + 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf0, 0x26, 0x2e, 0x36, 0x3e, + 0x64, 0x65, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x66, 0x67, +}; + +static const char kPrefix[30][8] = { + "rep", "repz", "repe", "repne", "repnz", "lock", + "es", "cs", "ss", "ds", "fs", "gs", + "rex", "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.wrb", "rex.wrx", "rex.wrxb", "data16", "addr32", +}; + +/** + * Context-sensitive register encoding information. + * + * ┌rex + * │ ┌log₂size + * │ │ ┌reg + * ├──────┐ ├─┐├─┐ + * 0b0000000000000000 + */ +static const struct Reg { + char s[8]; + short reg; + short rm; + short base; + short index; +} kRegs[] = /* clang-format off */ { + {"ah", 4, 4, -1, -1 }, + {"al", 0, 0, -1, -1 }, + {"ax", 0 | 1<<3, 0 | 1<<3, -1, -1 }, + {"bh", 7, 7, -1, -1 }, + {"bl", 3, 3, -1, -1 }, + {"bp", 5 | 1<<3, 5 | 1<<3, -1, -1 }, + {"bpl", 5 | REX<<8, 5 | REX<<8, -1, -1 }, + {"bx", 3 | 1<<3, 3 | 1<<3, -1, -1 }, + {"ch", 5, 5, -1, -1 }, + {"cl", 1, 1, -1, -1 }, + {"cx", 1 | 1<<3, 1 | 1<<3, -1, -1 }, + {"dh", 6, 6, -1, -1 }, + {"di", 7 | 1<<3, 7 | 1<<3, -1, -1 }, + {"dil", 7 | REX<<8, 7 | REX<<8, -1, -1 }, + {"dl", 2, 2, -1, -1 }, + {"dx", 2 | 1<<3, 2 | 1<<3, -1, -1 }, + {"eax", 0 | 2<<3, 0 | 2<<3, 0 | 2<<3, 0 | 2<<3 }, + {"ebp", 5 | 2<<3, 5 | 2<<3, 5 | 2<<3, 5 | 2<<3 }, + {"ebx", 3 | 2<<3, 3 | 2<<3, 3 | 2<<3, 3 | 2<<3 }, + {"ecx", 1 | 2<<3, 1 | 2<<3, 1 | 2<<3, 1 | 2<<3 }, + {"edi", 7 | 2<<3, 7 | 2<<3, 7 | 2<<3, 7 | 2<<3 }, + {"edx", 2 | 2<<3, 2 | 2<<3, 2 | 2<<3, 2 | 2<<3 }, + {"esi", 6 | 2<<3, 6 | 2<<3, 6 | 2<<3, 6 | 2<<3 }, + {"esp", 4 | 2<<3, 4 | 2<<3, 4 | 2<<3, 4 | 2<<3 }, + {"r10", 2 | 3<<3 | REXR<<8 | REXW<<8, 2 | 3<<3 | REXB<<8 | REXW<<8, 2 | 3<<3 | REXB<<8, 2 | 3<<3 | REXX<<8 }, + {"r10b", 2 | REXR<<8, 2 | REXB<<8, -1, -1 }, + {"r10d", 2 | 2<<3 | REXR<<8, 2 | 2<<3 | REXB<<8, 2 | 2<<3 | REXB<<8, 2 | 2<<3 | REXX<<8 }, + {"r10w", 2 | 1<<3 | REXR<<8, 2 | 1<<3 | REXB<<8, -1, -1 }, + {"r11", 3 | 3<<3 | REXR<<8 | REXW<<8, 3 | 3<<3 | REXB<<8 | REXW<<8, 3 | 3<<3 | REXB<<8, 3 | 3<<3 | REXX<<8 }, + {"r11b", 3 | REXR<<8, 3 | REXB<<8, -1, -1 }, + {"r11d", 3 | 2<<3 | REXR<<8, 3 | 2<<3 | REXB<<8, 3 | 2<<3 | REXB<<8, 3 | 2<<3 | REXX<<8 }, + {"r11w", 3 | 1<<3 | REXR<<8, 3 | 1<<3 | REXB<<8, -1, -1 }, + {"r12", 4 | 3<<3 | REXR<<8 | REXW<<8, 4 | 3<<3 | REXB<<8 | REXW<<8, 4 | 3<<3 | REXB<<8, 4 | 3<<3 | REXX<<8 }, + {"r12b", 4 | REXR<<8, 4 | REXB<<8, -1, -1 }, + {"r12d", 4 | 2<<3 | REXR<<8, 4 | 2<<3 | REXB<<8, 4 | 2<<3 | REXB<<8, 4 | 2<<3 | REXX<<8 }, + {"r12w", 4 | 1<<3 | REXR<<8, 4 | 1<<3 | REXB<<8, -1, -1 }, + {"r13", 5 | 3<<3 | REXR<<8 | REXW<<8, 5 | 3<<3 | REXB<<8 | REXW<<8, 5 | 3<<3 | REXB<<8, 5 | 3<<3 | REXX<<8 }, + {"r13b", 5 | REXR<<8, 5 | REXB<<8, -1, -1 }, + {"r13d", 5 | 2<<3 | REXR<<8, 5 | 2<<3 | REXB<<8, 5 | 2<<3 | REXB<<8, 5 | 2<<3 | REXX<<8 }, + {"r13w", 5 | 1<<3 | REXR<<8, 5 | 1<<3 | REXB<<8, -1, -1 }, + {"r14", 6 | 3<<3 | REXR<<8 | REXW<<8, 6 | 3<<3 | REXB<<8 | REXW<<8, 6 | 3<<3 | REXB<<8, 6 | 3<<3 | REXX<<8 }, + {"r14b", 6 | REXR<<8, 6 | REXB<<8, -1, -1 }, + {"r14d", 6 | 2<<3 | REXR<<8, 6 | 2<<3 | REXB<<8, 6 | 2<<3 | REXB<<8, 6 | 2<<3 | REXX<<8 }, + {"r14w", 6 | 1<<3 | REXR<<8, 6 | 1<<3 | REXB<<8, -1, -1 }, + {"r15", 7 | 3<<3 | REXR<<8 | REXW<<8, 7 | 3<<3 | REXB<<8 | REXW<<8, 7 | 3<<3 | REXB<<8, 7 | 3<<3 | REXX<<8 }, + {"r15b", 7 | REXR<<8, 7 | REXB<<8, -1, -1 }, + {"r15d", 7 | 2<<3 | REXR<<8, 7 | 2<<3 | REXB<<8, 7 | 2<<3 | REXB<<8, 7 | 2<<3 | REXX<<8 }, + {"r15w", 7 | 1<<3 | REXR<<8, 7 | 1<<3 | REXB<<8, -1, -1 }, + {"r8", 0 | 3<<3 | REXR<<8 | REXW<<8, 0 | 3<<3 | REXB<<8 | REXW<<8, 0 | 3<<3 | REXB<<8, 0 | 3<<3 | REXX<<8 }, + {"r8b", 0 | REXR<<8, 0 | REXB<<8, -1, -1 }, + {"r8d", 0 | 2<<3 | REXR<<8, 0 | 2<<3 | REXB<<8, 0 | 2<<3 | REXB<<8, 0 | 2<<3 | REXX<<8 }, + {"r8w", 0 | 1<<3 | REXR<<8, 0 | 1<<3 | REXB<<8, -1, -1 }, + {"r9", 1 | 3<<3 | REXR<<8 | REXW<<8, 1 | 3<<3 | REXB<<8 | REXW<<8, 1 | 3<<3 | REXB<<8, 1 | 3<<3 | REXX<<8 }, + {"r9b", 1 | REXR<<8, 1 | REXB<<8, -1, -1 }, + {"r9d", 1 | 2<<3 | REXR<<8, 1 | 2<<3 | REXB<<8, 1 | 2<<3 | REXB<<8, 1 | 2<<3 | REXX<<8 }, + {"r9w", 1 | 1<<3 | REXR<<8, 1 | 1<<3 | REXB<<8, -1, -1 }, + {"rax", 0 | 3<<3 | REXW<<8, 0 | 3<<3 | REXW<<8, 0 | 3<<3, 0 | 3<<3 }, + {"rbp", 5 | 3<<3 | REXW<<8, 5 | 3<<3 | REXW<<8, 5 | 3<<3, 5 | 3<<3 }, + {"rbx", 3 | 3<<3 | REXW<<8, 3 | 3<<3 | REXW<<8, 3 | 3<<3, 3 | 3<<3 }, + {"rcx", 1 | 3<<3 | REXW<<8, 1 | 3<<3 | REXW<<8, 1 | 3<<3, 1 | 3<<3 }, + {"rdi", 7 | 3<<3 | REXW<<8, 7 | 3<<3 | REXW<<8, 7 | 3<<3, 7 | 3<<3 }, + {"rdx", 2 | 3<<3 | REXW<<8, 2 | 3<<3 | REXW<<8, 2 | 3<<3, 2 | 3<<3 }, + {"rsi", 6 | 3<<3 | REXW<<8, 6 | 3<<3 | REXW<<8, 6 | 3<<3, 6 | 3<<3 }, + {"rsp", 4 | 3<<3 | REXW<<8, 4 | 3<<3 | REXW<<8, 4 | 3<<3, 4 | 3<<3 }, + {"si", 6 | 1<<3, 6 | 1<<3, 6 | 1<<3, 6 | 1<<3 }, + {"sil", 6 | REX<<8, 6 | REX<<8, 6 | REX<<8, 6 | REX<<8 }, + {"sp", 4 | 1<<3, 4 | 1<<3, 4 | 1<<3, 4 | 1<<3 }, + {"spl", 4 | REX<<8, 4 | REX<<8, 4 | REX<<8, 4 | REX<<8 }, + {"xmm0", 0 | 4<<3, 0 | 4<<3, -1, -1 }, + {"xmm1", 1 | 4<<3, 1 | 4<<3, -1, -1 }, + {"xmm10", 2 | 4<<3 | REXR<<8, 2 | 4<<3 | REXB<<8, -1, -1 }, + {"xmm11", 3 | 4<<3 | REXR<<8, 3 | 4<<3 | REXB<<8, -1, -1 }, + {"xmm12", 4 | 4<<3 | REXR<<8, 4 | 4<<3 | REXB<<8, -1, -1 }, + {"xmm13", 5 | 4<<3 | REXR<<8, 5 | 4<<3 | REXB<<8, -1, -1 }, + {"xmm14", 6 | 4<<3 | REXR<<8, 6 | 4<<3 | REXB<<8, -1, -1 }, + {"xmm15", 7 | 4<<3 | REXR<<8, 7 | 4<<3 | REXB<<8, -1, -1 }, + {"xmm2", 2 | 4<<3, 2 | 4<<3, -1, -1 }, + {"xmm3", 3 | 4<<3, 3 | 4<<3, -1, -1 }, + {"xmm4", 4 | 4<<3, 4 | 4<<3, -1, -1 }, + {"xmm5", 5 | 4<<3, 5 | 4<<3, -1, -1 }, + {"xmm6", 6 | 4<<3, 6 | 4<<3, -1, -1 }, + {"xmm7", 7 | 4<<3, 7 | 4<<3, -1, -1 }, + {"xmm8", 0 | 4<<3 | REXR<<8, 0 | 4<<3 | REXB<<8, -1, -1 }, + {"xmm9", 1 | 4<<3 | REXR<<8, 1 | 4<<3 | REXB<<8, -1, -1 }, +} /* clang-format on */; + +static bool g_ignore_err; +static const char *g_input_path; +static const char *g_output_path; +static struct Strings g_include_paths; + +static void AppendString(struct Strings *l, const char *p) { + l->p = realloc(l->p, ++l->n * sizeof(*l->p)); + l->p[l->n - 1] = p; +} + +static void ReadFlags(int argc, char *argv[]) { + int i; + g_input_path = "-"; + g_output_path = "a.out"; + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-o")) { + g_output_path = argv[++i]; + } else if (startswith(argv[i], "-o")) { + g_output_path = argv[i] + 2; + } else if (!strcmp(argv[i], "-I")) { + AppendString(&g_include_paths, argv[++i]); + } else if (startswith(argv[i], "-I")) { + AppendString(&g_include_paths, argv[i] + 2); + } else if (!strcmp(argv[i], "-Z")) { + g_ignore_err = true; + } else if (argv[i][0] != '-') { + g_input_path = argv[i]; + } + } +} + +static int AppendSection(struct Assembler *a, const char *name, int flags, + int type) { + int i; + APPEND(a->sections); + i = a->sections.n - 1; + a->sections.p[i].name = name; + a->sections.p[i].flags = flags; + a->sections.p[i].type = type; + a->sections.p[i].align = 1; + a->sections.p[i].binary.p = NULL; + a->sections.p[i].binary.n = 0; + return i; +} + +static struct Assembler *NewAssembler(void) { + struct Assembler *a = calloc(1, sizeof(struct Assembler)); + APPEND(a->slices); + a->slices.p[0].p = NULL; + a->slices.p[0].n = 0; + AppendSection(a, "", 0, SHT_NULL); + AppendSection(a, ".text", SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS); + AppendSection(a, ".data", SHF_ALLOC | SHF_WRITE, SHT_PROGBITS); + AppendSection(a, ".bss", SHF_ALLOC | SHF_WRITE, SHT_NOBITS); + a->section = 1; + return a; +} + +static int AppendLine(struct Assembler *a, char *path, int line) { + if (!a->sauces.n || (line != a->sauces.p[a->sauces.n - 1].line || + path != a->sauces.p[a->sauces.n - 1].path)) { + APPEND(a->sauces); + a->sauces.p[a->sauces.n - 1].path = path; + a->sauces.p[a->sauces.n - 1].line = line; + } + return a->sauces.n - 1; +} + +static int ReadCharLiteral(struct Slice *buf, int c, char *p, int *i) { + if (c != '\\') return c; + switch ((c = p[(*i)++])) { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 't': + return '\t'; + case 'n': + return '\n'; + case 'v': + return '\v'; + case 'f': + return '\f'; + case 'r': + return '\r'; + case 'e': + return 033; + case 'x': + if (isxdigit(p[*i])) { + c = hextoint(p[(*i)++]); + if (isxdigit(p[*i])) { + c = c * 16 + hextoint(p[(*i)++]); + } + } + return c; + case '0' ... '7': + c -= '0'; + if ('0' <= p[*i] && p[*i] <= '7') { + c = c * 8 + (p[(*i)++] - '0'); + if ('0' <= p[*i] && p[*i] <= '7') { + c = c * 8 + (p[(*i)++] - '0'); + } + } + return c; + default: + return c; + } +} + +static void CanonicalizeNewline(char *p) { + int i = 0, j = 0; + while (p[i]) { + if (p[i] == '\r' && p[i + 1] == '\n') { + i += 2; + p[j++] = '\n'; + } else if (p[i] == '\r') { + i++; + p[j++] = '\n'; + } else { + p[j++] = p[i++]; + } + } + p[j] = '\0'; +} + +static void RemoveBackslashNewline(char *p) { + int i, j, n; + for (i = j = n = 0; p[i];) { + if (p[i] == '\\' && p[i + 1] == '\n') { + i += 2; + n++; + } else if (p[i] == '\n') { + p[j++] = p[i++]; + for (; n > 0; n--) p[j++] = '\n'; + } else { + p[j++] = p[i++]; + } + } + for (; n > 0; n--) p[j++] = '\n'; + p[j] = '\0'; +} + +static char *ReadFile(const char *path) { + char *p; + FILE *fp; + int buflen, nread, end, n; + if (strcmp(path, "-") == 0) { + fp = stdin; + } else { + fp = fopen(path, "r"); + if (!fp) return NULL; + } + buflen = 4096; + nread = 0; + p = calloc(1, buflen); + for (;;) { + end = buflen - 2; + n = fread(p + nread, 1, end - nread, fp); + if (n == 0) break; + nread += n; + if (nread == end) { + buflen *= 2; + p = realloc(p, buflen); + } + } + if (fp != stdin) fclose(fp); + if (nread > 0 && p[nread - 1] == '\\') { + p[nread - 1] = '\n'; + } else if (nread == 0 || p[nread - 1] != '\n') { + p[nread++] = '\n'; + } + p[nread] = '\0'; + return p; +} + +static void Tokenize(struct Assembler *a, char *path) { + bool bol; + int c, i, line; + char *p, *path2; + struct Slice buf; + if (!(p = ReadFile(path))) return; + if (!memcmp(p, "\357\273\277", 3)) p += 3; + CanonicalizeNewline(p); + RemoveBackslashNewline(p); + line = 1; + bol = true; + while ((c = *p)) { + if (c == '#' || (c == '/' && bol)) { + p = strchr(p, '\n') + 1; + bol = true; + ++line; + continue; + } + if (c == '\n') { + c = ';'; + bol = true; + ++line; + } else { + bol = false; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' || + c == '\v' || c == ',') { + ++p; + continue; + } + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_' || + c == '%' || c == '@' || (c == '.' && !('0' <= p[1] && p[1] <= '9'))) { + for (i = 1;; ++i) { + if (!(('a' <= p[i] && p[i] <= 'z') || ('A' <= p[i] && p[i] <= 'Z') || + ('0' <= p[i] && p[i] <= '9') || p[i] == '.' || p[i] == '_' || + p[i] == '$' || p[i] == '(' || p[i] == ')')) { + break; + } + } + APPEND(a->things); + a->things.p[a->things.n - 1].t = TT_SLICE; + a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + a->things.p[a->things.n - 1].i = a->slices.n; + APPEND(a->slices); + a->slices.p[a->slices.n - 1].p = p; + a->slices.p[a->slices.n - 1].n = i; + p += i; + continue; + } + if (('0' <= c && c <= '9') || (c == '.' && '0' <= p[1] && p[1] <= '9')) { + bool isfloat = c == '.'; + if (c == '0' && p[1] != '.') { + if (p[1] == 'x' || p[1] == 'X') { + for (i = 2;; ++i) { + if (!(('0' <= p[i] && p[i] <= '9') || + ('a' <= p[i] && p[i] <= 'f') || + ('A' <= p[i] && p[i] <= 'F'))) { + break; + } + } + } else if (p[1] == 'b' || p[1] == 'B') { + for (i = 2;; ++i) { + if (!(p[i] == '0' || p[i] == '1')) break; + } + } else { + for (i = 1;; ++i) { + if (!('0' <= p[i] && p[i] <= '7')) break; + } + } + } else { + for (i = 1;; ++i) { + if (('0' <= p[i] && p[i] <= '9') || p[i] == '-' || p[i] == '+') { + continue; + } else if (p[i] == '.' || p[i] == 'e' || p[i] == 'E' || + p[i] == 'e') { + isfloat = true; + continue; + } + break; + } + } + APPEND(a->things); + if (isfloat) { + APPEND(a->floats); + a->floats.p[a->floats.n - 1] = strtold(p, NULL); + a->things.p[a->things.n - 1].i = a->floats.n - 1; + a->things.p[a->things.n - 1].t = TT_FLOAT; + } else { + APPEND(a->ints); + a->ints.p[a->ints.n - 1] = strtol(p, NULL, 0); + a->things.p[a->things.n - 1].i = a->ints.n - 1; + if (p[i] == 'f' || p[i] == 'F') { + a->things.p[a->things.n - 1].t = TT_FORWARD; + } else if (p[i] == 'b' || p[i] == 'B') { + a->things.p[a->things.n - 1].t = TT_BACKWARD; + } else { + a->things.p[a->things.n - 1].t = TT_INT; + } + } + a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + p += i; + continue; + } + if (c == '\'') { + i = 1; + c = p[i++]; + c = ReadCharLiteral(&buf, c, p, &i); + if (p[i] == '\'') ++i; + p += i; + APPEND(a->things); + a->things.p[a->things.n - 1].t = TT_INT; + a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + a->things.p[a->things.n - 1].i = a->ints.n; + APPEND(a->ints); + a->ints.p[a->ints.n - 1] = c; + continue; + } + if (c == '"') { + buf.n = 0; + buf.p = NULL; + for (i = 1; (c = p[i++]);) { + if (c == '"') break; + c = ReadCharLiteral(&buf, c, p, &i); + APPEND(buf); + buf.p[buf.n - 1] = c; + } + p += i; + if (a->things.n && a->things.p[a->things.n - 1].t == TT_SLICE && + IS(a->slices.p[a->things.p[a->things.n - 1].i].p, + a->slices.p[a->things.p[a->things.n - 1].i].n, ".include")) { + APPEND(buf); + buf.p[buf.n - 1] = '\0'; + --a->things.n; + if (fileexists(buf.p)) { + Tokenize(a, buf.p); + } else { + for (i = 0; i < g_include_paths.n; ++i) { + path2 = xstrcat(g_include_paths.p[i], '/', buf.p); + if (fileexists(path2)) { + Tokenize(a, path2); + free(path2); + break; + } else { + free(path2); + } + } + } + free(buf.p); + } else { + APPEND(a->things); + a->things.p[a->things.n - 1].t = TT_SLICE; + a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + a->things.p[a->things.n - 1].i = a->slices.n; + APPEND(a->slices); + a->slices.p[a->slices.n - 1] = buf; + } + continue; + } + } + APPEND(a->things); + a->things.p[a->things.n - 1].t = TT_PUNCT; + a->things.p[a->things.n - 1].s = AppendLine(a, path, line); + a->things.p[a->things.n - 1].i = c; + ++p; + } +} + +static bool IsLocal(struct Assembler *a, int name) { + if (name < 0) return true; + return a->slices.p[name].n >= 2 && !memcmp(a->slices.p[name].p, ".L", 2); +} + +static int GetSymbol(struct Assembler *a, int name) { + int i; + for (i = 0; i < a->symbols.n; ++i) { + if (a->slices.p[a->symbols.p[i].name].n == a->slices.p[name].n && + !memcmp(a->slices.p[a->symbols.p[i].name].p, a->slices.p[name].p, + a->slices.p[name].n)) { + return i; + } + } + APPEND(a->symbols); + i = a->symbols.n - 1; + memset(&a->symbols.p[i], 0, sizeof(a->symbols.p[i])); + a->symbols.p[i].name = name; + return i; +} + +static wontreturn void Fail(struct Assembler *a, const char *fmt, ...) { + va_list va; + fprintf(stderr, "%s:%d:: ", a->sauces.p[a->things.p[a->i].s].path, + a->sauces.p[a->things.p[a->i].s].line); + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); + fputc('\n', stderr); + exit(1); +} + +static void Label(struct Assembler *a, int name) { + int i = GetSymbol(a, name); + if (a->symbols.p[i].section) { + Fail(a, "already defined: %.*s", a->slices.p[name].n, a->slices.p[name].p); + } + a->symbols.p[i].section = a->section; + a->symbols.p[i].location = a->sections.p[a->section].binary.n; + a->i += 2; +} + +static void LocalLabel(struct Assembler *a, int id) { + APPEND(a->labels); + a->labels.p[a->labels.n - 1].s = a->things.p[a->i].s; + a->labels.p[a->labels.n - 1].id = id; + a->labels.p[a->labels.n - 1].section = a->section; + a->labels.p[a->labels.n - 1].location = a->sections.p[a->section].binary.n; + a->i += 2; +} + +static void SetSection(struct Assembler *a, int section) { + a->previous = a->section; + a->section = section; +} + +static bool IsInt(struct Assembler *a, int i) { + return a->things.p[i].t == TT_INT; +} + +static bool IsFloat(struct Assembler *a, int i) { + return a->things.p[i].t == TT_FLOAT; +} + +static bool IsSlice(struct Assembler *a, int i) { + return a->things.p[i].t == TT_SLICE; +} + +static bool IsPunct(struct Assembler *a, int i, int c) { + return a->things.p[i].t == TT_PUNCT && a->things.p[i].i == c; +} + +static void ConsumePunct(struct Assembler *a, int c) { + if (IsPunct(a, a->i, c)) { + ++a->i; + } else { + Fail(a, "expected %`'c", c); + } +} + +static long GetInt(struct Assembler *a) { + long res; + if (IsInt(a, a->i)) { + res = a->ints.p[a->things.p[a->i].i]; + ++a->i; + return res; + } else { + Fail(a, "expected int"); + } +} + +static long double GetFloat(struct Assembler *a) { + long double res; + if (IsFloat(a, a->i)) { + res = a->floats.p[a->things.p[a->i].i]; + ++a->i; + return res; + } else { + Fail(a, "expected float"); + } +} + +static struct Slice GetString(struct Assembler *a) { + struct Slice res; + if (IsSlice(a, a->i)) { + res = a->slices.p[a->things.p[a->i].i]; + ++a->i; + return res; + } else { + Fail(a, "expected string"); + } +} + +static void Emit(struct Assembler *a, int c) { + APPEND(a->sections.p[a->section].binary); + a->sections.p[a->section].binary.p[a->sections.p[a->section].binary.n - 1] = + c; +} + +static void EmitWord(struct Assembler *a, long x) { + Emit(a, x >> 000); + Emit(a, x >> 010); +} + +static void EmitLong(struct Assembler *a, long x) { + Emit(a, x >> 000); + Emit(a, x >> 010); + Emit(a, x >> 020); + Emit(a, x >> 030); +} + +static void EmitQuad(struct Assembler *a, long x) { + EmitLong(a, x >> 000); + EmitLong(a, x >> 040); +} + +static void OnZero(struct Assembler *a) { + long i, n; + while (IsInt(a, a->i)) { + n = GetInt(a); + for (i = 0; i < n; ++i) { + Emit(a, 0); + } + } +} + +static void OnFill(struct Assembler *a) { + int x; + long i, n; + n = GetInt(a); + if (IsInt(a, a->i)) { + x = GetInt(a); + } else { + x = 0; + } + for (i = 0; i < n; ++i) { + Emit(a, x); + } +} + +static void OnByte(struct Assembler *a) { + while (IsInt(a, a->i)) { + Emit(a, GetInt(a)); + } +} + +static void OnWord(struct Assembler *a) { + long x; + while (IsInt(a, a->i)) { + x = GetInt(a); + Emit(a, x >> 000); + Emit(a, x >> 010); + } +} + +static void OnLong(struct Assembler *a) { + while (IsInt(a, a->i)) { + EmitLong(a, GetInt(a)); + } +} + +static void OnQuad(struct Assembler *a) { + while (IsInt(a, a->i)) { + EmitQuad(a, GetInt(a)); + } +} + +static void OnFloat(struct Assembler *a) { + float f; + char b[4]; + while (IsFloat(a, a->i)) { + f = GetFloat(a); + memcpy(b, &f, 4); + Emit(a, b[0]); + Emit(a, b[1]); + Emit(a, b[2]); + Emit(a, b[3]); + } +} + +static void OnDouble(struct Assembler *a) { + double f; + char b[8]; + while (IsFloat(a, a->i)) { + f = GetFloat(a); + memcpy(b, &f, 8); + Emit(a, b[0]); + Emit(a, b[1]); + Emit(a, b[2]); + Emit(a, b[3]); + Emit(a, b[4]); + Emit(a, b[5]); + Emit(a, b[6]); + Emit(a, b[7]); + } +} + +static void OnAscii(struct Assembler *a) { + size_t i; + struct Slice arg; + while (IsSlice(a, a->i)) { + arg = GetString(a); + for (i = 0; i < arg.n; ++i) { + Emit(a, arg.p[i]); + } + } +} + +static void OnAsciz(struct Assembler *a) { + size_t i; + struct Slice arg; + while (IsSlice(a, a->i)) { + arg = GetString(a); + for (i = 0; i < arg.n; ++i) { + Emit(a, arg.p[i]); + } + Emit(a, 0); + } +} + +static void OnAbort(struct Assembler *a) { + Fail(a, "aborted"); +} + +static void OnErr(struct Assembler *a) { + if (g_ignore_err) return; + Fail(a, "error"); +} + +static void OnError(struct Assembler *a) { + struct Slice msg = GetString(a); + if (g_ignore_err) return; + Fail(a, "%.*s", msg.n, msg.p); +} + +static void OnText(struct Assembler *a) { + SetSection(a, 0); +} + +static void OnData(struct Assembler *a) { + SetSection(a, 1); +} + +static void OnBss(struct Assembler *a) { + SetSection(a, 2); +} + +static void OnPrevious(struct Assembler *a) { + SetSection(a, a->previous); +} + +static void OnAlign(struct Assembler *a) { + long i, n, align, fill, maxskip; + align = GetInt(a); + if (popcnt(align) != 1) Fail(a, "alignment not power of 2"); + fill = (a->sections.p[a->section].flags & SHF_EXECINSTR) ? 0x90 : 0; + maxskip = 268435456; + if (IsInt(a, a->i)) { + fill = GetInt(a); + if (IsInt(a, a->i)) { + maxskip = GetInt(a); + } + } + i = a->sections.p[a->section].binary.n; + n = ROUNDUP(i, align) - i; + if (n > maxskip) return; + a->sections.p[a->section].align = MAX(a->sections.p[a->section].align, align); + for (i = 0; i < n; ++i) { + Emit(a, fill); + } +} + +static int SectionFlag(struct Assembler *a, int c) { + switch (c) { + case 'a': + return SHF_ALLOC; + case 'w': + return SHF_WRITE; + case 'x': + return SHF_EXECINSTR; + case 'g': + return SHF_GROUP; + case 'M': + return SHF_MERGE; + case 'S': + return SHF_STRINGS; + case 'T': + return SHF_TLS; + default: + Fail(a, "unknown section flag: %`'c", c); + } +} + +static int SectionFlags(struct Assembler *a, struct Slice s) { + int i, flags; + for (flags = i = 0; i < s.n; ++i) { + flags |= SectionFlag(a, s.p[i]); + } + return flags; +} + +static int SectionType(struct Assembler *a, struct Slice s) { + if (IS(s.p, s.n, "@progbits")) { + return SHT_PROGBITS; + } else if (IS(s.p, s.n, "@note")) { + return SHT_NOTE; + } else if (IS(s.p, s.n, "@nobits")) { + return SHT_NOBITS; + } else if (IS(s.p, s.n, "@preinit_array")) { + return SHT_PREINIT_ARRAY; + } else if (IS(s.p, s.n, "@init_array")) { + return SHT_INIT_ARRAY; + } else if (IS(s.p, s.n, "@fini_array")) { + return SHT_FINI_ARRAY; + } else { + Fail(a, "unknown section type: %.*s", s.n, s.p); + } +} + +static void OnSection(struct Assembler *a) { + char *name; + int flags, type; + struct Slice arg; + arg = GetString(a); + name = strndup(arg.p, arg.n); + if (startswith(name, ".text")) { + flags = SHF_ALLOC | SHF_EXECINSTR; + type = SHT_PROGBITS; + } else if (startswith(name, ".data")) { + flags = SHF_ALLOC | SHF_WRITE; + type = SHT_PROGBITS; + } else if (startswith(name, ".bss")) { + flags = SHF_ALLOC | SHF_WRITE; + type = SHT_NOBITS; + } else { + flags = SHF_ALLOC | SHF_EXECINSTR | SHF_WRITE; + type = SHT_PROGBITS; + } + if (IsSlice(a, a->i)) { + flags = SectionFlags(a, GetString(a)); + if (IsSlice(a, a->i)) { + type = SectionType(a, GetString(a)); + } + } + SetSection(a, AppendSection(a, name, flags, type)); +} + +static void OnIncbin(struct Assembler *a) { + int fd; + char *path; + struct stat st; + struct Slice arg; + arg = GetString(a); + path = strndup(arg.p, arg.n); + if ((fd = open(path, O_RDONLY)) == -1 || fstat(fd, &st) == -1) { + Fail(a, "open failed: %s", path); + } + a->sections.p[a->section].binary.p = + realloc(a->sections.p[a->section].binary.p, + a->sections.p[a->section].binary.n + st.st_size); + if (read(fd, a->sections.p[a->section].binary.p, st.st_size) != st.st_size) { + Fail(a, "read failed: %s", path); + } + a->sections.p[a->section].binary.n += st.st_size; + close(fd); + free(path); +} + +static int SymbolType(struct Assembler *a, struct Slice s) { + if (IS(s.p, s.n, "@object") || IS(s.p, s.n, "STT_OBJECT")) { + return STT_OBJECT; + } else if (IS(s.p, s.n, "@function") || IS(s.p, s.n, "STT_FUNC")) { + return STT_FUNC; + } else if (IS(s.p, s.n, "@common") || IS(s.p, s.n, "STT_COMMON")) { + return STT_COMMON; + } else if (IS(s.p, s.n, "@notype") || IS(s.p, s.n, "STT_NOTYPE")) { + return STT_NOTYPE; + } else if (IS(s.p, s.n, "@tls_object") || IS(s.p, s.n, "STT_TLS")) { + return STT_TLS; + } else { + Fail(a, "unknown symbol type: %.*s", s.n, s.p); + } +} + +static void OnType(struct Assembler *a) { + int i; + i = GetSymbol(a, a->things.p[a->i++].i); + a->symbols.p[i].type = SymbolType(a, GetString(a)); +} + +static void OnSize(struct Assembler *a) { + int i; + i = GetSymbol(a, a->things.p[a->i++].i); + a->symbols.p[i].size = GetInt(a); +} + +static void OnInternal(struct Assembler *a) { + int i; + while (IsSlice(a, a->i)) { + i = GetSymbol(a, a->things.p[a->i++].i); + a->symbols.p[i].stv = STV_INTERNAL; + } +} + +static void OnHidden(struct Assembler *a) { + int i; + while (IsSlice(a, a->i)) { + i = GetSymbol(a, a->things.p[a->i++].i); + a->symbols.p[i].stv = STV_HIDDEN; + } +} + +static void OnProtected(struct Assembler *a) { + int i; + while (IsSlice(a, a->i)) { + i = GetSymbol(a, a->things.p[a->i++].i); + a->symbols.p[i].stv = STV_PROTECTED; + } +} + +static void OnLocal(struct Assembler *a) { + int i; + while (IsSlice(a, a->i)) { + i = GetSymbol(a, a->things.p[a->i++].i); + a->symbols.p[i].stb = STB_LOCAL; + } +} + +static void OnWeak(struct Assembler *a) { + int i; + while (IsSlice(a, a->i)) { + i = GetSymbol(a, a->things.p[a->i++].i); + a->symbols.p[i].stb = STB_WEAK; + } +} + +static void OnGlobal(struct Assembler *a) { + int i; + while (IsSlice(a, a->i)) { + i = GetSymbol(a, a->things.p[a->i++].i); + a->symbols.p[i].stb = STB_GLOBAL; + } +} + +static bool IsSizableOp(const char *op, struct Slice s) { + size_t n = strlen(op); + if (n == s.n) return !memcmp(op, s.p, n); + if (n + 1 == s.n && !memcmp(op, s.p, n)) { + switch (s.p[n]) { + case 'b': + case 'B': + case 'w': + case 'W': + case 'l': + case 'L': + case 'q': + case 'Q': + return true; + default: + break; + } + } + return false; +} + +static int GetOpSize(struct Assembler *a, struct Slice s) { + switch (s.p[s.n - 1]) { + case 'b': + case 'B': + return 0; + case 'w': + case 'W': + return 1; + case 'l': + case 'L': + return 2; + case 'q': + case 'Q': + return 3; + default: + Fail(a, "could not size instruction"); + } +} + +static int CompareString8(const char a[8], const char b[8]) { + uint64_t x, y; + x = READ64BE(a); + y = READ64BE(b); + return x > y ? 1 : x < y ? -1 : 0; +} + +static bool FindReg(const char *p, size_t n, struct Reg *out_reg) { + char key[8]; + int i, c, m, l, r; + if (n && n <= 8) { + if (*p == '%') ++p, --n; + memset(key, 0, sizeof(key)); + for (i = 0; i < n; ++i) { + key[i] = tolower(p[i]); + } + l = 0; + r = ARRAYLEN(kRegs) - 1; + while (l <= r) { + m = (l + r) >> 1; + c = CompareString8(kRegs[m].s, key); + if (c < 0) { + l = m + 1; + } else if (c > 0) { + r = m - 1; + } else { + *out_reg = kRegs[m]; + return true; + } + } + } + return false; +} + +static int RemoveRexw(int x) { + if (x == -1) return x; + x &= ~0x0800; + if (((x & 0xff00) >> 8) == REX) x &= ~0xff00; + return x; +} + +static int FindRegReg(struct Slice s) { + struct Reg reg; + if (!FindReg(s.p, s.n, ®)) return -1; + return reg.reg; +} + +static int FindRegRm(struct Slice s) { + struct Reg reg; + if (!FindReg(s.p, s.n, ®)) return -1; + return reg.rm; +} + +static int FindRegBase(struct Slice s) { + struct Reg reg; + if (!FindReg(s.p, s.n, ®)) return -1; + return reg.base; +} + +static int FindRegIndex(struct Slice s) { + struct Reg reg; + if (!FindReg(s.p, s.n, ®)) return -1; + return reg.index; +} + +static wontreturn void InvalidRegister(struct Assembler *a) { + Fail(a, "invalid register"); +} + +static int GetRegisterRm(struct Assembler *a) { + int reg; + struct Slice wut; + if ((reg = FindRegRm(GetString(a))) == -1) InvalidRegister(a); + return reg; +} + +static int ParseMemory(struct Assembler *a, long *disp) { + /* ┌isrip + │┌hasindex + ││┌hasbase + │││┌hasasz + ││││┌rex + │││││ ┌scale + │││││ │ ┌index + │││││ │ │ ┌base + ││││├──────┐├┐├─┐├─┐ + 0b00000000000000000000000000000000*/ + struct Slice str; + int reg, scale, mem = 0; + *disp = IsInt(a, a->i) ? GetInt(a) : 0; + if (IsPunct(a, a->i, '(')) { + ++a->i; + if ((str = GetString(a)).n) { + mem |= HASBASE; + if (!strncasecmp(str.p, "%rip", str.n)) { + mem |= ISRIP; + } else { + reg = FindRegBase(str); + if (reg == -1) InvalidRegister(a); + mem |= reg & 007; // reg + mem |= reg & 0xff00; // rex + if (((reg & 070) >> 3) == 2) mem |= HASASZ; // asz + } + } + if (!IsPunct(a, a->i, ')')) { + mem |= HASINDEX; + reg = FindRegIndex(GetString(a)); + if (reg == -1) InvalidRegister(a); + mem |= (reg & 007) << 3; // index + mem |= reg & 0xff00; // rex + if (((reg & 070) >> 3) == 2) mem |= HASASZ; // asz + if (!IsPunct(a, a->i, ')')) { + mem |= (bsr(GetInt(a)) & 3) << 6; + } + } else { + mem |= 4 << 3; // rsp index (hint: there is none) + } + ConsumePunct(a, ')'); + } + return mem; +} + +static void EncodeModrm(struct Assembler *a, int reg, int mem, long disp) { + reg <<= 3; + if (mem & (HASBASE | HASINDEX)) { + if (mem & ISRIP) { + Emit(a, 005 | reg); + } else { + Emit(a, 0204 | reg); // suboptimal + Emit(a, mem); + } + } else { + Emit(a, 004 | reg); + Emit(a, 045); + } + EmitLong(a, disp); +} + +static void OnMov(struct Assembler *a, struct Slice op) { + int reg, mem; + long imm, disp; + if (IsPunct(a, a->i, '$')) { + ++a->i; + imm = GetInt(a); + if (IsSlice(a, a->i)) { + reg = GetRegisterRm(a); + if (reg & 0xff00) { + Emit(a, reg >> 8); + } + switch ((reg & 070) >> 3) { + case 0: + Emit(a, 0xb0 + (reg & 7)); + Emit(a, imm); + break; + case 1: + Emit(a, OSZ); + Emit(a, 0xb8 + (reg & 7)); + EmitWord(a, imm); + break; + case 2: + Emit(a, 0xb8 + (reg & 7)); + EmitLong(a, imm); + break; + case 3: + Emit(a, 0xb8 + (reg & 7)); // suboptimal + EmitQuad(a, imm); + break; + default: + Fail(a, "todo movd/movq"); + } + } else { + mem = ParseMemory(a, &disp); + if (mem & 0xff00) { + Emit(a, mem >> 8); + } + switch (GetOpSize(a, op)) { + case 0: + Emit(a, 0xc6); + EncodeModrm(a, 0, mem, disp); + Emit(a, imm); + break; + case 1: + Emit(a, OSZ); + Emit(a, 0xc7); + EncodeModrm(a, 0, mem, disp); + EmitWord(a, imm); + break; + case 2: + Emit(a, 0xc7); + EncodeModrm(a, 0, mem, disp); + EmitLong(a, imm); + break; + case 3: + Emit(a, 0xc7); // suboptimal + EncodeModrm(a, 0, mem, disp); + EmitQuad(a, imm); + break; + default: + unreachable; + } + } + } +} + +static void OnPush(struct Assembler *a) { + long x; + int reg; + if (IsPunct(a, a->i, '$')) { + ++a->i; + x = GetInt(a); + if (-128 <= x && x <= 127) { + Emit(a, 0x6A); + Emit(a, x); + } else { + Emit(a, 0x68); + EmitLong(a, x); + } + } else { + reg = RemoveRexw(GetRegisterRm(a)); + if (reg & 0xff00) Emit(a, reg >> 8); + if (((reg & 070) >> 3) == 1) Emit(a, OSZ); + Emit(a, 0x50 + (reg & 7)); + } +} + +static void OnPop(struct Assembler *a) { + int reg; + reg = RemoveRexw(GetRegisterRm(a)); + if (reg & 0xff00) Emit(a, reg >> 8); + if (((reg & 070) >> 3) == 1) Emit(a, OSZ); + Emit(a, 0x58 + (reg & 7)); +} + +static void OnRet(struct Assembler *a) { + if (IsPunct(a, a->i, '$')) { + ++a->i; + Emit(a, 0xC2); + EmitWord(a, GetInt(a)); + } else { + Emit(a, 0xC3); + } +} + +static void OnLeave(struct Assembler *a) { + Emit(a, 0xC9); +} +static void OnHlt(struct Assembler *a) { + Emit(a, 0xF4); +} +static void OnCmc(struct Assembler *a) { + Emit(a, 0xF5); +} +static void OnClc(struct Assembler *a) { + Emit(a, 0xF8); +} +static void OnStc(struct Assembler *a) { + Emit(a, 0xF9); +} +static void OnCli(struct Assembler *a) { + Emit(a, 0xFA); +} +static void OnSti(struct Assembler *a) { + Emit(a, 0xFB); +} +static void OnCld(struct Assembler *a) { + Emit(a, 0xFC); +} +static void OnStd(struct Assembler *a) { + Emit(a, 0xFD); +} +static void OnLodsb(struct Assembler *a) { + Emit(a, 0xAC); +} +static void OnLodsw(struct Assembler *a) { + Emit(a, OSZ); + Emit(a, 0xAD); +} +static void OnLodsl(struct Assembler *a) { + Emit(a, 0xAD); +} +static void OnLodsq(struct Assembler *a) { + Emit(a, REXW); + Emit(a, 0xAD); +} +static void OnStosb(struct Assembler *a) { + Emit(a, 0xAA); +} +static void OnStosw(struct Assembler *a) { + Emit(a, OSZ); + Emit(a, 0xAB); +} +static void OnStosl(struct Assembler *a) { + Emit(a, 0xAB); +} +static void OnStosq(struct Assembler *a) { + Emit(a, REXW); + Emit(a, 0xAB); +} +static void OnMovsb(struct Assembler *a) { + Emit(a, 0xA4); +} +static void OnMovsw(struct Assembler *a) { + Emit(a, OSZ); + Emit(a, 0xA5); +} +static void OnMovsl(struct Assembler *a) { + Emit(a, 0xA5); +} +static void OnMovsq(struct Assembler *a) { + Emit(a, REXW); + Emit(a, 0xA5); +} + +static bool Prefix(struct Assembler *a, const char *s, size_t n) { + int i; + char key[8]; + if (n <= 8) { + memset(key, 0, 8); + for (i = 0; i < n; ++i) key[i] = tolower(s[i]); + for (i = 0; i < sizeof(kPrefix) / sizeof(*kPrefix); i++) { + if (!memcmp(key, kPrefix[i], 8)) { + Emit(a, kPrefixByte[i]); + return true; + } + } + } + return false; +} + +static void Directive(struct Assembler *a) { + struct Slice s; + for (;;) { + s = GetString(a); + if (!Prefix(a, s.p, s.n)) break; + } + if (s.n >= 1 && s.p[0] == '.') { + if (IS(s.p, s.n, ".zero")) { + OnZero(a); + } else if (IS(s.p, s.n, ".align") || IS(s.p, s.n, ".balign")) { + OnAlign(a); + } else if (IS(s.p, s.n, ".byte")) { + OnByte(a); + } else if (IS(s.p, s.n, ".word") || IS(s.p, s.n, ".short")) { + OnWord(a); + } else if (IS(s.p, s.n, ".long")) { + OnLong(a); + } else if (IS(s.p, s.n, ".quad")) { + OnQuad(a); + } else if (IS(s.p, s.n, ".float")) { + OnFloat(a); + } else if (IS(s.p, s.n, ".double")) { + OnDouble(a); + } else if (IS(s.p, s.n, ".ascii")) { + OnAscii(a); + } else if (IS(s.p, s.n, ".asciz")) { + OnAsciz(a); + } else if (IS(s.p, s.n, ".text")) { + OnText(a); + } else if (IS(s.p, s.n, ".data")) { + OnData(a); + } else if (IS(s.p, s.n, ".bss")) { + OnBss(a); + } else if (IS(s.p, s.n, ".previous")) { + OnPrevious(a); + } else if (IS(s.p, s.n, ".section")) { + OnSection(a); + } else if (IS(s.p, s.n, ".abort")) { + OnAbort(a); + } else if (IS(s.p, s.n, ".err")) { + OnErr(a); + } else if (IS(s.p, s.n, ".error")) { + OnError(a); + } else if (IS(s.p, s.n, ".fill") || IS(s.p, s.n, ".space")) { + OnFill(a); + } else if (IS(s.p, s.n, ".type")) { + OnType(a); + } else if (IS(s.p, s.n, ".size")) { + OnSize(a); + } else if (IS(s.p, s.n, ".local")) { + OnLocal(a); + } else if (IS(s.p, s.n, ".internal")) { + OnInternal(a); + } else if (IS(s.p, s.n, ".weak")) { + OnWeak(a); + } else if (IS(s.p, s.n, ".hidden")) { + OnHidden(a); + } else if (IS(s.p, s.n, ".globl") || IS(s.p, s.n, ".global")) { + OnGlobal(a); + } else if (IS(s.p, s.n, ".protected")) { + OnProtected(a); + } else if (IS(s.p, s.n, ".incbin")) { + OnIncbin(a); + } else { + Fail(a, "unexpected directive: %.*s", s.n, s.p); + } + } else if (IS(s.p, s.n, "ret")) { + OnRet(a); + } else if (IS(s.p, s.n, "leave")) { + OnLeave(a); + } else if (IS(s.p, s.n, "push")) { + OnPush(a); + } else if (IS(s.p, s.n, "pop")) { + OnPop(a); + } else if (IS(s.p, s.n, "hlt")) { + OnHlt(a); + } else if (IS(s.p, s.n, "cmc")) { + OnCmc(a); + } else if (IS(s.p, s.n, "clc")) { + OnClc(a); + } else if (IS(s.p, s.n, "stc")) { + OnStc(a); + } else if (IS(s.p, s.n, "cli")) { + OnCli(a); + } else if (IS(s.p, s.n, "sti")) { + OnSti(a); + } else if (IS(s.p, s.n, "cld")) { + OnCld(a); + } else if (IS(s.p, s.n, "std")) { + OnStd(a); + } else if (IS(s.p, s.n, "stosb")) { + OnStosb(a); + } else if (IS(s.p, s.n, "stosw")) { + OnStosw(a); + } else if (IS(s.p, s.n, "stosl")) { + OnStosl(a); + } else if (IS(s.p, s.n, "stosq")) { + OnStosq(a); + } else if (IS(s.p, s.n, "lodsb")) { + OnLodsb(a); + } else if (IS(s.p, s.n, "lodsw")) { + OnLodsw(a); + } else if (IS(s.p, s.n, "lodsl")) { + OnLodsl(a); + } else if (IS(s.p, s.n, "lodsq")) { + OnLodsq(a); + } else if (IS(s.p, s.n, "movsb")) { + OnMovsb(a); + } else if (IS(s.p, s.n, "movsw")) { + OnMovsw(a); + } else if (IS(s.p, s.n, "movsl")) { + OnMovsl(a); + } else if (IS(s.p, s.n, "movsq")) { + OnMovsq(a); + } else if (IsSizableOp("mov", s)) { + OnMov(a, s); + } else { + Fail(a, "unexpected op: %.*s", s.n, s.p); + } + ConsumePunct(a, ';'); +} + +static void Assemble(struct Assembler *a) { + while (a->i < a->things.n) { + if (IsPunct(a, a->i, ';')) { + ++a->i; + continue; + } + switch (a->things.p[a->i].t) { + case TT_SLICE: + if (IsPunct(a, a->i + 1, ':')) { + Label(a, a->things.p[a->i].i); + } else { + Directive(a); + } + break; + case TT_INT: + if (IsPunct(a, a->i + 1, ':')) { + LocalLabel(a, a->ints.p[a->things.p[a->i].i]); + } + // fallthrough + default: + Fail(a, "unexpected token"); + } + } +} + +static void Objectify(struct Assembler *a, const char *path) { + size_t i, j; + struct ElfWriter *elf; + elf = elfwriter_open(path, 0644); + for (i = 0; i < a->sections.n; ++i) { + elfwriter_align(elf, a->sections.p[i].align, 0); + elfwriter_startsection(elf, a->sections.p[i].name, a->sections.p[i].type, + a->sections.p[i].flags); + for (j = 0; j < a->symbols.n; ++j) { + if (a->symbols.p[j].section != i) continue; + elfwriter_appendsym( + elf, + strndup(a->slices.p[a->symbols.p[j].name].p, + a->slices.p[a->symbols.p[j].name].n), + ELF64_ST_INFO(a->symbols.p[j].stb, a->symbols.p[j].type), + a->symbols.p[j].stv, a->symbols.p[j].location, a->symbols.p[j].size); + } + memcpy(elfwriter_reserve(elf, a->sections.p[i].binary.n), + a->sections.p[i].binary.p, a->sections.p[i].binary.n); + elfwriter_commit(elf, a->sections.p[i].binary.n); + elfwriter_finishsection(elf); + } + elfwriter_close(elf); +} + +static void PrintThings(struct Assembler *a) { + int i; + char fbuf[32]; + for (i = 0; i < a->things.n; ++i) { + printf("%s:%d:: ", a->sauces.p[a->things.p[i].s].path, + a->sauces.p[a->things.p[i].s].line); + switch (a->things.p[i].t) { + case TT_INT: + printf("TT_INT %ld\n", a->ints.p[a->things.p[i].i]); + break; + case TT_FLOAT: + g_xfmt_p(fbuf, &a->floats.p[a->things.p[i].i], 19, sizeof(fbuf), 0); + printf("TT_FLOAT %s\n", fbuf); + break; + case TT_SLICE: + printf("TT_SLICE %`'.*s\n", a->slices.p[a->things.p[i].i].n, + a->slices.p[a->things.p[i].i].p); + break; + case TT_PUNCT: + printf("TT_PUNCT %`'c\n", a->things.p[i].i); + break; + default: + unreachable; + } + } +} + +void Assembler(int argc, char *argv[]) { + struct Assembler *a; + showcrashreports(); + if (argc == 1) { + system("o//third_party/chibicc/as.com -o /tmp/o third_party/chibicc/hog.s"); + system("objdump -wxd /tmp/o"); + exit(0); + } + ReadFlags(argc, argv); + a = NewAssembler(); + Tokenize(a, g_input_path); + /* PrintThings(a); */ + Assemble(a); + Objectify(a, g_output_path); +} + +int main(int argc, char *argv[]) { + Assembler(argc, argv); + return 0; +} diff --git a/third_party/chibicc/asm.c b/third_party/chibicc/asm.c index 320a7f61..09ea0834 100644 --- a/third_party/chibicc/asm.c +++ b/third_party/chibicc/asm.c @@ -20,6 +20,8 @@ #define PRECIOUS 0b1111000000101000 // bx,bp,r12-r15 +StaticAsm *staticasms; + static const char kGreg[4][16][5] = { {"al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b"}, @@ -31,8 +33,6 @@ static const char kGreg[4][16][5] = { "r11", "r12", "r13", "r14", "r15"}, }; -StaticAsm *staticasms; - static void DecodeAsmConstraints(AsmOperand *op) { int i; char c; diff --git a/third_party/chibicc/chibicc.c b/third_party/chibicc/chibicc.c index 4f0ccab0..7b6ea99d 100644 --- a/third_party/chibicc/chibicc.c +++ b/third_party/chibicc/chibicc.c @@ -590,7 +590,7 @@ static void run_linker(StringArray *inputs, char *output) { run_subprocess(arr.data); } -int main(int argc, char **argv) { +int chibicc(int argc, char **argv) { showcrashreports(); atexit(cleanup); init_macros(); diff --git a/third_party/chibicc/chibicc.h b/third_party/chibicc/chibicc.h index 4cb5bff1..3b33a487 100644 --- a/third_party/chibicc/chibicc.h +++ b/third_party/chibicc/chibicc.h @@ -120,6 +120,10 @@ Token *tokenize_string_literal(Token *, Type *); bool consume(Token **, Token *, char *, size_t); bool equal(Token *, char *, size_t); void convert_pp_tokens(Token *); +void remove_backslash_newline(char *); +void canonicalize_newline(char *); +char *read_file(char *); +int read_escaped_char(char **, char *); #define UNREACHABLE() error("internal error at %s:%d", __FILE__, __LINE__) #define EQUAL(T, S) equal(T, S, strlen(S)) @@ -563,6 +567,8 @@ extern bool opt_sse4; extern bool opt_verbose; extern char *base_file; +int chibicc(int, char **); + // // alloc.c // diff --git a/third_party/chibicc/chibicc.mk b/third_party/chibicc/chibicc.mk index 9ec07c1b..3244a195 100644 --- a/third_party/chibicc/chibicc.mk +++ b/third_party/chibicc/chibicc.mk @@ -20,10 +20,16 @@ PKGS += THIRD_PARTY_CHIBICC THIRD_PARTY_CHIBICC_ARTIFACTS += THIRD_PARTY_CHIBICC_A THIRD_PARTY_CHIBICC = $(THIRD_PARTY_CHIBICC_A_DEPS) $(THIRD_PARTY_CHIBICC_A) THIRD_PARTY_CHIBICC_A = o/$(MODE)/third_party/chibicc/chibicc.a +THIRD_PARTY_CHIBICC2_A = o/$(MODE)/third_party/chibicc/chibicc2.a THIRD_PARTY_CHIBICC_A_FILES := $(wildcard third_party/chibicc/*) THIRD_PARTY_CHIBICC_A_HDRS = $(filter %.h,$(THIRD_PARTY_CHIBICC_A_FILES)) THIRD_PARTY_CHIBICC_A_SRCS = $(filter %.c,$(THIRD_PARTY_CHIBICC_A_FILES)) +THIRD_PARTY_CHIBICC_DEFINES = \ + -DCRT=\"$(CRT)\" \ + -DAPE=\"o/$(MODE)/ape/ape.o\" \ + -DLDS=\"o/$(MODE)/ape/ape.lds\" + THIRD_PARTY_CHIBICC_BINS = \ o/$(MODE)/third_party/chibicc/chibicc.com.dbg \ o/$(MODE)/third_party/chibicc/chibicc.com \ @@ -33,9 +39,13 @@ THIRD_PARTY_CHIBICC_BINS = \ THIRD_PARTY_CHIBICC_A_OBJS = \ $(THIRD_PARTY_CHIBICC_A_SRCS:%=o/$(MODE)/%.zip.o) \ $(THIRD_PARTY_CHIBICC_A_SRCS:%.c=o/$(MODE)/%.o) +THIRD_PARTY_CHIBICC2_A_OBJS = \ + $(THIRD_PARTY_CHIBICC_A_SRCS:%=o/$(MODE)/%.zip.o) \ + $(THIRD_PARTY_CHIBICC_A_SRCS:%.c=o/$(MODE)/%.chibicc.o) THIRD_PARTY_CHIBICC_A_CHECKS = \ $(THIRD_PARTY_CHIBICC_A).pkg \ + $(THIRD_PARTY_CHIBICC2_A).pkg \ $(THIRD_PARTY_CHIBICC_A_HDRS:%=o/$(MODE)/%.ok) THIRD_PARTY_CHIBICC_A_DIRECTDEPS = \ @@ -53,7 +63,9 @@ THIRD_PARTY_CHIBICC_A_DIRECTDEPS = \ LIBC_STUBS \ LIBC_TIME \ LIBC_UNICODE \ + LIBC_SYSV \ LIBC_X \ + TOOL_BUILD_LIB \ THIRD_PARTY_COMPILER_RT \ THIRD_PARTY_DLMALLOC \ THIRD_PARTY_GDTOA @@ -65,39 +77,49 @@ $(THIRD_PARTY_CHIBICC_A): \ third_party/chibicc/ \ $(THIRD_PARTY_CHIBICC_A).pkg \ $(THIRD_PARTY_CHIBICC_A_OBJS) - $(THIRD_PARTY_CHIBICC_A).pkg: \ $(THIRD_PARTY_CHIBICC_A_OBJS) \ $(foreach x,$(THIRD_PARTY_CHIBICC_A_DIRECTDEPS),$($(x)_A).pkg) +$(THIRD_PARTY_CHIBICC2_A): \ + third_party/chibicc/ \ + $(THIRD_PARTY_CHIBICC2_A).pkg \ + $(THIRD_PARTY_CHIBICC2_A_OBJS) +$(THIRD_PARTY_CHIBICC2_A).pkg: \ + $(THIRD_PARTY_CHIBICC2_A_OBJS) \ + $(foreach x,$(THIRD_PARTY_CHIBICC_A_DIRECTDEPS),$($(x)_A).pkg) + o/$(MODE)/third_party/chibicc/chibicc.com.dbg: \ $(THIRD_PARTY_CHIBICC_A_DEPS) \ $(THIRD_PARTY_CHIBICC_A) \ $(APE) \ $(CRT) \ - o/$(MODE)/third_party/chibicc/chibicc.o \ + o/$(MODE)/third_party/chibicc/main.o \ + $(THIRD_PARTY_CHIBICC_A).pkg + @$(APELINK) +o/$(MODE)/third_party/chibicc/chibicc2.com.dbg: \ + $(THIRD_PARTY_CHIBICC_A_DEPS) \ + $(THIRD_PARTY_CHIBICC2_A) \ + $(APE) \ + $(CRT) \ + o/$(MODE)/third_party/chibicc/main.chibicc.o \ + $(THIRD_PARTY_CHIBICC2_A).pkg + @$(APELINK) + +o/$(MODE)/third_party/chibicc/as.com.dbg: \ + $(THIRD_PARTY_CHIBICC_A_DEPS) \ + $(THIRD_PARTY_CHIBICC_A) \ + $(APE) \ + $(CRT) \ + o/$(MODE)/third_party/chibicc/as.o \ $(THIRD_PARTY_CHIBICC_A).pkg @$(APELINK) -o/$(MODE)/third_party/chibicc/chibicc2.com.dbg: \ - $(THIRD_PARTY_CHIBICC_A_DEPS) \ - $(THIRD_PARTY_CHIBICC_A_SRCS:%.c=o/$(MODE)/%.chibicc.o) \ - $(THIRD_PARTY_CHIBICC_A).pkg \ - $(CRT) \ - $(APE) - @$(APELINK) - o/$(MODE)/third_party/chibicc/chibicc.o: \ - CPPFLAGS += \ - -DCRT=\"$(CRT)\" \ - -DAPE=\"o/$(MODE)/ape/ape.o\" \ - -DLDS=\"o/$(MODE)/ape/ape.lds\" + CPPFLAGS += $(THIRD_PARTY_CHIBICC_DEFINES) o/$(MODE)/third_party/chibicc/chibicc.chibicc.o: \ - CHIBICC_FLAGS += \ - -DCRT=\"$(CRT)\" \ - -DAPE=\"o/$(MODE)/ape/ape.o\" \ - -DLDS=\"o/$(MODE)/ape/ape.lds\" + CHIBICC_FLAGS += $(THIRD_PARTY_CHIBICC_DEFINES) o/$(MODE)/%.chibicc.o: %.c o/$(MODE)/third_party/chibicc/chibicc.com.dbg @ACTION=CHIBICC TARGET=$@ build/do $(CHIBICC) $(CHIBICC_FLAGS) -c -o $@ $< diff --git a/third_party/chibicc/codegen.c b/third_party/chibicc/codegen.c index 1c5fef7c..86a71116 100644 --- a/third_party/chibicc/codegen.c +++ b/third_party/chibicc/codegen.c @@ -1770,9 +1770,9 @@ void gen_expr(Node *node) { println("\tdiv\t%s", di); } else { if (node->lhs->ty->size == 8) { - emitlin("\tcqo"); + emitlin("\tcqto"); } else { - emitlin("\tcdq"); + emitlin("\tcltd"); } println("\tidiv\t%s", di); } diff --git a/third_party/chibicc/main.c b/third_party/chibicc/main.c new file mode 100644 index 00000000..05a2b009 --- /dev/null +++ b/third_party/chibicc/main.c @@ -0,0 +1,5 @@ +#include "third_party/chibicc/chibicc.h" + +int main(int argc, char **argv) { + return chibicc(argc, argv); +} diff --git a/third_party/chibicc/test/macro_test.c b/third_party/chibicc/test/macro_test.c index d2bfb664..83977202 100644 --- a/third_party/chibicc/test/macro_test.c +++ b/third_party/chibicc/test/macro_test.c @@ -5,6 +5,8 @@ char *main_filename1 = __FILE__; int main_line1 = __LINE__; #define LINE() __LINE__ int main_line2 = LINE(); +#define add2 add2 +#define add6(a, b, c, d, e, f) add6(a, b, c, d, e, f) # diff --git a/third_party/chibicc/test/test.mk b/third_party/chibicc/test/test.mk index bf46daeb..dacc120a 100644 --- a/third_party/chibicc/test/test.mk +++ b/third_party/chibicc/test/test.mk @@ -79,7 +79,6 @@ o/$(MODE)/third_party/chibicc/test/%.com.dbg: \ $(THIRD_PARTY_CHIBICC_TEST_A) \ o/$(MODE)/third_party/chibicc/test/%.chibicc.o \ $(THIRD_PARTY_CHIBICC_TEST_A).pkg \ - $(LIBC_TESTMAIN) \ $(CRT) \ $(APE) @$(APELINK) @@ -89,7 +88,6 @@ o/$(MODE)/third_party/chibicc/test/%2.com.dbg: \ $(THIRD_PARTY_CHIBICC_TEST2_A) \ o/$(MODE)/third_party/chibicc/test/%.chibicc2.o \ $(THIRD_PARTY_CHIBICC_TEST2_A).pkg \ - $(LIBC_TESTMAIN) \ $(CRT) \ $(APE) @$(APELINK) diff --git a/third_party/chibicc/tokenize.c b/third_party/chibicc/tokenize.c index 1e2eb576..a4b44fb2 100644 --- a/third_party/chibicc/tokenize.c +++ b/third_party/chibicc/tokenize.c @@ -128,7 +128,7 @@ static int read_ident(char *start) { for (;;) { char *q; c = decode_utf8(&q, p); - if (!('a' <= c && c <= 'f') && !is_ident2(c)) { + if (!is_ident2(c)) { return p - start; } p = q; @@ -142,13 +142,14 @@ static int from_hex(char c) { } // Read a punctuator token from p and returns its length. -static int read_punct(char *p) { - static char *kw[] = {"<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", - "+=", "-=", "*=", "/=", "++", "--", "%=", "&=", - "|=", "^=", "&&", "||", "<<", ">>", "##"}; +int read_punct(char *p) { + static char kw[][4] = {"<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", + "+=", "-=", "*=", "/=", "++", "--", "%=", "&=", + "|=", "^=", "&&", "||", "<<", ">>", "##"}; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) { - if (startswith(p, kw[i])) { - return strlen(kw[i]); + for (int j = 0;;) { + if (p[j] != kw[i][j]) break; + if (!kw[i][++j]) return j; } } return ispunct(*p) ? 1 : 0; @@ -181,7 +182,7 @@ static bool is_keyword(Token *tok) { return hashmap_get2(&map, tok->loc, tok->len); } -static int read_escaped_char(char **new_pos, char *p) { +int read_escaped_char(char **new_pos, char *p) { if ('0' <= *p && *p <= '7') { // Read an octal number. unsigned c = *p++ - '0'; @@ -592,7 +593,7 @@ Token *tokenize(File *file) { } // Returns the contents of a given file. -static char *read_file(char *path) { +char *read_file(char *path) { FILE *fp; if (strcmp(path, "-") == 0) { // By convention, read from stdin if a given filename is "-". @@ -639,7 +640,7 @@ File *new_file(char *name, int file_no, char *contents) { } // Replaces \r or \r\n with \n. -static void canonicalize_newline(char *p) { +void canonicalize_newline(char *p) { int i = 0, j = 0; while (p[i]) { if (p[i] == '\r' && p[i + 1] == '\n') { @@ -656,7 +657,7 @@ static void canonicalize_newline(char *p) { } // Removes backslashes followed by a newline. -static void remove_backslash_newline(char *p) { +void remove_backslash_newline(char *p) { int i = 0, j = 0; // We want to keep the number of newline characters so that // the logical line number matches the physical one. diff --git a/third_party/chibicc/unicode.c b/third_party/chibicc/unicode.c index 0ff0b840..385a0392 100644 --- a/third_party/chibicc/unicode.c +++ b/third_party/chibicc/unicode.c @@ -61,8 +61,11 @@ uint32_t decode_utf8(char **new_pos, char *p) { } static bool in_range(uint32_t *range, uint32_t c) { - for (int i = 0; range[i] != -1; i += 2) - if (range[i] <= c && c <= range[i + 1]) return true; + for (int i = 0; range[i] != -1; i += 2) { + if (range[i] <= c && c <= range[i + 1]) { + return true; + } + } return false; } @@ -78,7 +81,6 @@ static bool in_range(uint32_t *range, uint32_t c) { // (U+3000, full-width space) are allowed because they are out of range. bool is_ident1(uint32_t c) { static uint32_t range[] = { - 'a', 'z', 'A', 'Z', '_', '_', '$', '$', 0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF, 0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6, 0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F, @@ -93,17 +95,26 @@ bool is_ident1(uint32_t c) { 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD, 0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD, -1, }; - return in_range(range, c); + if (c < 128) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_' || + c == '$'; + } else { + return in_range(range, c); + } } // Returns true if a given character is acceptable as a non-first // character of an identifier. bool is_ident2(uint32_t c) { static uint32_t range[] = { - '0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, - 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F, -1, + 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F, -1, }; - return is_ident1(c) || in_range(range, c); + if (is_ident1(c)) return true; + if (c < 128) { + return '0' <= c && c <= '9'; + } else { + return in_range(range, c); + } } // Returns the number of columns needed to display a given diff --git a/third_party/musl/tempnam.c b/third_party/musl/tempnam.c new file mode 100644 index 00000000..c20af821 --- /dev/null +++ b/third_party/musl/tempnam.c @@ -0,0 +1,84 @@ +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/struct/timespec.h" +#include "libc/errno.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/clock.h" +#include "libc/time/time.h" +#include "third_party/musl/tempnam.h" + +#define MAXTRIES 100 + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); + +static char *__randname(char *template) { + int i; + struct timespec ts; + unsigned long r; + clock_gettime(CLOCK_REALTIME, &ts); + r = ts.tv_nsec * 65537 ^ (uintptr_t)&ts / 16 + (uintptr_t) template; + for (i = 0; i < 6; i++, r >>= 5) template[i] = 'A' + (r & 15) + (r & 16) * 2; + return template; +} + +/** + * Creates name for temporary file. + */ +char *tempnam(const char *dir, const char *pfx) { + int i, r; + char s[PATH_MAX]; + size_t l, dl, pl; + if (!dir) dir = kTmpPath; + if (!pfx) pfx = "temp"; + dl = strlen(dir); + pl = strlen(pfx); + l = dl + 1 + pl + 1 + 6; + if (l >= PATH_MAX) { + errno = ENAMETOOLONG; + return 0; + } + memcpy(s, dir, dl); + s[dl] = '/'; + memcpy(s + dl + 1, pfx, pl); + s[dl + 1 + pl] = '_'; + s[l] = 0; + for (i = 0; i < MAXTRIES; i++) { + __randname(s + l - 6); + r = fstatat(AT_FDCWD, s, &(struct stat){0}, AT_SYMLINK_NOFOLLOW); + if (r == -ENOENT) return strdup(s); + } + return 0; +} diff --git a/third_party/musl/tempnam.h b/third_party/musl/tempnam.h new file mode 100644 index 00000000..7ff9794b --- /dev/null +++ b/third_party/musl/tempnam.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_MUSL_TEMPNAM_H_ +#define COSMOPOLITAN_THIRD_PARTY_MUSL_TEMPNAM_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +char *tempnam(const char *, const char *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_MUSL_TEMPNAM_H_ */ diff --git a/tool/build/blinkenlights.c b/tool/build/blinkenlights.c index a9d6f27b..fb853141 100644 --- a/tool/build/blinkenlights.c +++ b/tool/build/blinkenlights.c @@ -2497,7 +2497,8 @@ static void Exec(void) { KeepGoing: CheckFramePointer(); if (action & ALARM) { - DrawDisplayOnly(&pan.display); + /* TODO(jart): Fix me */ + /* DrawDisplayOnly(&pan.display); */ action &= ~ALARM; } if (action & EXIT) { diff --git a/tool/build/build.mk b/tool/build/build.mk index b90590be..e60120f4 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -53,12 +53,12 @@ TOOL_BUILD_DIRECTDEPS = \ LIBC_TINYMATH \ LIBC_UNICODE \ LIBC_X \ + TOOL_BUILD_LIB \ THIRD_PARTY_COMPILER_RT \ THIRD_PARTY_GDTOA \ THIRD_PARTY_GETOPT \ THIRD_PARTY_XED \ THIRD_PARTY_ZLIB \ - TOOL_BUILD_LIB \ THIRD_PARTY_STB TOOL_BUILD_DEPS := \ diff --git a/tool/build/lib/elfwriter.c b/tool/build/lib/elfwriter.c index 33b8c1e6..25bd7182 100644 --- a/tool/build/lib/elfwriter.c +++ b/tool/build/lib/elfwriter.c @@ -129,7 +129,7 @@ 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); + AppendSection(elf, name, SHT_STRTAB, 0); mempcpy(elfwriter_reserve(elf, size), strtab->p, size); elfwriter_commit(elf, size); return FinishSection(elf); @@ -172,6 +172,9 @@ struct ElfWriter *elfwriter_open(const char *path, int mode) { elf->ehdr = memcpy(elf->map, &kObjHeader, (elf->wrote = sizeof(kObjHeader))); elf->strtab = newinterner(); elf->shstrtab = newinterner(); + intern(elf->strtab, ""); + intern(elf->shstrtab, ""); + intern(elf->shstrtab, ".shstrtab"); return elf; } diff --git a/tool/build/lib/elfwriter_cargoculting.c b/tool/build/lib/elfwriter_cargoculting.c index ff217b9e..04afa588 100644 --- a/tool/build/lib/elfwriter_cargoculting.c +++ b/tool/build/lib/elfwriter_cargoculting.c @@ -22,6 +22,7 @@ void elfwriter_cargoculting(struct ElfWriter *elf) { elfwriter_startsection(elf, "", SHT_NULL, 0); + elfwriter_finishsection(elf); elfwriter_startsection(elf, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); elfwriter_finishsection(elf); elfwriter_startsection(elf, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); diff --git a/tool/viz/life.c b/tool/viz/life.c index d58704c6..7c7659f2 100644 --- a/tool/viz/life.c +++ b/tool/viz/life.c @@ -1386,7 +1386,7 @@ int main(int argc, char *argv[]) { return 1; } } - if (0 && IsWindows()) { + if (IsWindows()) { Gui(); } else { Tui();