diff --git a/Dockerfile b/Dockerfile index c47c2eb..abd04e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -294,6 +294,9 @@ RUN $ARCH-gcc -DEXE=gcc.exe -DCMD=cc \ && $ARCH-gcc -DEXE=gcc.exe -DCMD="cc -std=c99" \ -Os -fno-asynchronous-unwind-tables -Wl,--gc-sections -s -nostdlib \ -o $PREFIX/bin/c99.exe $PREFIX/src/alias.c -lkernel32 \ + && $ARCH-gcc -DEXE=gcc.exe -DCMD="cc -ansi" \ + -Os -fno-asynchronous-unwind-tables -Wl,--gc-sections -s -nostdlib \ + -o $PREFIX/bin/c89.exe $PREFIX/src/alias.c -lkernel32 \ && printf '%s\n' addr2line ar as c++filt cpp dlltool dllwrap elfedit g++ \ gcc gcc-ar gcc-nm gcc-ranlib gcov gcov-dump gcov-tool ld nm objcopy \ objdump ranlib readelf size strings strip windmc windres \ @@ -375,7 +378,7 @@ RUN /make-$MAKE_VERSION/configure \ -o $PREFIX/bin/mingw32-make.exe $PREFIX/src/alias.c -lkernel32 WORKDIR /busybox-w32 -COPY src/busybox-*.patch $PREFIX/src/ +COPY src/busybox-* $PREFIX/src/ RUN cat $PREFIX/src/busybox-*.patch | patch -p1 \ && make mingw64_defconfig \ && sed -ri 's/^(CONFIG_AR)=y/\1=n/' .config \ @@ -398,7 +401,9 @@ RUN cat $PREFIX/src/busybox-*.patch | patch -p1 \ && cp busybox.exe $PREFIX/bin/ # Create BusyBox command aliases (like "busybox --install") -RUN printf '%s\n' arch ash awk base32 base64 basename bash bc bunzip2 bzcat \ +RUN $ARCH-gcc -Os -fno-asynchronous-unwind-tables -Wl,--gc-sections -s \ + -nostdlib -o alias.exe $PREFIX/src/busybox-alias.c -lkernel32 \ + && printf '%s\n' arch ash awk base32 base64 basename bash bc bunzip2 bzcat \ bzip2 cal cat chattr chmod cksum clear cmp comm cp cpio crc32 cut date \ dc dd df diff dirname dos2unix du echo ed egrep env expand expr factor \ false fgrep find fold free fsync getopt grep groups gunzip gzip hd \ @@ -411,11 +416,7 @@ RUN printf '%s\n' arch ash awk base32 base64 basename bash bc bunzip2 bzcat \ tr true truncate ts ttysize uname uncompress unexpand uniq unix2dos \ unlzma unlzop unxz unzip uptime usleep uudecode uuencode watch \ wc wget which whoami whois xargs xz xzcat yes zcat \ - | xargs -I{} -P$(nproc) \ - $ARCH-gcc -DEXE=busybox.exe -DCMD={} \ - -Os -fno-asynchronous-unwind-tables \ - -Wl,--gc-sections -s -nostdlib \ - -o $PREFIX/bin/{}.exe $PREFIX/src/alias.c -lkernel32 + | xargs -I{} cp alias.exe $PREFIX/bin/{}.exe # TODO: Either somehow use $VIM_VERSION or normalize the workdir WORKDIR /vim90/src diff --git a/src/alias.c b/src/alias.c index 8280b0d..0b4ca72 100644 --- a/src/alias.c +++ b/src/alias.c @@ -3,94 +3,105 @@ // Unlike batch script aliases, this program will not produce an annoying // and useless "Terminate batch job (Y/N)" prompt. When compiling, define // EXE as the target executable (relative or absolute path), and define CMD -// as the argv[0] replacement, including additional arguments. Example: +// as the argv[0] replacement, including additional arguments. // // $ gcc -DEXE="target.exe" -DCMD="argv0 argv1" -// -Os -fno-asynchronous-unwind-tables -// -s -nostartfiles -Wl,--gc-sections -o alias.exe alias.c +// -nostartfiles -fno-builtin -o alias.exe alias.c // // This is free and unencumbered software released into the public domain. -// Win32 declarations -// NOTE: Parsing windows.h was by far the slowest part of the build, and -// this program is compiled hundreds times for w64devkit. So instead, -// define just what's needed. +#define sizeof(x) (i32)sizeof(x) +#define alignof(x) (i32)_Alignof(x) +#define countof(a) (sizeof(a) / sizeof(*(a))) +#define lengthof(s) (countof(s) - 1) -#define MAX_PATH 260 - -typedef __SIZE_TYPE__ size_t; -typedef unsigned short char16_t; - -typedef struct { - int cb; - void *a, *b, *c; - int d, e, f, g, h, i, j, k; - short l, m; - void *n, *o, *p, *q; -} StartupInfo; - -typedef struct { - void *process; - void *thread; - int pid; - int tid; -} ProcessInformation; - -int CreateProcessW( - void *, void *, void *, void *, int, int, void *, void *, void *, void * -) __attribute((dllimport,stdcall)); -void ExitProcess(int) - __attribute((dllimport,stdcall)); -char16_t *GetCommandLineW(void) - __attribute((dllimport,stdcall)); -int GetExitCodeProcess(void *, int *) - __attribute((dllimport,stdcall)); -int GetModuleFileNameW(void *, char16_t *, int) - __attribute((dllimport,stdcall)); -void *GetStdHandle(int) - __attribute((dllimport,stdcall)); -int lstrlenW(void *) - __attribute((dllimport,stdcall)); -void *VirtualAlloc(void *, size_t, int, int) - __attribute__((dllimport,stdcall,malloc)); -int WriteFile(void *, void *, int, int *, void *) - __attribute((dllimport,stdcall)); -int WaitForSingleObject(void *, int) - __attribute((dllimport,stdcall)); - -// Application - -#define FATAL "fatal: w64devkit alias failed: " -#define TOOLONG "command too long\n" -#define OOM "out of memory\n" -#define CREATEPROC "cannot create process\n" -#define COUNTOF(a) (int)(sizeof(a) / sizeof(0[a])) #define LSTR(s) XSTR(s) -#define XSTR(s) L ## # s +#define XSTR(s) u ## # s #define LEXE LSTR(EXE) #define LCMD LSTR(CMD) -#define STRBUF(buf, cap) {buf, cap, 0, 0} -typedef struct { - char16_t *buf; - int cap; - int len; - int error; -} StrBuf; +typedef __UINT8_TYPE__ u8; +typedef signed short i16; +typedef signed int b32; +typedef signed int i32; +typedef unsigned int u32; +typedef unsigned char byte; +typedef __UINTPTR_TYPE__ uptr; +typedef __SIZE_TYPE__ usize; +typedef unsigned short char16_t; // for GDB +typedef char16_t c16; -static void append(StrBuf *b, char16_t *s, int len) +// Win32 + +#define MAX_PATH 260 +#define MAX_CMDLINE (1<<15) + +typedef struct {} *handle; + +typedef struct { + u32 cb; + uptr a, b, c; + i32 d, e, f, g, h, i, j, k; + i16 l, m; + uptr n, o, p, q; +} si; + +typedef struct { + handle process; + handle thread; + u32 pid; + u32 tid; +} pi; + +#define W32 __attribute((dllimport, stdcall)) +W32 b32 CreateProcessW(c16*,c16*,void*,void*,b32,u32,c16*,c16*,si*,pi*); +W32 void ExitProcess(u32) __attribute((noreturn)); +W32 c16 *GetCommandLineW(void); +W32 i32 GetExitCodeProcess(handle, u32 *); +W32 u32 GetFullPathNameW(c16 *, u32, c16 *, c16 *); +W32 u32 GetModuleFileNameW(handle, c16 *, u32); +W32 handle GetStdHandle(u32); +W32 byte *VirtualAlloc(byte *, usize, u32, u32); +W32 b32 VirtualFree(byte *, usize, u32); +W32 u32 WaitForSingleObject(handle, u32); +W32 b32 WriteFile(handle, u8 *, u32, u32 *, void *); + +// Application + +#define ERR(s) "w64devkit (alias): " s "\n" + +#define new(h, t, n) (t *)alloc(h, sizeof(t), alignof(t), n) +__attribute((malloc)) +__attribute((alloc_align(3))) +__attribute((alloc_size(2, 4))) +static byte *alloc(byte **heap, i32 size, i32 align, i32 count) { - int avail = b->cap - b->len; - int count = lencap - b->len; + i32 count = availbuf[b->len+i] = s[i]; } b->len += count; - b->error |= len > avail; + b->err |= avail < len; } // Find the end of argv[0]. -static char16_t *findargs(char16_t *s) +static c16 *findargs(c16 *s) { if (s[0] == '"') { for (s++;; s++) { // quoted argv[0] @@ -110,80 +121,105 @@ static char16_t *findargs(char16_t *s) } } -// Return the directory component length including the last slash. -static int dirname(char16_t *s) +static i32 c16len(c16 *s) { - int len = 0; - for (int i = 0; s[i]; i++) { - if (s[i]=='/' || s[i]=='\\') { - len = i + 1; - } - } + i32 len = 0; + for (; s[len]; len++) {} return len; } -static void fail(char *reason, int len) +static u32 fatal(u8 *msg, i32 len) { - int dummy; - void *out = GetStdHandle(-12); - WriteFile(out, FATAL, COUNTOF(FATAL), &dummy, 0); - WriteFile(out, reason, len, &dummy, 0); + handle stderr = GetStdHandle(-12); + u32 dummy; + WriteFile(stderr, msg, len, &dummy, 0); + return 0x17e; } -static int aliasmain(void) +static void getmoduledir(c16buf *b) { - // Replace alias module with adjacent target - char16_t exebuf[MAX_PATH]; - StrBuf exe = STRBUF(exebuf, COUNTOF(exebuf)); - if (LEXE[1] == ':') { - // EXE looks like an absolute path - append(&exe, LEXE, COUNTOF(LEXE)); - } else { - // EXE looks like a relative path - char16_t module[MAX_PATH]; - GetModuleFileNameW(0, module, COUNTOF(module)); - int len = dirname(module); - append(&exe, module, len); - append(&exe, LEXE, COUNTOF(LEXE)); + c16 path[MAX_PATH]; + u32 len = GetModuleFileNameW(0, path, countof(path)); + for (; len; len--) { + switch (path[len-1]) { + case '/': case '\\': + append(b, path, len); + return; + } + } +} + +static si *newstartupinfo(byte **heap) +{ + si *s = new(heap, si, 1); + s->cb = sizeof(si); + return s; +} + +static i32 aliasmain(void) +{ + byte *heap_start = VirtualAlloc(0, 1<<18, 0x3000, 4); + byte *heap = heap_start; + if (!heap) { + static const u8 msg[] = ERR("out of memory"); + return fatal((u8 *)msg, lengthof(msg)); + } + + // Construct a path to the .exe + c16buf *exe = new(&heap, c16buf, 1); + exe->cap = 2*MAX_PATH; // concatenating two paths + exe->buf = new(&heap, c16, exe->cap); + if (LEXE[1] != ':') { // relative path? + getmoduledir(exe); + } + append(exe, LEXE, countof(LEXE)); + if (exe->err) { + static const u8 msg[] = ERR(".exe path too long"); + return fatal((u8 *)msg, lengthof(msg)); + } + + // Try to collapse relative components + if (LEXE[0] == '.') { // relative components? + c16buf *tmp = new(&heap, c16buf, 1); + tmp->buf = new(&heap, c16, MAX_PATH); + tmp->cap = MAX_PATH; + tmp->len = GetFullPathNameW(exe->buf, tmp->cap, tmp->buf, 0); + if (tmp->len>0 && tmp->lencap) { + tmp->len++; // include null terminator + *exe = *tmp; + } } // Construct a new command line string - int cmdcap = 1<<15; - char16_t *cmdbuf = VirtualAlloc(0, 2*cmdcap, 0x3000, 4); - if (!cmdbuf) { - fail(OOM, COUNTOF(OOM)-1); - return -2; - } - StrBuf cmd = STRBUF(cmdbuf, cmdcap); - append(&cmd, LCMD, COUNTOF(LCMD)-1); - char16_t *args = findargs(GetCommandLineW()); - append(&cmd, args, lstrlenW(args)+1); - - if (exe.error || cmd.error) { - fail(TOOLONG, COUNTOF(TOOLONG)-1); - return -3; + c16buf *cmd = new(&heap, c16buf, 1); + cmd->cap = MAX_CMDLINE; + cmd->buf = new(&heap, c16, cmd->cap); + append(cmd, LCMD, lengthof(LCMD)); + c16 *args = findargs(GetCommandLineW()); + append(cmd, args, c16len(args)+1); + if (cmd->err) { + static const u8 msg[] = ERR("command line too long"); + return fatal((u8 *)msg, lengthof(msg)); } - StartupInfo si = {0}; - si.cb = sizeof(si); - ProcessInformation pi; - if (!CreateProcessW(exebuf, cmdbuf, 0, 0, 1, 0, 0, 0, &si, &pi)) { - fail(CREATEPROC, COUNTOF(CREATEPROC)-1); - return -4; + si *si = newstartupinfo(&heap); + pi pi; + if (!CreateProcessW(exe->buf, cmd->buf, 0, 0, 1, 0, 0, 0, si, &pi)) { + static const u8 msg[] = ERR("could not start process\n"); + return fatal((u8 *)msg, lengthof(msg)); } - int ret; + // Wait for child to exit + VirtualFree(heap_start, 0, 0x8000); + u32 ret; WaitForSingleObject(pi.process, -1); GetExitCodeProcess(pi.process, &ret); return ret; } -#if __i386 __attribute((force_align_arg_pointer)) -#endif -__attribute((externally_visible)) -int mainCRTStartup(void) +void mainCRTStartup(void) { - int r = aliasmain(); + u32 r = aliasmain(); ExitProcess(r); } diff --git a/src/busybox-alias.c b/src/busybox-alias.c new file mode 100644 index 0000000..861c9a2 --- /dev/null +++ b/src/busybox-alias.c @@ -0,0 +1,122 @@ +// busybox-w32 command alias +// Starts the adjacent busybox.exe with an unmodified command line. +// $ cc -nostartfiles -fno-builtin -o COMMAND busybox-alias.c +// This is free and unencumbered software released into the public domain. + +#define sizeof(x) (i32)sizeof(x) +#define countof(a) (sizeof(a) / sizeof(*(a))) +#define lengthof(s) (countof(s) - 1) + +typedef __UINT8_TYPE__ u8; +typedef signed short i16; +typedef signed int b32; +typedef signed int i32; +typedef unsigned int u32; +typedef unsigned short char16_t; // for GDB +typedef char16_t c16; + +#define MAX_PATH 260 + +typedef struct {} *handle; + +typedef struct { + u32 cb; + void *a, *b, *c; + i32 d, e, f, g, h, i, j, k; + i16 l, m; + void *n, *o, *p, *q; +} si; + +typedef struct { + handle process; + handle thread; + u32 pid; + u32 tid; +} pi; + +#define W32 __attribute((dllimport, stdcall)) +W32 b32 CreateProcessW(c16*,c16*,void*,void*,b32,u32,c16*,c16*,si*,pi*); +W32 void ExitProcess(u32) __attribute((noreturn)); +W32 c16 *GetCommandLineW(void); +W32 i32 GetExitCodeProcess(handle, u32 *); +W32 u32 GetModuleFileNameW(handle, c16 *, u32); +W32 handle GetStdHandle(u32); +W32 u32 WaitForSingleObject(handle, u32); +W32 b32 WriteFile(handle, u8 *, u32, u32 *, void *); + +typedef struct { + c16 *buf; + i32 len; + i32 cap; + b32 err; +} c16buf; + +static void append(c16buf *b, c16 *buf, i32 len) +{ + i32 avail = b->cap - b->len; + i32 count = availbuf + b->len; + for (i32 i = 0; i < count; i++) { + dst[i] = buf[i]; + } + b->len += count; + b->err |= count < len; +} + +static void getmoduledir(c16buf *b) +{ + c16 path[MAX_PATH]; + u32 len = GetModuleFileNameW(0, path, countof(path)); + for (; len; len--) { + switch (path[len-1]) { + case '/': case '\\': + append(b, path, len-1); + return; + } + } +} + +static u32 fatal(u8 *msg, i32 len) +{ + handle stderr = GetStdHandle(-12); + u32 dummy; + WriteFile(stderr, msg, len, &dummy, 0); + return 0x17e; +} + +static u32 run(void) +{ + c16 buf[MAX_PATH]; + c16buf exe = {}; + exe.buf = buf; + exe.cap = countof(buf); + + getmoduledir(&exe); + c16 busybox[] = u"\\busybox.exe"; + append(&exe, busybox, countof(busybox)); + if (exe.err) { + static u8 msg[] = "w64devkit: busybox.exe path too long\n"; + return fatal(msg, lengthof(msg)); + } + + si si = {}; + si.cb = sizeof(si); + pi pi; + c16 *cmdline = GetCommandLineW(); + if (!CreateProcessW(exe.buf, cmdline, 0, 0, 1, 0, 0, 0, &si, &pi)) { + static u8 msg[] = "w64devkit: could not start busybox.exe\n"; + return fatal(msg, lengthof(msg)); + } + + u32 ret; + WaitForSingleObject(pi.process, -1); + GetExitCodeProcess(pi.process, &ret); + return ret; +} + +__attribute((force_align_arg_pointer)) +void mainCRTStartup(void) +{ + u32 r = run(); + ExitProcess(r); +}