├── .gitattributes ├── Dockerfile ├── README.md ├── UNLICENSE ├── contrib ├── README.md ├── cppcheck.mak ├── libgc.c ├── libregex.c └── llama.mak ├── multibuild.sh └── src ├── 7z.mak ├── SHA256SUMS ├── alias.c ├── binutils-dlltool-zero-ordinals.patch ├── binutils-exclude-sysroot.patch ├── busybox-000-disable-beep.patch ├── busybox-001-disable-empty-complete.patch ├── busybox-002-default-noiconify.patch ├── busybox-004-system-profile.patch ├── busybox-005-disable-utf8-check.patch ├── busybox-alias.c ├── crossgcc-relocatable.patch ├── debugbreak.c ├── gcc-avx-misaligned.patch ├── gcc-stdcall-align.patch ├── gcc-trap-terminate.patch ├── gdb-000-alternate-main.patch ├── gdb-001-confirm-off.patch ├── gendef-silent.patch ├── libchkstk.S ├── libmemory.c ├── peports.c ├── pkg-config.c ├── profile ├── rexxd.c ├── variant-x86.patch ├── vc++filt.c ├── w64devkit.c ├── w64devkit.ico └── w64devkit.ini /.gitattributes: -------------------------------------------------------------------------------- 1 | * -crlf 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm-slim 2 | 3 | ARG VERSION=2.2.0 4 | ARG PREFIX=/w64devkit 5 | ARG Z7_VERSION=2301 6 | ARG BINUTILS_VERSION=2.44 7 | ARG BUSYBOX_VERSION=FRP-5579-g5749feb35 8 | ARG CTAGS_VERSION=6.0.0 9 | ARG EXPAT_VERSION=2.7.0 10 | ARG GCC_VERSION=15.1.0 11 | ARG GDB_VERSION=16.2 12 | ARG GMP_VERSION=6.3.0 13 | ARG LIBICONV_VERSION=1.18 14 | ARG MAKE_VERSION=4.4.1 15 | ARG MINGW_VERSION=13.0.0 16 | ARG MPC_VERSION=1.3.1 17 | ARG MPFR_VERSION=4.2.2 18 | ARG PDCURSES_VERSION=3.9 19 | ARG VIM_VERSION=9.0 20 | 21 | RUN apt-get update && apt-get install --yes --no-install-recommends \ 22 | build-essential curl libgmp-dev libmpc-dev libmpfr-dev m4 p7zip-full 23 | 24 | # Download, verify, and unpack 25 | 26 | RUN curl --insecure --location --remote-name-all --remote-header-name \ 27 | https://downloads.sourceforge.net/project/sevenzip/7-Zip/23.01/7z$Z7_VERSION-src.tar.xz \ 28 | https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.xz \ 29 | https://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.xz \ 30 | https://ftp.gnu.org/gnu/gdb/gdb-$GDB_VERSION.tar.xz \ 31 | https://downloads.sourceforge.net/project/expat/expat/$EXPAT_VERSION/expat-$EXPAT_VERSION.tar.xz \ 32 | https://ftp.gnu.org/gnu/gmp/gmp-$GMP_VERSION.tar.xz \ 33 | https://ftp.gnu.org/gnu/mpc/mpc-$MPC_VERSION.tar.gz \ 34 | https://ftp.gnu.org/gnu/mpfr/mpfr-$MPFR_VERSION.tar.xz \ 35 | https://ftp.gnu.org/gnu/make/make-$MAKE_VERSION.tar.gz \ 36 | https://ftp.gnu.org/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz \ 37 | https://frippery.org/files/busybox/busybox-w32-$BUSYBOX_VERSION.tgz \ 38 | https://ftp.nluug.nl/pub/vim/unix/vim-$VIM_VERSION.tar.bz2 \ 39 | https://github.com/universal-ctags/ctags/archive/refs/tags/v$CTAGS_VERSION.tar.gz \ 40 | https://downloads.sourceforge.net/project/mingw-w64/mingw-w64/mingw-w64-release/mingw-w64-v$MINGW_VERSION.tar.bz2 \ 41 | https://downloads.sourceforge.net/project/pdcurses/pdcurses/$PDCURSES_VERSION/PDCurses-$PDCURSES_VERSION.tar.gz 42 | COPY src/SHA256SUMS $PREFIX/src/ 43 | RUN sha256sum -c $PREFIX/src/SHA256SUMS \ 44 | && tar xJf 7z$Z7_VERSION-src.tar.xz --xform 's%^%7z/%' \ 45 | && tar xJf binutils-$BINUTILS_VERSION.tar.xz \ 46 | && tar xzf busybox-w32-$BUSYBOX_VERSION.tgz \ 47 | && tar xzf ctags-$CTAGS_VERSION.tar.gz \ 48 | && tar xJf gcc-$GCC_VERSION.tar.xz \ 49 | && tar xJf gdb-$GDB_VERSION.tar.xz \ 50 | && tar xJf expat-$EXPAT_VERSION.tar.xz \ 51 | && tar xzf libiconv-$LIBICONV_VERSION.tar.gz \ 52 | && tar xJf gmp-$GMP_VERSION.tar.xz \ 53 | && tar xzf mpc-$MPC_VERSION.tar.gz \ 54 | && tar xJf mpfr-$MPFR_VERSION.tar.xz \ 55 | && tar xzf make-$MAKE_VERSION.tar.gz \ 56 | && tar xjf mingw-w64-v$MINGW_VERSION.tar.bz2 \ 57 | && tar xzf PDCurses-$PDCURSES_VERSION.tar.gz \ 58 | && tar xjf vim-$VIM_VERSION.tar.bz2 59 | COPY src/w64devkit.c src/w64devkit.ico src/libmemory.c src/libchkstk.S \ 60 | src/alias.c src/debugbreak.c src/pkg-config.c src/vc++filt.c \ 61 | src/peports.c src/profile $PREFIX/src/ 62 | 63 | ARG ARCH=x86_64-w64-mingw32 64 | 65 | # Build cross-compiler 66 | 67 | WORKDIR /binutils-$BINUTILS_VERSION 68 | COPY src/binutils-*.patch $PREFIX/src/ 69 | RUN sed -ri 's/(static bool insert_timestamp = )/\1!/' ld/emultempl/pe*.em \ 70 | && sed -ri 's/(int pe_enable_stdcall_fixup = )/\1!!/' ld/emultempl/pe*.em \ 71 | && sed -ri 's/(static int use_big_obj = )/\1!/' gas/config/tc-i386.c \ 72 | && cat $PREFIX/src/binutils-*.patch | patch -p1 73 | WORKDIR /x-binutils 74 | RUN /binutils-$BINUTILS_VERSION/configure \ 75 | --prefix=/bootstrap \ 76 | --with-sysroot=/bootstrap \ 77 | --target=$ARCH \ 78 | --disable-nls \ 79 | --with-static-standard-libraries \ 80 | --disable-multilib \ 81 | && make MAKEINFO=true -j$(nproc) \ 82 | && make MAKEINFO=true install 83 | 84 | # Fixes i686 Windows XP regression 85 | # https://sourceforge.net/p/mingw-w64/bugs/821/ 86 | RUN sed -i /OpenThreadToken/d /mingw-w64-v$MINGW_VERSION/mingw-w64-crt/lib32/kernel32.def 87 | 88 | WORKDIR /x-mingw-headers 89 | RUN /mingw-w64-v$MINGW_VERSION/mingw-w64-headers/configure \ 90 | --prefix=/bootstrap \ 91 | --host=$ARCH \ 92 | --with-default-msvcrt=msvcrt-os \ 93 | && make -j$(nproc) \ 94 | && make install 95 | 96 | WORKDIR /bootstrap 97 | RUN ln -s /bootstrap mingw 98 | 99 | WORKDIR /x-gcc 100 | COPY src/gcc-*.patch $PREFIX/src/ 101 | RUN cat $PREFIX/src/gcc-*.patch | patch -d/gcc-$GCC_VERSION -p1 \ 102 | && /gcc-$GCC_VERSION/configure \ 103 | --prefix=/bootstrap \ 104 | --with-sysroot=/bootstrap \ 105 | --target=$ARCH \ 106 | --enable-static \ 107 | --disable-shared \ 108 | --with-pic \ 109 | --enable-languages=c,c++,fortran \ 110 | --enable-libgomp \ 111 | --enable-threads=posix \ 112 | --enable-version-specific-runtime-libs \ 113 | --disable-libstdcxx-verbose \ 114 | --disable-dependency-tracking \ 115 | --disable-nls \ 116 | --disable-lto \ 117 | --disable-multilib \ 118 | CFLAGS_FOR_TARGET="-Os" \ 119 | CXXFLAGS_FOR_TARGET="-Os" \ 120 | LDFLAGS_FOR_TARGET="-s" \ 121 | CFLAGS="-Os" \ 122 | CXXFLAGS="-Os" \ 123 | LDFLAGS="-s" \ 124 | && make -j$(nproc) all-gcc \ 125 | && make install-gcc 126 | 127 | ENV PATH="/bootstrap/bin:${PATH}" 128 | 129 | RUN mkdir -p $PREFIX/lib \ 130 | && CC=$ARCH-gcc AR=$ARCH-ar DESTDIR=$PREFIX/lib/ \ 131 | sh $PREFIX/src/libmemory.c \ 132 | && ln $PREFIX/lib/libmemory.a /bootstrap/lib/ \ 133 | && CC=$ARCH-gcc AR=$ARCH-ar DESTDIR=$PREFIX/lib/ \ 134 | sh $PREFIX/src/libchkstk.S \ 135 | && ln $PREFIX/lib/libchkstk.a /bootstrap/lib/ 136 | 137 | WORKDIR /x-mingw-crt 138 | RUN /mingw-w64-v$MINGW_VERSION/mingw-w64-crt/configure \ 139 | --prefix=/bootstrap \ 140 | --with-sysroot=/bootstrap \ 141 | --host=$ARCH \ 142 | --with-default-msvcrt=msvcrt-os \ 143 | --disable-dependency-tracking \ 144 | --disable-lib32 \ 145 | --enable-lib64 \ 146 | CFLAGS="-Os" \ 147 | LDFLAGS="-s" \ 148 | && make -j$(nproc) \ 149 | && make install 150 | 151 | WORKDIR /x-winpthreads 152 | RUN /mingw-w64-v$MINGW_VERSION/mingw-w64-libraries/winpthreads/configure \ 153 | --prefix=/bootstrap \ 154 | --with-sysroot=/bootstrap \ 155 | --host=$ARCH \ 156 | --enable-static \ 157 | --disable-shared \ 158 | CFLAGS="-Os" \ 159 | LDFLAGS="-s" \ 160 | && make -j$(nproc) \ 161 | && make install 162 | 163 | WORKDIR /x-gcc 164 | RUN make -j$(nproc) \ 165 | && make install 166 | 167 | # Cross-compile GCC 168 | 169 | WORKDIR /binutils 170 | RUN /binutils-$BINUTILS_VERSION/configure \ 171 | --prefix=$PREFIX \ 172 | --with-sysroot=$PREFIX \ 173 | --host=$ARCH \ 174 | --target=$ARCH \ 175 | --disable-nls \ 176 | --with-static-standard-libraries \ 177 | CFLAGS="-Os" \ 178 | LDFLAGS="-s" \ 179 | && make MAKEINFO=true tooldir=$PREFIX -j$(nproc) \ 180 | && make MAKEINFO=true tooldir=$PREFIX install \ 181 | && rm $PREFIX/bin/elfedit.exe $PREFIX/bin/readelf.exe 182 | 183 | WORKDIR /gmp 184 | RUN /gmp-$GMP_VERSION/configure \ 185 | --prefix=/deps \ 186 | --host=$ARCH \ 187 | --disable-assembly \ 188 | --enable-static \ 189 | --disable-shared \ 190 | CC=$ARCH-gcc \ 191 | CFLAGS="-std=gnu17 -Os" \ 192 | CXXFLAGS="-Os" \ 193 | LDFLAGS="-s" \ 194 | && make -j$(nproc) \ 195 | && make install 196 | 197 | WORKDIR /mpfr 198 | RUN /mpfr-$MPFR_VERSION/configure \ 199 | --prefix=/deps \ 200 | --host=$ARCH \ 201 | --with-gmp-include=/deps/include \ 202 | --with-gmp-lib=/deps/lib \ 203 | --enable-static \ 204 | --disable-shared \ 205 | CC=$ARCH-gcc \ 206 | CFLAGS="-Os" \ 207 | LDFLAGS="-s" \ 208 | && make -j$(nproc) \ 209 | && make install 210 | 211 | WORKDIR /mpc 212 | RUN /mpc-$MPC_VERSION/configure \ 213 | --prefix=/deps \ 214 | --host=$ARCH \ 215 | --with-gmp-include=/deps/include \ 216 | --with-gmp-lib=/deps/lib \ 217 | --with-mpfr-include=/deps/include \ 218 | --with-mpfr-lib=/deps/lib \ 219 | --enable-static \ 220 | --disable-shared \ 221 | CC=$ARCH-gcc \ 222 | CFLAGS="-Os" \ 223 | LDFLAGS="-s" \ 224 | && make -j$(nproc) \ 225 | && make install 226 | 227 | WORKDIR /mingw-headers 228 | RUN /mingw-w64-v$MINGW_VERSION/mingw-w64-headers/configure \ 229 | --prefix=$PREFIX \ 230 | --host=$ARCH \ 231 | --with-default-msvcrt=msvcrt-os \ 232 | && make -j$(nproc) \ 233 | && make install 234 | 235 | WORKDIR /mingw-crt 236 | RUN /mingw-w64-v$MINGW_VERSION/mingw-w64-crt/configure \ 237 | --prefix=$PREFIX \ 238 | --with-sysroot=$PREFIX \ 239 | --host=$ARCH \ 240 | --with-default-msvcrt=msvcrt-os \ 241 | --disable-dependency-tracking \ 242 | --disable-lib32 \ 243 | --enable-lib64 \ 244 | CFLAGS="-Os" \ 245 | LDFLAGS="-s" \ 246 | && make -j$(nproc) \ 247 | && make install 248 | 249 | WORKDIR /winpthreads 250 | RUN /mingw-w64-v$MINGW_VERSION/mingw-w64-libraries/winpthreads/configure \ 251 | --prefix=$PREFIX \ 252 | --with-sysroot=$PREFIX \ 253 | --host=$ARCH \ 254 | --enable-static \ 255 | --disable-shared \ 256 | CFLAGS="-Os" \ 257 | LDFLAGS="-s" \ 258 | && make -j$(nproc) \ 259 | && make install 260 | 261 | WORKDIR /gcc 262 | COPY src/crossgcc-*.patch $PREFIX/src/ 263 | RUN echo 'BEGIN {print "pecoff"}' \ 264 | >/gcc-$GCC_VERSION/libbacktrace/filetype.awk \ 265 | && cat $PREFIX/src/crossgcc-*.patch | patch -d/gcc-$GCC_VERSION -p1 \ 266 | && /gcc-$GCC_VERSION/configure \ 267 | --prefix=$PREFIX \ 268 | --with-sysroot=$PREFIX \ 269 | --with-native-system-header-dir=/include \ 270 | --target=$ARCH \ 271 | --host=$ARCH \ 272 | --enable-static \ 273 | --disable-shared \ 274 | --with-pic \ 275 | --with-gmp-include=/deps/include \ 276 | --with-gmp-lib=/deps/lib \ 277 | --with-mpc-include=/deps/include \ 278 | --with-mpc-lib=/deps/lib \ 279 | --with-mpfr-include=/deps/include \ 280 | --with-mpfr-lib=/deps/lib \ 281 | --enable-languages=c,c++,fortran \ 282 | --enable-libgomp \ 283 | --enable-threads=posix \ 284 | --enable-version-specific-runtime-libs \ 285 | --disable-libstdcxx-verbose \ 286 | --disable-dependency-tracking \ 287 | --disable-lto \ 288 | --disable-multilib \ 289 | --disable-nls \ 290 | --disable-win32-registry \ 291 | --enable-mingw-wildcard \ 292 | CFLAGS_FOR_TARGET="-Os" \ 293 | CXXFLAGS_FOR_TARGET="-Os" \ 294 | LDFLAGS_FOR_TARGET="-s" \ 295 | CFLAGS="-Os" \ 296 | CXXFLAGS="-Os" \ 297 | LDFLAGS="-s" \ 298 | && make -j$(nproc) \ 299 | && make install \ 300 | && rm -f $PREFIX/bin/ld.bfd.exe \ 301 | && $ARCH-gcc -DEXE=g++.exe -DCMD=c++ \ 302 | -Os -fno-asynchronous-unwind-tables \ 303 | -Wl,--gc-sections -s -nostdlib \ 304 | -o $PREFIX/bin/c++.exe \ 305 | $PREFIX/src/alias.c -lkernel32 306 | 307 | # Create various tool aliases 308 | RUN $ARCH-gcc -DEXE=gcc.exe -DCMD=cc \ 309 | -Os -fno-asynchronous-unwind-tables -Wl,--gc-sections -s -nostdlib \ 310 | -o $PREFIX/bin/cc.exe $PREFIX/src/alias.c -lkernel32 \ 311 | && $ARCH-gcc -DEXE=gcc.exe -DCMD="cc -std=c99" \ 312 | -Os -fno-asynchronous-unwind-tables -Wl,--gc-sections -s -nostdlib \ 313 | -o $PREFIX/bin/c99.exe $PREFIX/src/alias.c -lkernel32 \ 314 | && $ARCH-gcc -DEXE=gcc.exe -DCMD="cc -ansi" \ 315 | -Os -fno-asynchronous-unwind-tables -Wl,--gc-sections -s -nostdlib \ 316 | -o $PREFIX/bin/c89.exe $PREFIX/src/alias.c -lkernel32 \ 317 | && printf '%s\n' addr2line ar as c++filt cpp dlltool dllwrap elfedit g++ \ 318 | gcc gcc-ar gcc-nm gcc-ranlib gcov gcov-dump gcov-tool ld nm objcopy \ 319 | objdump ranlib readelf size strings strip windmc windres gfortran \ 320 | | xargs -I{} -P$(nproc) \ 321 | $ARCH-gcc -DEXE={}.exe -DCMD=$ARCH-{} \ 322 | -Os -fno-asynchronous-unwind-tables \ 323 | -Wl,--gc-sections -s -nostdlib \ 324 | -o $PREFIX/bin/$ARCH-{}.exe $PREFIX/src/alias.c -lkernel32 325 | 326 | # Build some extra development tools 327 | 328 | WORKDIR /mingw-tools/gendef 329 | COPY src/gendef-silent.patch $PREFIX/src/ 330 | RUN patch -d/mingw-w64-v$MINGW_VERSION -p1 <$PREFIX/src/gendef-silent.patch \ 331 | && /mingw-w64-v$MINGW_VERSION/mingw-w64-tools/gendef/configure \ 332 | --host=$ARCH \ 333 | CFLAGS="-Os" \ 334 | LDFLAGS="-s" \ 335 | && make -j$(nproc) \ 336 | && cp gendef.exe $PREFIX/bin/ 337 | 338 | WORKDIR /expat 339 | RUN /expat-$EXPAT_VERSION/configure \ 340 | --prefix=/deps \ 341 | --host=$ARCH \ 342 | --disable-shared \ 343 | --without-docbook \ 344 | --without-examples \ 345 | --without-tests \ 346 | CFLAGS="-Os" \ 347 | LDFLAGS="-s" \ 348 | && make -j$(nproc) \ 349 | && make install 350 | 351 | WORKDIR /PDCurses-$PDCURSES_VERSION 352 | RUN make -j$(nproc) -C wincon \ 353 | CC=$ARCH-gcc AR=$ARCH-ar CFLAGS="-I.. -Os -DPDC_WIDE" pdcurses.a \ 354 | && cp wincon/pdcurses.a /deps/lib/libcurses.a \ 355 | && cp curses.h /deps/include 356 | 357 | WORKDIR /libiconv 358 | RUN /libiconv-$LIBICONV_VERSION/configure \ 359 | --prefix=/deps \ 360 | --host=$ARCH \ 361 | --disable-nls \ 362 | --disable-shared \ 363 | CFLAGS="-Os" \ 364 | LDFLAGS="-s" \ 365 | && make -j$(nproc) \ 366 | && make install 367 | 368 | WORKDIR /gdb 369 | COPY src/gdb-*.patch $PREFIX/src/ 370 | RUN cat $PREFIX/src/gdb-*.patch | patch -d/gdb-$GDB_VERSION -p1 \ 371 | && sed -i 's/quiet = 0/quiet = 1/' /gdb-$GDB_VERSION/gdb/main.c \ 372 | && /gdb-$GDB_VERSION/configure \ 373 | --host=$ARCH \ 374 | --enable-tui \ 375 | CFLAGS="-std=gnu17 -Os -D__MINGW_USE_VC2005_COMPAT -DPDC_WIDE -I/deps/include" \ 376 | CXXFLAGS="-Os -D__MINGW_USE_VC2005_COMPAT -DPDC_WIDE -I/deps/include" \ 377 | LDFLAGS="-s -L/deps/lib" \ 378 | && make MAKEINFO=true -j$(nproc) \ 379 | && cp gdb/.libs/gdb.exe gdbserver/gdbserver.exe $PREFIX/bin/ 380 | 381 | WORKDIR /make 382 | RUN /make-$MAKE_VERSION/configure \ 383 | --host=$ARCH \ 384 | --disable-nls \ 385 | CFLAGS="-std=gnu17 -Os" \ 386 | LDFLAGS="-s" \ 387 | && make -j$(nproc) \ 388 | && cp make.exe $PREFIX/bin/ \ 389 | && $ARCH-gcc -DEXE=make.exe -DCMD=make \ 390 | -Os -fno-asynchronous-unwind-tables \ 391 | -Wl,--gc-sections -s -nostdlib \ 392 | -o $PREFIX/bin/mingw32-make.exe $PREFIX/src/alias.c -lkernel32 393 | 394 | WORKDIR /busybox-w32 395 | COPY src/busybox-* $PREFIX/src/ 396 | RUN cat $PREFIX/src/busybox-*.patch | patch -p1 \ 397 | && make mingw64u_defconfig \ 398 | && sed -ri 's/^(CONFIG_AR)=y/\1=n/' .config \ 399 | && sed -ri 's/^(CONFIG_ASCII)=y/\1=n/' .config \ 400 | && sed -ri 's/^(CONFIG_DPKG\w*)=y/\1=n/' .config \ 401 | && sed -ri 's/^(CONFIG_FTP\w*)=y/\1=n/' .config \ 402 | && sed -ri 's/^(CONFIG_LINK)=y/\1=n/' .config \ 403 | && sed -ri 's/^(CONFIG_MAN)=y/\1=n/' .config \ 404 | && sed -ri 's/^(CONFIG_MAKE)=y/\1=n/' .config \ 405 | && sed -ri 's/^(CONFIG_PDPMAKE)=y/\1=n/' .config \ 406 | && sed -ri 's/^(CONFIG_RPM\w*)=y/\1=n/' .config \ 407 | && sed -ri 's/^(CONFIG_STRINGS)=y/\1=n/' .config \ 408 | && sed -ri 's/^(CONFIG_TEST2)=y/\1=n/' .config \ 409 | && sed -ri 's/^(CONFIG_TSORT)=y/\1=n/' .config \ 410 | && sed -ri 's/^(CONFIG_UNLINK)=y/\1=n/' .config \ 411 | && sed -ri 's/^(CONFIG_VI)=y/\1=n/' .config \ 412 | && sed -ri 's/^(CONFIG_XXD)=y/\1=n/' .config \ 413 | && make -j$(nproc) CROSS_COMPILE=$ARCH- \ 414 | CONFIG_EXTRA_CFLAGS="-D_WIN32_WINNT=0x502" \ 415 | && cp busybox.exe $PREFIX/bin/ 416 | 417 | # Create BusyBox command aliases (like "busybox --install") 418 | RUN $ARCH-gcc -Os -fno-asynchronous-unwind-tables -Wl,--gc-sections -s \ 419 | -nostdlib -o alias.exe $PREFIX/src/busybox-alias.c -lkernel32 \ 420 | && printf '%s\n' arch ash awk base32 base64 basename bash bc bunzip2 bzcat \ 421 | bzip2 cal cat chattr chmod cksum clear cmp comm cp cpio crc32 cut date \ 422 | dc dd df diff dirname dos2unix du echo ed egrep env expand expr factor \ 423 | false fgrep find fold free fsync getopt grep groups gunzip gzip hd \ 424 | head hexdump httpd iconv id inotifyd install ipcalc jn kill killall \ 425 | lash less ln logname ls lsattr lzcat lzma lzop lzopcat md5sum mkdir \ 426 | mktemp mv nc nl nproc od paste patch pgrep pidof pipe_progress pkill \ 427 | printenv printf ps pwd readlink realpath reset rev rm rmdir sed seq sh \ 428 | sha1sum sha256sum sha3sum sha512sum shred shuf sleep sort split \ 429 | ssl_client stat su sum sync tac tail tar tee test time timeout touch \ 430 | tr true truncate ts ttysize uname uncompress unexpand uniq unix2dos \ 431 | unlzma unlzop unxz unzip uptime usleep uudecode uuencode watch \ 432 | wc wget which whoami whois xargs xz xzcat yes zcat \ 433 | | xargs -I{} cp alias.exe $PREFIX/bin/{}.exe 434 | 435 | # TODO: Either somehow use $VIM_VERSION or normalize the workdir 436 | WORKDIR /vim90/src 437 | COPY src/rexxd.c $PREFIX/src/ 438 | RUN ARCH= make -j$(nproc) -f Make_ming.mak CC="$ARCH-gcc -std=gnu17" \ 439 | OPTIMIZE=SIZE STATIC_STDCPLUS=yes HAS_GCC_EH=no \ 440 | UNDER_CYGWIN=yes CROSS=yes CROSS_COMPILE=$ARCH- \ 441 | FEATURES=HUGE VIMDLL=yes NETBEANS=no WINVER=0x0501 \ 442 | && $ARCH-strip vimrun.exe \ 443 | && rm -rf ../runtime/tutor/tutor.* \ 444 | && cp -r ../runtime $PREFIX/share/vim \ 445 | && cp vimrun.exe gvim.exe vim.exe *.dll $PREFIX/share/vim/ \ 446 | && printf '@set SHELL=\r\n@start "" "%%~dp0/../share/vim/gvim.exe" %%*\r\n' \ 447 | >$PREFIX/bin/gvim.bat \ 448 | && printf '@set SHELL=\r\n@"%%~dp0/../share/vim/vim.exe" %%*\r\n' \ 449 | >$PREFIX/bin/vim.bat \ 450 | && printf '@set SHELL=\r\n@"%%~dp0/../share/vim/vim.exe" %%*\r\n' \ 451 | >$PREFIX/bin/vi.bat \ 452 | && printf '@vim -N -u NONE "+read %s" "+write" "%s"\r\n' \ 453 | '$VIMRUNTIME/tutor/tutor' '%TMP%/tutor%RANDOM%' \ 454 | >$PREFIX/bin/vimtutor.bat \ 455 | && $ARCH-gcc -nostartfiles -O2 -funroll-loops -s -o $PREFIX/bin/xxd.exe \ 456 | $PREFIX/src/rexxd.c -lmemory 457 | 458 | WORKDIR /ctags-$CTAGS_VERSION 459 | RUN sed -i /RT_MANIFEST/d win32/ctags.rc \ 460 | && make -j$(nproc) -f mk_mingw.mak CC=gcc packcc.exe \ 461 | && make -j$(nproc) -f mk_mingw.mak \ 462 | CC=$ARCH-gcc WINDRES=$ARCH-windres \ 463 | OPT= CFLAGS=-Os LDFLAGS=-s \ 464 | && cp ctags.exe $PREFIX/bin/ 465 | 466 | WORKDIR /7z 467 | COPY src/7z.mak $PREFIX/src/ 468 | RUN sed -i s/CommCtrl/commctrl/ $(grep -Rl CommCtrl CPP/) \ 469 | && sed -i s%7z\\.ico%$PREFIX/src/w64devkit.ico% \ 470 | CPP/7zip/Bundles/SFXWin/resource.rc \ 471 | && make -f $PREFIX/src/7z.mak -j$(nproc) CROSS=$ARCH- 472 | 473 | # Pack up a release 474 | 475 | WORKDIR / 476 | RUN rm -rf $PREFIX/share/man/ $PREFIX/share/info/ $PREFIX/share/gcc-* 477 | COPY README.md Dockerfile src/w64devkit.ini $PREFIX/ 478 | RUN printf "id ICON \"$PREFIX/src/w64devkit.ico\"" >w64devkit.rc \ 479 | && $ARCH-windres -o w64devkit.o w64devkit.rc \ 480 | && $ARCH-gcc -DVERSION=$VERSION -nostdlib -fno-asynchronous-unwind-tables \ 481 | -fno-builtin -Wl,--gc-sections -s -o $PREFIX/w64devkit.exe \ 482 | $PREFIX/src/w64devkit.c w64devkit.o -lkernel32 -luser32 \ 483 | && $ARCH-gcc \ 484 | -Os -fno-asynchronous-unwind-tables \ 485 | -Wl,--gc-sections -s -nostdlib \ 486 | -o $PREFIX/bin/debugbreak.exe $PREFIX/src/debugbreak.c \ 487 | -lkernel32 \ 488 | && $ARCH-gcc \ 489 | -Os -fno-asynchronous-unwind-tables -fno-builtin -Wl,--gc-sections \ 490 | -s -nostdlib -o $PREFIX/bin/pkg-config.exe $PREFIX/src/pkg-config.c \ 491 | -lkernel32 \ 492 | && $ARCH-gcc \ 493 | -Os -fno-asynchronous-unwind-tables -fno-builtin -Wl,--gc-sections \ 494 | -s -nostdlib -o $PREFIX/bin/vc++filt.exe $PREFIX/src/vc++filt.c \ 495 | -lkernel32 -lshell32 -ldbghelp \ 496 | && $ARCH-gcc \ 497 | -Os -fno-asynchronous-unwind-tables -fno-builtin -Wl,--gc-sections \ 498 | -s -nostdlib -o $PREFIX/bin/peports.exe $PREFIX/src/peports.c \ 499 | -lkernel32 -lshell32 \ 500 | && $ARCH-gcc -DEXE=pkg-config.exe -DCMD=pkg-config \ 501 | -Os -fno-asynchronous-unwind-tables -Wl,--gc-sections -s -nostdlib \ 502 | -o $PREFIX/bin/$ARCH-pkg-config.exe $PREFIX/src/alias.c -lkernel32 \ 503 | && sed -i s/'\'/$ARCH/g $PREFIX/src/profile \ 504 | && mkdir -p $PREFIX/lib/pkgconfig \ 505 | && cp /mingw-w64-v$MINGW_VERSION/COPYING.MinGW-w64-runtime/COPYING.MinGW-w64-runtime.txt \ 506 | $PREFIX/ \ 507 | && printf "\n===========\nwinpthreads\n===========\n\n" \ 508 | >>$PREFIX/COPYING.MinGW-w64-runtime.txt . \ 509 | && cat /mingw-w64-v$MINGW_VERSION/mingw-w64-libraries/winpthreads/COPYING \ 510 | >>$PREFIX/COPYING.MinGW-w64-runtime.txt \ 511 | && echo $VERSION >$PREFIX/VERSION.txt \ 512 | && 7z a -mx=9 -mtm=- $PREFIX.7z $PREFIX 513 | ENV PREFIX=${PREFIX} 514 | CMD cat /7z/7z.sfx $PREFIX.7z 515 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Portable C, C++, and Fortran Development Kit for x64 and x86 Windows 2 | 3 | [w64devkit][] is a Dockerfile that builds from source a small, portable 4 | development suite for creating C and C++ applications on and for x86 and 5 | x64 Windows. See "Releases" for pre-built, ready-to-use kits. 6 | 7 | Included tools: 8 | 9 | * [Mingw-w64 GCC][w64] : compilers, linker, assembler 10 | * [GDB][gdb] : debugger 11 | * [GNU Make][make] : standard build tool 12 | * [busybox-w32][bb] : standard unix utilities, including sh 13 | * [Vim][vim] : powerful text editor 14 | * [Universal Ctags][ctags] : source navigation 15 | 16 | It is an MSVCRT toolchain with pthreads, C++11 threads, and OpenMP. All 17 | included runtime components are static. **Docker/Podman is not required to 18 | use the development kit**. It's merely a reliable, clean environment for 19 | building the kit itself. 20 | 21 | ## Build 22 | 23 | Build the image, then run it to produce a self-extracting 7z archive: 24 | 25 | docker build -t w64devkit . 26 | docker run --rm w64devkit >w64devkit-x64.exe 27 | 28 | This takes about 15 minutes on modern systems. You will need an internet 29 | connection during the first few minutes of the build. **Note:** Do not use 30 | PowerShell because it lacks file redirection. 31 | 32 | ## Usage 33 | 34 | The self-extracting 7z archive contains tools in a typical unix-like 35 | configuration. Extract wherever is convenient. Inside is `w64devkit.exe`, 36 | which launches a console window with the environment configured and ready 37 | to go. It is the easiest way to enter the development environment, and 38 | requires no system changes. It also sets two extra environment variables: 39 | `W64DEVKIT_HOME` to the installation root and `W64DEVKIT` to the version. 40 | 41 | Alternatively, add the `w64devkit/bin` directory to your path. For 42 | example, inside a `cmd.exe` console or batch script: 43 | 44 | set PATH=c:\path\to\w64devkit\bin;%PATH% 45 | 46 | Then to start an interactive unix shell: 47 | 48 | sh -l 49 | 50 | ## Main features 51 | 52 | * No installation required. Run it anywhere as any user. Simply delete 53 | when no longer needed. 54 | 55 | * Fully offline. No internet access is ever required or attempted. 56 | 57 | * A focus on static linking all runtime components. The runtime is 58 | optimized for size. 59 | 60 | * Trivial to build from source, meaning it's easy to tweak and adjust any 61 | part of the kit for your own requirements. 62 | 63 | * [Complements Go][go] for cgo and bootstrapping. 64 | 65 | ## Operating system support 66 | 67 | The x64 kit requires Windows 7 or later, though some tools only support 68 | Unicode ("wide") paths, inputs, and outputs on Windows 10 or later. The 69 | toolchain targets Windows 7 by default. 70 | 71 | The x86 kit requires Windows XP or later and an SSE2-capable processor 72 | (e.g. at least Pentium 4); limited Unicode support. The toolchain targets 73 | the same by default. Runtimes contain SSE2 instructions, so GCC `-march` 74 | will not reliably target less capable processors when runtimes are linked 75 | (exceptions: `-lmemory`, `-lchkstk`). 76 | 77 | ## Optimized for size 78 | 79 | Runtime components are optimized for size, leading to smaller application 80 | executables. Unique to w64devkit, `libmemory.a` is a library of `memset`, 81 | `memcpy`, `memmove`, `memcmp`, and `strlen` implemented as x86 string 82 | instructions. When [not linking a CRT][crt], linking `-lmemory` provides 83 | tiny definitions, particularly when GCC requires them. 84 | 85 | Also unique to w64devkit, `libchkstk.a` has a leaner, faster definition of 86 | `___chkstk_ms` than GCC (`-lgcc`), as well as `__chkstk`, sometimes needed 87 | when linking MSVC artifacts. Both are in the public domain and so, unlike 88 | default implementations, do not involve complex licensing. When required 89 | in a `-nostdlib` build, link `-lchkstk`. 90 | 91 | Unlike traditional toolchains, import tables are not populated with junk 92 | ordinal hints. If an explicit hint is not provided (i.e. via a DEF file), 93 | then the hint is zeroed: "no data." Eliminating this random data makes 94 | binaries more compressible and *theoretically* faster loading. See also: 95 | `peports`. 96 | 97 | ## Recommended downloadable, offline documentation 98 | 99 | With a few exceptions, such as Vim's built-in documentation (`:help`), 100 | w64devkit does not include documentation. However, you need not forgo 101 | offline documentation alongside your offline development tools. This is a 102 | list of recommended, no-cost, downloadable documentation complementing 103 | w64devkit's capabilities. In rough order of importance: 104 | 105 | * [cppreference][doc-cpp] (HTML), friendly documentation for the C and C++ 106 | standard libraries. 107 | 108 | * [GCC manuals][doc-gcc] (PDF, HTML), to reference GCC features, 109 | especially built-ins, intrinsics, and command line switches. 110 | 111 | * [Win32 Help File][doc-win32] (CHM) is old, but official, Windows API 112 | documentation. Unfortunately much is missing, such as Winsock. (Offline 113 | Windows documentation has always been very hard to come by.) 114 | 115 | * [C Standards][doc-std-c] and [C++ Standards][doc-std-cpp] (drafts), for 116 | figuring out how corner cases are intended to work. 117 | 118 | * [Intel Intrinsics Guide][doc-intr] (interactive HTML), a great resource 119 | when working with SIMD intrinsics. (Search for "Download" on the left.) 120 | 121 | * [GNU Make manual][doc-make] (PDF, HTML) 122 | 123 | * [GNU Binutils manuals][doc-ld] (PDF, HTML), particularly `ld` and `as`. 124 | 125 | * [GDB manual][doc-gdb] (PDF) 126 | 127 | * [BusyBox man pages][doc-bb] (TXT), though everything here is also 128 | available via `-h` option inside w64devkit. 129 | 130 | * [Intel Software Developer Manuals][doc-intel] (PDF), for referencing x86 131 | instructions, when either studying compiler output with `objdump` or 132 | writing assembly. 133 | 134 | ## Library installation 135 | 136 | Except for the standard libraries and Win32 import libraries, w64devkit 137 | does not include libraries, but you can install additional libraries such 138 | that the toolchain can find them naturally. There are three options: 139 | 140 | 1. Install it under the sysroot at `w64devkit/$ARCH/`. The easiest option, 141 | but will require re-installation after upgrading w64devkit. If it 142 | defines `.pc` files, the `pkg-config` command will automatically find 143 | and use them. 144 | 145 | 2. Append its installation directory to your `CPATH` and `LIBRARY_PATH` 146 | environment variables. Use `;` to delimit directories. You would likely 147 | do this in your `.profile`. 148 | 149 | 3. If it exists, append its `pkgconfig` directory to the `PKG_CONFIG_PATH` 150 | environment variable, then use the `pkg-config` command as usual. Use 151 | `;` to delimit directories 152 | 153 | Both (1) and (3) are designed to work correctly even if w64devkit or the 154 | libraries have paths containing spaces. 155 | 156 | ## Unique command-line programs 157 | 158 | * `peports`: displays export and import tables of EXEs and DLLs. Like MSVC 159 | `dumpbin` options `/exports` and `/imports`; narrower and more precise 160 | than Binutils `objdump -p`. Useful for checking if exports and imports 161 | match your expectations. Complemented by `c++filt` and `vc++filt`, i.e. 162 | in a pipeline. Pronounced like *purports*. 163 | 164 | * `vc++filt`: a `c++filt` for [Visual C++ name decorations][names]. Used 165 | to examine GCC-incompatible binaries, potentially to make some use of 166 | them anyway. 167 | 168 | * [`debugbreak`][debugbreak]: causes all debugee processes to break in the 169 | debugger, like using Windows' F12 debugger hotkey. Especially useful for 170 | console subsystem programs. 171 | 172 | ## Notes 173 | 174 | `$HOME` can be set through the adjacent `w64devkit.ini` configuration, and 175 | may even be relative to the `w64devkit/` directory. This is useful for 176 | encapsulating the entire development environment, with home directory, on 177 | removable, even read-only, media. Use a `.profile` in the home directory 178 | to configure the environment further. 179 | 180 | Neither Address Sanitizer (ASan) nor Thread Sanitizer (TSan) [has been 181 | ported to Mingw-w64][san] ([also][san2]), but Undefined Behavior Sanitizer 182 | (UBSan) works perfectly under GDB. With both `-fsanitize=undefined` and 183 | `-fsanitize-trap`, GDB will [break precisely][break] on undefined 184 | behavior, and it does not require linking with libsanitizer. 185 | 186 | ## Licenses 187 | 188 | When distributing binaries built using w64devkit, your .exe will include 189 | parts of this distribution. For the GCC runtime, including OpenMP, you're 190 | covered by the [GCC Runtime Library Exception][gpl] so you do not need to 191 | do anything. However the Mingw-w64 runtime [has the usual software license 192 | headaches][bs] and you may need to comply with various BSD-style licenses 193 | depending on the functionality used by your program: [MinGW-w64 runtime 194 | licensing][lic1] and [winpthreads license][lic2]. To make this easy, 195 | w64devkit includes the concatenated set of all licenses in the file 196 | `COPYING.MinGW-w64-runtime.txt`, which should be distributed with your 197 | binaries. 198 | 199 | 200 | [bb]: https://frippery.org/busybox/ 201 | [break]: https://nullprogram.com/blog/2022/06/26/ 202 | [bs]: https://www.rdegges.com/2016/i-dont-give-a-shit-about-licensing/ 203 | [crt]: https://nullprogram.com/blog/2023/02/15/ 204 | [ctags]: https://github.com/universal-ctags/ctags 205 | [debugbreak]: https://nullprogram.com/blog/2022/07/31/ 206 | [doc-bb]: https://busybox.net/downloads/BusyBox.txt 207 | [doc-cpp]: https://en.cppreference.com/w/Cppreference:Archives 208 | [doc-gcc]: https://gcc.gnu.org/onlinedocs/ 209 | [doc-gdb]: https://sourceware.org/gdb/current/onlinedocs/gdb.pdf 210 | [doc-intel]: https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html 211 | [doc-intr]: https://software.intel.com/sites/landingpage/IntrinsicsGuide/ 212 | [doc-ld]: https://sourceware.org/binutils/docs/ 213 | [doc-make]: https://www.gnu.org/software/make/manual/ 214 | [doc-std-c]: https://en.cppreference.com/w/c/links 215 | [doc-std-cpp]: https://en.cppreference.com/w/cpp/links 216 | [doc-win32]: https://web.archive.org/web/20220922051031/http://www.laurencejackson.com/win32/ 217 | [gdb]: https://www.gnu.org/software/gdb/ 218 | [go]: https://nullprogram.com/blog/2021/06/29/ 219 | [gpl]: https://www.gnu.org/licenses/gcc-exception-3.1.en.html 220 | [lic1]: https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/COPYING.MinGW-w64-runtime/COPYING.MinGW-w64-runtime.txt 221 | [lic2]: https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-libraries/winpthreads/COPYING 222 | [make]: https://www.gnu.org/software/make/ 223 | [names]: https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names 224 | [san]: http://mingw-w64.org/doku.php/contribute#sanitizers_asan_tsan_usan 225 | [san2]: https://groups.google.com/forum/#!topic/address-sanitizer/q0e5EBVKZT4 226 | [vim]: https://www.vim.org/ 227 | [w64]: http://mingw-w64.org/ 228 | [w64devkit]: https://github.com/skeeto/w64devkit 229 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /contrib/README.md: -------------------------------------------------------------------------------- 1 | # Unsupported w64devkit enhancements 2 | 3 | The files in this directory enhance w64devkit with specially-built 4 | third-party libraries, such as garbage collection and POSIX `regex.h`. 5 | Falling outside the scope of w64devkit, these libraries are not included 6 | in the distribution, but may be situationally useful. 7 | 8 | The header of each file has the instructions for its use. The file itself 9 | is mostly a custom build script, and you will need to obtain the library 10 | sources yourself the usual way. 11 | -------------------------------------------------------------------------------- /contrib/cppcheck.mak: -------------------------------------------------------------------------------- 1 | # Build and install Cppcheck inside w64devkit 2 | # 3 | # Invoke in a Cppcheck source tree: 4 | # $ make -j$(nproc) -f path/to/cppcheck.mak install 5 | # 6 | # Alternatively, use the default target and run it in place in the 7 | # source tree without installing. 8 | 9 | ext := $(shell find externals -mindepth 1 -type d) 10 | src := $(shell find cli lib externals -name '*.cpp') 11 | obj := $(src:.cpp=.o) 12 | CXXFLAGS := -w -O2 -Ilib $(addprefix -I,$(ext)) 13 | 14 | cppcheck.exe: $(obj) 15 | $(CXX) -s -o $@ $(obj) -lshlwapi 16 | 17 | install: cppcheck.exe 18 | mkdir -p "$$W64DEVKIT_HOME"/share/cppcheck 19 | cp -r cppcheck.exe cfg/ "$$W64DEVKIT_HOME"/share/cppcheck/ 20 | $(CC) -DEXE=../share/cppcheck/cppcheck.exe -DCMD=cppcheck \ 21 | -Oz -s -nostartfiles -o "$$W64DEVKIT_HOME"/bin/cppcheck.exe \ 22 | "$$W64DEVKIT_HOME"/src/alias.c 23 | 24 | uninstall: 25 | rm -rf "$$W64DEVKIT_HOME"/share/cppcheck/ \ 26 | "$$W64DEVKIT_HOME"/bin/cppcheck.exe 27 | -------------------------------------------------------------------------------- /contrib/libgc.c: -------------------------------------------------------------------------------- 1 | #if 0 2 | # Minimalist, stripped down, w64devkit, static, unity build of Boehm GC. 3 | # Provides an easy-to-use garbage collector weighing ~24KiB. The API is 4 | # two functions: GC_malloc and GC_realloc. Tested with gc 8.2.4. 5 | # 6 | # Place this source file in the Boehm GC source tree root, then invoke 7 | # it with the shell to produce gc.h and libgc.a. 8 | # 9 | # $ sh libgc.c 10 | # 11 | # Philosophy: If the goal is ease-of-use, remove all knobs that are not 12 | # strictly necessary. If performance isn't good enough, don't use garbage 13 | # collection; switch to a region-based allocator. 14 | # 15 | # Ref: https://www.hboehm.info/gc/gc_source/gc-8.2.4.tar.gz 16 | # This is free and unencumbered software released into the public domain. 17 | set -ex 18 | 19 | PREFIX="${PREFIX:-$(gcc -print-sysroot)}" 20 | 21 | ${CC:-cc} -c -Iinclude -fwhole-program ${CFLAGS:--Os} libgc.c 22 | ${STRIP-strip} -x libgc.o 23 | 24 | mkdir -p "$PREFIX/lib" 25 | rm -f "$PREFIX/lib/libgc.a" 26 | ${AR:-ar} -r "$PREFIX/lib/libgc.a" libgc.o 27 | 28 | mkdir -p "$PREFIX/include" 29 | tee "$PREFIX/include/gc.h" < 34 | void *GC_malloc(size_t) __attribute((alloc_size(1), malloc)); 35 | void *GC_realloc(void *, size_t) __attribute((alloc_size(2))); 36 | #endif 37 | EOF 38 | exit 0 39 | #endif 40 | 41 | #define ALL_INTERIOR_POINTERS 42 | #define DONT_USE_ATEXIT 43 | #define GC_API 44 | #define GC_NO_FINALIZATION 45 | #define GC_TOGGLE_REFS_NOT_NEEDED 46 | #define GC_USE_ENTIRE_HEAP 47 | #define NO_CLOCK 48 | #define NO_DEBUGGING 49 | #define NO_GETENV 50 | #define NO_MSGBOX_ON_ERROR 51 | 52 | #include "allchblk.c" 53 | #include "alloc.c" 54 | #include "blacklst.c" 55 | #include "dbg_mlc.c" 56 | #include "dyn_load.c" 57 | #include "finalize.c" 58 | #include "headers.c" 59 | #include "mach_dep.c" 60 | #include "malloc.c" 61 | #include "mallocx.c" 62 | #include "mark.c" 63 | #include "mark_rts.c" 64 | #include "misc.c" 65 | #include "new_hblk.c" 66 | #include "obj_map.c" 67 | #include "os_dep.c" 68 | #include "ptr_chck.c" 69 | #include "reclaim.c" 70 | 71 | __attribute((used)) void *GC_malloc(size_t); 72 | __attribute((used)) void *GC_realloc(void *, size_t); 73 | -------------------------------------------------------------------------------- /contrib/libregex.c: -------------------------------------------------------------------------------- 1 | #if 0 2 | # Stripped down, w64devkit, static, unity build of the PCRE2 POSIX regex 3 | # library. This script builds and installs regex.h and libregex.a inside 4 | # w64devkit. Link with -lregex. It is pretty bulky, nearly 300KiB. 5 | # 6 | # Copy and run this script in the root fo the PCRE source tree. Tested 7 | # with PCRE 10.43. 8 | # 9 | # $ sh libregex.c 10 | # 11 | # Ref: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/regex.h.html 12 | # This is free and unencumbered software released into the public domain. 13 | set -ex 14 | 15 | PREFIX="${PREFIX:-$(gcc -print-sysroot)}" 16 | 17 | cp src/pcre2.h.generic src/pcre2.h 18 | ${CC:-cc} -c -fwhole-program ${CFLAGS:--Os} "$0" 19 | ${STRIP:-strip} -x libregex.o 20 | 21 | mkdir -p "$PREFIX/lib" 22 | rm -f "$PREFIX/lib/libregex.a" 23 | ${AR:-ar} -r "$PREFIX/lib/libregex.a" libregex.o 24 | 25 | mkdir -p "$PREFIX/include" 26 | tee >nul "$PREFIX/include/regex.h" < 29 | 30 | enum { 31 | REG_EXTENDED = 0x0000, 32 | REG_ICASE = 0x0001, 33 | REG_NEWLINE = 0x0002, 34 | REG_NOTBOL = 0x0004, 35 | REG_NOTEOL = 0x0008, 36 | REG_DOTALL = 0x0010, 37 | REG_NOSUB = 0x0020, 38 | REG_UTF = 0x0040, 39 | REG_STARTEND = 0x0080, 40 | REG_NOTEMPTY = 0x0100, 41 | REG_UNGREEDY = 0x0200, 42 | REG_UCP = 0x0400, 43 | REG_PEND = 0x0800, 44 | REG_NOSPEC = 0x1000, 45 | }; 46 | 47 | enum { 48 | REG_ASSERT = 1, 49 | REG_BADBR, 50 | REG_BADPAT, 51 | REG_BADRPT, 52 | REG_EBRACE, 53 | REG_EBRACK, 54 | REG_ECOLLATE, 55 | REG_ECTYPE, 56 | REG_EESCAPE, 57 | REG_EMPTY, 58 | REG_EPAREN, 59 | REG_ERANGE, 60 | REG_ESIZE, 61 | REG_ESPACE, 62 | REG_ESUBREG, 63 | REG_INVARG, 64 | REG_NOMATCH, 65 | }; 66 | 67 | typedef struct { 68 | void *re_pcre2_code; 69 | void *re_match_data; 70 | char *re_endp; 71 | size_t re_nsub; 72 | size_t re_erroffset; 73 | int re_cflags; 74 | } regex_t; 75 | 76 | typedef int regoff_t; 77 | 78 | typedef struct { 79 | regoff_t rm_so; 80 | regoff_t rm_eo; 81 | } regmatch_t; 82 | 83 | int regcomp(regex_t *, const char *, int); 84 | size_t regerror(int, const regex_t *, char *, size_t); 85 | int regexec(const regex_t *, const char *, size_t, regmatch_t *, int); 86 | void regfree(regex_t *); 87 | EOF 88 | exit 0 89 | #endif 90 | 91 | #define PCRE2_EXP_DEFN static 92 | #define PCRE2_EXP_DECL static 93 | #define PCRE2POSIX_EXP_DECL 94 | #define PCRE2POSIX_EXP_DEFN __attribute((used)) 95 | 96 | #define HAVE_BUILTIN_MUL_OVERFLOW 97 | #define HAVE_MEMMOVE 98 | #define HEAP_LIMIT 20000000 99 | #define LINK_SIZE 2 100 | #define MATCH_LIMIT 10000000 101 | #define MATCH_LIMIT_DEPTH MATCH_LIMIT 102 | #define MAX_NAME_COUNT 10000 103 | #define MAX_NAME_SIZE 32 104 | #define MAX_VARLOOKBEHIND 255 105 | #define NEWLINE_DEFAULT 2 106 | #define PARENS_NEST_LIMIT 250 107 | #define PCRE2_CODE_UNIT_WIDTH 8 108 | #define PCRE2_STATIC 109 | #define SUPPORT_UNICODE 110 | 111 | #include "src/pcre2posix.h" 112 | #undef regcomp 113 | #undef regexec 114 | #undef regerror 115 | #undef regfree 116 | #define pcre2_regcomp regcomp 117 | #define pcre2_regexec regexec 118 | #define pcre2_regerror regerror 119 | #define pcre2_regfree regfree 120 | #include "src/pcre2posix.c" 121 | 122 | #include "src/pcre2_auto_possess.c" 123 | #include "src/pcre2_chartables.c.dist" 124 | #include "src/pcre2_chkdint.c" 125 | #include "src/pcre2_compile.c" 126 | #include "src/pcre2_context.c" 127 | #include "src/pcre2_extuni.c" 128 | #include "src/pcre2_find_bracket.c" 129 | #include "src/pcre2_match.c" 130 | #include "src/pcre2_match_data.c" 131 | #include "src/pcre2_newline.c" 132 | #include "src/pcre2_ord2utf.c" 133 | #include "src/pcre2_pattern_info.c" 134 | #include "src/pcre2_script_run.c" 135 | #include "src/pcre2_string_utils.c" 136 | #include "src/pcre2_study.c" 137 | #include "src/pcre2_tables.c" 138 | #include "src/pcre2_ucd.c" 139 | #include "src/pcre2_valid_utf.c" 140 | #include "src/pcre2_xclass.c" 141 | -------------------------------------------------------------------------------- /contrib/llama.mak: -------------------------------------------------------------------------------- 1 | # llama.cpp server and DLL build (CPU inference only) 2 | # 3 | # llama.cpp is an amazing project, but its build system is poor and 4 | # growing worse. It's never properly built llama.dll under any compiler, 5 | # and DLL builds have been unsupported by w64dk for some time. This 6 | # makefile is a replacement build system that produces llama.dll and 7 | # llama-server.exe using w64dk. No source file changes are needed. 8 | # 9 | # The DLL exports the public API and no more, and is readily usable as a 10 | # component in another project (game engine, etc.). The server EXE is 11 | # fully functional on Windows 7 or later. It is not linked against the 12 | # DLL, since that's not useful, but can be made to do so with a small 13 | # tweak to this makefile. 14 | # 15 | # Invoke this makefile in the llama.cpp source tree: 16 | # 17 | # $ make -j$(nproc) -f path/to/w64devkit/contrib/llama.mak 18 | # 19 | # Incremental builds are unsupported, so clean rebuild after pulling. It 20 | # was last tested at b5587, and an update will inevitably break it. 21 | 22 | CROSS = 23 | CPPFLAGS = -w -O2 24 | LDFLAGS = -s 25 | 26 | .SUFFIXES: .c .cpp .o 27 | def = -DGGML_USE_CPU 28 | inc = \ 29 | -I. \ 30 | -Icommon \ 31 | -Iggml/include \ 32 | -Iggml/src \ 33 | -Iggml/src/ggml-cpu \ 34 | -Iinclude \ 35 | -Itools/mtmd \ 36 | -Ivendor 37 | %.c.o: %.c 38 | $(CROSS)gcc -c -o $@ $(inc) $(def) $(CPPFLAGS) $< 39 | %.cpp.o: %.cpp 40 | $(CROSS)g++ -c -o $@ $(inc) $(def) $(CPPFLAGS) $< 41 | 42 | dll = \ 43 | ggml/src/ggml-alloc.c.o \ 44 | ggml/src/ggml-backend-reg.cpp.o \ 45 | ggml/src/ggml-backend.cpp.o \ 46 | ggml/src/ggml-cpu/binary-ops.cpp.o \ 47 | ggml/src/ggml-cpu/ggml-cpu-aarch64.cpp.o \ 48 | ggml/src/ggml-cpu/ggml-cpu-quants.c.o \ 49 | ggml/src/ggml-cpu/ggml-cpu-traits.cpp.o \ 50 | ggml/src/ggml-cpu/ggml-cpu.c.o \ 51 | ggml/src/ggml-cpu/ggml-cpu.cpp.o \ 52 | ggml/src/ggml-cpu/llamafile/sgemm.cpp.o \ 53 | ggml/src/ggml-cpu/ops.cpp.o \ 54 | ggml/src/ggml-cpu/unary-ops.cpp.o \ 55 | ggml/src/ggml-cpu/vec.cpp.o \ 56 | ggml/src/ggml-opt.cpp.o \ 57 | ggml/src/ggml-quants.c.o \ 58 | ggml/src/ggml-threading.cpp.o \ 59 | ggml/src/ggml.c.o \ 60 | ggml/src/gguf.cpp.o \ 61 | src/llama-adapter.cpp.o \ 62 | src/llama-arch.cpp.o \ 63 | src/llama-batch.cpp.o \ 64 | src/llama-chat.cpp.o \ 65 | src/llama-context.cpp.o \ 66 | src/llama-grammar.cpp.o \ 67 | src/llama-graph.cpp.o \ 68 | src/llama-hparams.cpp.o \ 69 | src/llama-impl.cpp.o \ 70 | src/llama-io.cpp.o \ 71 | src/llama-kv-cache-recurrent.cpp.o \ 72 | src/llama-kv-cache-unified-iswa.cpp.o \ 73 | src/llama-kv-cache-unified.cpp.o \ 74 | src/llama-kv-cache.cpp.o \ 75 | src/llama-mmap.cpp.o \ 76 | src/llama-model-loader.cpp.o \ 77 | src/llama-model-saver.cpp.o \ 78 | src/llama-model.cpp.o \ 79 | src/llama-quant.cpp.o \ 80 | src/llama-sampling.cpp.o \ 81 | src/llama-vocab.cpp.o \ 82 | src/llama.cpp.o \ 83 | src/unicode-data.cpp.o \ 84 | src/unicode.cpp.o 85 | 86 | exe = \ 87 | common/arg.cpp.o \ 88 | common/chat-parser.cpp.o \ 89 | common/chat.cpp.o \ 90 | common/common.cpp.o \ 91 | common/console.cpp.o \ 92 | common/json-partial.cpp.o \ 93 | common/json-schema-to-grammar.cpp.o \ 94 | common/log.cpp.o \ 95 | common/ngram-cache.cpp.o \ 96 | common/regex-partial.cpp.o \ 97 | common/sampling.cpp.o \ 98 | common/speculative.cpp.o \ 99 | common/w64dk-build-info.cpp.o \ 100 | tools/mtmd/clip.cpp.o \ 101 | tools/mtmd/mtmd-audio.cpp.o \ 102 | tools/mtmd/mtmd-helper.cpp.o \ 103 | tools/mtmd/mtmd.cpp.o \ 104 | tools/server/server.cpp.o 105 | 106 | all: llama.dll llama-server.exe 107 | 108 | llama-server.exe: $(exe) $(dll) 109 | $(CROSS)g++ $(LDFLAGS) -o $@ $(exe) $(dll) -lws2_32 110 | 111 | llama.dll: $(dll) llama.def 112 | $(CROSS)g++ -shared $(LDFLAGS) -o $@ $(dll) llama.def 113 | 114 | clean: 115 | rm -f $(dll) $(exe) llama.def llama.dll llama-server.exe \ 116 | tools/server/index.html.gz.hpp tools/server/loading.html.hpp \ 117 | common/w64dk-build-info.cpp 118 | 119 | .ONESHELL: # needed for heredocs 120 | 121 | # NOTE: produces valid C++ even if Git is unavailable 122 | common/w64dk-build-info.cpp: 123 | cat >$@ <../index.html.gz.hpp 134 | tools/server/loading.html.hpp: tools/server/public/loading.html 135 | cd tools/server/public/ && xxd -i loading.html >../loading.html.hpp 136 | tools/server/server.cpp.o: \ 137 | tools/server/server.cpp \ 138 | tools/server/index.html.gz.hpp \ 139 | tools/server/loading.html.hpp 140 | 141 | llama.def: 142 | @cat >$@ <&2; exit 1;; 38 | esac 39 | done 40 | shift $((OPTIND - 1)) 41 | 42 | if [ $# -gt 0 ]; then 43 | printf 'multibuild.sh: Too many arguments\n' >&2 44 | usage >&2 45 | exit 1 46 | fi 47 | 48 | if [ -z "$arch" ]; then 49 | arch="w64devkit-x64" 50 | fi 51 | if [ -z "$flavors" ]; then 52 | flavors="X" 53 | fi 54 | 55 | builds= 56 | for base in $arch; do 57 | for flavor in $flavors; do 58 | builds="$builds $base$(echo $flavor | tr -d X)" 59 | done 60 | done 61 | 62 | target="tmp-w64-$$" 63 | cleanup() { 64 | $dryrun git checkout . 65 | $dryrun git stash pop 66 | $dryrun docker rmi --no-prune $target || true 67 | } 68 | trap cleanup INT TERM 69 | 70 | $dryrun git stash 71 | for build in $builds; do 72 | $dryrun git checkout . 73 | ( 74 | IFS=- 75 | set $build; shift 76 | for flavor in "$@"; do 77 | if [ -e src/variant-$flavor.patch ]; then 78 | $dryrun patch -p1 -i src/variant-$flavor.patch 79 | fi 80 | done 81 | ) 82 | $dryrun docker build -t $target . 83 | if [ -n "$dryrun" ]; then 84 | $dryrun docker run --rm $target ">$build$suffix.7z.exe" 85 | else 86 | docker run --rm $target >$build$suffix.7z.exe 87 | fi 88 | done 89 | 90 | cleanup 91 | -------------------------------------------------------------------------------- /src/7z.mak: -------------------------------------------------------------------------------- 1 | CROSS = x86_64-w64-mingw32- 2 | CC = $(CROSS)gcc 3 | CXX = $(CROSS)g++ 4 | WINDRES = $(CROSS)windres 5 | LDFLAGS = -mwindows -s -Wl,--gc-sections 6 | LDLIBS = -lcomdlg32 -lole32 -loleaut32 -luuid 7 | CFLAGS = -fno-ident -Oz \ 8 | -DZ7_SFX \ 9 | -DZ7_EXTRACT_ONLY \ 10 | -DZ7_NO_CRYPTO \ 11 | -DZ7_NO_REGISTRY \ 12 | -DZ7_NO_READ_FROM_CODER \ 13 | 14 | obj = \ 15 | CPP/7zip/Bundles/SFXWin/resource.o \ 16 | CPP/7zip/Bundles/SFXWin/SfxWin.o \ 17 | CPP/7zip/UI/GUI/ExtractDialog.o \ 18 | CPP/7zip/UI/GUI/ExtractGUI.o \ 19 | CPP/Common/CRC.o \ 20 | CPP/Common/CommandLineParser.o \ 21 | CPP/Common/IntToString.o \ 22 | CPP/Common/NewHandler.o \ 23 | CPP/Common/MyString.o \ 24 | CPP/Common/StringConvert.o \ 25 | CPP/Common/MyVector.o \ 26 | CPP/Common/Wildcard.o \ 27 | CPP/Windows/Clipboard.o \ 28 | CPP/Windows/CommonDialog.o \ 29 | CPP/Windows/DLL.o \ 30 | CPP/Windows/ErrorMsg.o \ 31 | CPP/Windows/FileDir.o \ 32 | CPP/Windows/FileFind.o \ 33 | CPP/Windows/FileIO.o \ 34 | CPP/Windows/FileName.o \ 35 | CPP/Windows/MemoryGlobal.o \ 36 | CPP/Windows/PropVariant.o \ 37 | CPP/Windows/PropVariantConv.o \ 38 | CPP/Windows/ResourceString.o \ 39 | CPP/Windows/Shell.o \ 40 | CPP/Windows/Synchronization.o \ 41 | CPP/Windows/System.o \ 42 | CPP/Windows/Window.o \ 43 | CPP/Windows/Control/ComboBox.o \ 44 | CPP/Windows/Control/Dialog.o \ 45 | CPP/Windows/Control/ListView.o \ 46 | CPP/7zip/Common/CreateCoder.o \ 47 | CPP/7zip/Common/CWrappers.o \ 48 | CPP/7zip/Common/FilePathAutoRename.o \ 49 | CPP/7zip/Common/FileStreams.o \ 50 | CPP/7zip/Common/InBuffer.o \ 51 | CPP/7zip/Common/FilterCoder.o \ 52 | CPP/7zip/Common/LimitedStreams.o \ 53 | CPP/7zip/Common/OutBuffer.o \ 54 | CPP/7zip/Common/ProgressUtils.o \ 55 | CPP/7zip/Common/PropId.o \ 56 | CPP/7zip/Common/StreamBinder.o \ 57 | CPP/7zip/Common/StreamObjects.o \ 58 | CPP/7zip/Common/StreamUtils.o \ 59 | CPP/7zip/Common/VirtThread.o \ 60 | CPP/7zip/UI/Common/ArchiveExtractCallback.o \ 61 | CPP/7zip/UI/Common/ArchiveOpenCallback.o \ 62 | CPP/7zip/UI/Common/DefaultName.o \ 63 | CPP/7zip/UI/Common/Extract.o \ 64 | CPP/7zip/UI/Common/ExtractingFilePath.o \ 65 | CPP/7zip/UI/Common/LoadCodecs.o \ 66 | CPP/7zip/UI/Common/OpenArchive.o \ 67 | CPP/7zip/UI/Explorer/MyMessages.o \ 68 | CPP/7zip/UI/FileManager/BrowseDialog.o \ 69 | CPP/7zip/UI/FileManager/ComboDialog.o \ 70 | CPP/7zip/UI/FileManager/ExtractCallback.o \ 71 | CPP/7zip/UI/FileManager/FormatUtils.o \ 72 | CPP/7zip/UI/FileManager/OverwriteDialog.o \ 73 | CPP/7zip/UI/FileManager/PasswordDialog.o \ 74 | CPP/7zip/UI/FileManager/ProgressDialog2.o \ 75 | CPP/7zip/UI/FileManager/PropertyName.o \ 76 | CPP/7zip/UI/FileManager/SysIconUtils.o \ 77 | CPP/7zip/Archive/SplitHandler.o \ 78 | CPP/7zip/Archive/Common/CoderMixer2.o \ 79 | CPP/7zip/Archive/Common/ItemNameUtils.o \ 80 | CPP/7zip/Archive/Common/MultiStream.o \ 81 | CPP/7zip/Archive/Common/OutStreamWithCRC.o \ 82 | CPP/7zip/Archive/7z/7zDecode.o \ 83 | CPP/7zip/Archive/7z/7zExtract.o \ 84 | CPP/7zip/Archive/7z/7zHandler.o \ 85 | CPP/7zip/Archive/7z/7zIn.o \ 86 | CPP/7zip/Archive/7z/7zRegister.o \ 87 | CPP/7zip/Compress/Bcj2Coder.o \ 88 | CPP/7zip/Compress/Bcj2Register.o \ 89 | CPP/7zip/Compress/BcjCoder.o \ 90 | CPP/7zip/Compress/BcjRegister.o \ 91 | CPP/7zip/Compress/BranchMisc.o \ 92 | CPP/7zip/Compress/BranchRegister.o \ 93 | CPP/7zip/Compress/CopyCoder.o \ 94 | CPP/7zip/Compress/CopyRegister.o \ 95 | CPP/7zip/Compress/DeltaFilter.o \ 96 | CPP/7zip/Compress/Lzma2Decoder.o \ 97 | CPP/7zip/Compress/Lzma2Register.o \ 98 | CPP/7zip/Compress/LzmaDecoder.o \ 99 | CPP/7zip/Compress/LzmaRegister.o \ 100 | CPP/7zip/Compress/PpmdDecoder.o \ 101 | CPP/7zip/Compress/PpmdRegister.o \ 102 | C/7zCrc.o \ 103 | C/7zCrcOpt.o \ 104 | C/7zStream.o \ 105 | C/Alloc.o \ 106 | C/Bcj2.o \ 107 | C/Bra.o \ 108 | C/Bra86.o \ 109 | C/BraIA64.o \ 110 | C/CpuArch.o \ 111 | C/Delta.o \ 112 | C/DllSecur.o \ 113 | C/Lzma2Dec.o \ 114 | C/Lzma2DecMt.o \ 115 | C/LzmaDec.o \ 116 | C/MtDec.o \ 117 | C/Ppmd7.o \ 118 | C/Ppmd7Dec.o \ 119 | C/Sha256.o \ 120 | C/Sha256Opt.o \ 121 | C/Threads.o \ 122 | 123 | 7z.sfx: $(obj) 124 | $(CXX) $(LDFLAGS) -o $@ $(obj) $(LDLIBS) 125 | clean: 126 | rm -f 7z.sfx $(obj) 127 | %.o: %.cpp 128 | $(CXX) -c $(CFLAGS) -o $@ $^ 129 | %.o: %.c 130 | $(CC) -c $(CFLAGS) -o $@ $^ 131 | %.o: %.rc 132 | $(WINDRES) -o $@ $^ 133 | -------------------------------------------------------------------------------- /src/SHA256SUMS: -------------------------------------------------------------------------------- 1 | 356071007360e5a1824d9904993e8b2480b51b570e8c9faf7c0f58ebe4bf9f74 7z2301-src.tar.xz 2 | ce2017e059d63e67ddb9240e9d4ec49c2893605035cd60e92ad53177f4377237 binutils-2.44.tar.xz 3 | cde68932d0680939977344259182482341adfffac4558cd1592bc26d2b558311 busybox-w32-FRP-5579-g5749feb35.tgz 4 | 71229a73f25529c9e3dabb2cb7310c55405d31caee8e8a9ab5c71b2406d4005a ctags-6.0.0.tar.gz 5 | 25df13dd2819e85fb27a1ce0431772b7047d72af81ae78dc26b4c6e0805f48d1 expat-2.7.0.tar.xz 6 | e2b09ec21660f01fecffb715e0120265216943f038d0e48a9868713e54f06cea gcc-15.1.0.tar.xz 7 | 4002cb7f23f45c37c790536a13a720942ce4be0402d929c9085e92f10d480119 gdb-16.2.tar.xz 8 | a3c2b80201b89e68616f4ad30bc66aee4927c3ce50e33929ca819d5c43538898 gmp-6.3.0.tar.xz 9 | 3b08f5f4f9b4eb82f151a7040bfd6fe6c6fb922efe4b1659c66ea933276965e8 libiconv-1.18.tar.gz 10 | dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3 make-4.4.1.tar.gz 11 | 5afe822af5c4edbf67daaf45eec61d538f49eef6b19524de64897c6b95828caf mingw-w64-v13.0.0.tar.bz2 12 | ab642492f5cf882b74aa0cb730cd410a81edcdbec895183ce930e706c1c759b8 mpc-1.3.1.tar.gz 13 | b67ba0383ef7e8a8563734e2e889ef5ec3c3b898a01d00fa0a6869ad81c6ce01 mpfr-4.2.2.tar.xz 14 | 590dbe0f5835f66992df096d3602d0271103f90cf8557a5d124f693c2b40d7ec PDCurses-3.9.tar.gz 15 | a6456bc154999d83d0c20d968ac7ba6e7df0d02f3cb6427fb248660bacfb336e vim-9.0.tar.bz2 16 | -------------------------------------------------------------------------------- /src/alias.c: -------------------------------------------------------------------------------- 1 | // Well-behaved command line aliases for w64devkit 2 | // 3 | // Unlike batch script aliases, this program will not produce an annoying 4 | // and useless "Terminate batch job (Y/N)" prompt. When compiling, define 5 | // EXE as the target executable (relative or absolute path), and define CMD 6 | // as the argv[0] replacement, including additional arguments. 7 | // 8 | // $ gcc -DEXE="target.exe" -DCMD="argv0 argv1" 9 | // -nostartfiles -fno-builtin -o alias.exe alias.c 10 | // 11 | // This is free and unencumbered software released into the public domain. 12 | 13 | #define sizeof(x) (i32)sizeof(x) 14 | #define alignof(x) (i32)_Alignof(x) 15 | #define countof(a) (sizeof(a) / sizeof(*(a))) 16 | #define lengthof(s) (countof(s) - 1) 17 | 18 | #define LSTR(s) XSTR(s) 19 | #define XSTR(s) u ## # s 20 | #define LEXE LSTR(EXE) 21 | #define LCMD LSTR(CMD) 22 | 23 | typedef __UINT8_TYPE__ u8; 24 | typedef signed short i16; 25 | typedef signed int b32; 26 | typedef signed int i32; 27 | typedef unsigned int u32; 28 | typedef unsigned char byte; 29 | typedef __UINTPTR_TYPE__ uptr; 30 | typedef __SIZE_TYPE__ usize; 31 | typedef unsigned short char16_t; // for GDB 32 | typedef char16_t c16; 33 | 34 | // Win32 35 | 36 | #define MAX_PATH 260 37 | #define MAX_CMDLINE (1<<15) 38 | 39 | typedef struct {} *handle; 40 | 41 | typedef b32 __stdcall handler(i32); 42 | 43 | typedef struct { 44 | u32 cb; 45 | uptr a, b, c; 46 | i32 d, e, f, g, h, i, j, k; 47 | i16 l, m; 48 | uptr n, o, p, q; 49 | } si; 50 | 51 | typedef struct { 52 | handle process; 53 | handle thread; 54 | u32 pid; 55 | u32 tid; 56 | } pi; 57 | 58 | #define W32 __attribute((dllimport, stdcall)) 59 | W32 b32 CreateProcessW(c16*,c16*,void*,void*,b32,u32,c16*,c16*,si*,pi*); 60 | W32 void ExitProcess(u32) __attribute((noreturn)); 61 | W32 c16 *GetCommandLineW(void); 62 | W32 i32 GetExitCodeProcess(handle, u32 *); 63 | W32 u32 GetFullPathNameW(c16 *, u32, c16 *, c16 *); 64 | W32 u32 GetModuleFileNameW(handle, c16 *, u32); 65 | W32 handle GetStdHandle(u32); 66 | W32 b32 SetConsoleCtrlHandler(handler, b32); 67 | W32 byte *VirtualAlloc(byte *, usize, u32, u32); 68 | W32 b32 VirtualFree(byte *, usize, u32); 69 | W32 u32 WaitForSingleObject(handle, u32); 70 | W32 b32 WriteFile(handle, u8 *, u32, u32 *, void *); 71 | 72 | // Application 73 | 74 | #define ERR(s) "w64devkit (alias): " s "\n" 75 | 76 | #define new(h, t, n) (t *)alloc(h, sizeof(t), alignof(t), n) 77 | __attribute((malloc)) 78 | __attribute((alloc_align(3))) 79 | __attribute((alloc_size(2, 4))) 80 | static byte *alloc(byte **heap, i32 size, i32 align, i32 count) 81 | { 82 | *heap += -(uptr)*heap & (align - 1); 83 | byte *p = *heap; 84 | *heap += size * count; 85 | return p; 86 | } 87 | 88 | typedef struct { 89 | c16 *buf; 90 | i32 len; 91 | i32 cap; 92 | b32 err; 93 | } c16buf; 94 | 95 | static void append(c16buf *b, c16 *s, i32 len) 96 | { 97 | i32 avail = b->cap - b->len; 98 | i32 count = availbuf[b->len+i] = s[i]; 101 | } 102 | b->len += count; 103 | b->err |= avail < len; 104 | } 105 | 106 | // Find the end of argv[0]. 107 | static c16 *findargs(c16 *s) 108 | { 109 | if (s[0] == '"') { 110 | for (s++;; s++) { // quoted argv[0] 111 | switch (*s) { 112 | case 0: return s; 113 | case '"': return s + 1; 114 | } 115 | } 116 | } else { 117 | for (;; s++) { // unquoted argv[0] 118 | switch (*s) { 119 | case 0: 120 | case '\t': 121 | case ' ': return s; 122 | } 123 | } 124 | } 125 | } 126 | 127 | static i32 c16len(c16 *s) 128 | { 129 | i32 len = 0; 130 | for (; s[len]; len++) {} 131 | return len; 132 | } 133 | 134 | static u32 fatal(u8 *msg, i32 len) 135 | { 136 | handle stderr = GetStdHandle(-12); 137 | u32 dummy; 138 | WriteFile(stderr, msg, len, &dummy, 0); 139 | return 0x17e; 140 | } 141 | 142 | static void getmoduledir(c16buf *b) 143 | { 144 | c16 path[MAX_PATH]; 145 | u32 len = GetModuleFileNameW(0, path, countof(path)); 146 | for (; len; len--) { 147 | switch (path[len-1]) { 148 | case '/': case '\\': 149 | append(b, path, len); 150 | return; 151 | } 152 | } 153 | } 154 | 155 | static si *newstartupinfo(byte **heap) 156 | { 157 | si *s = new(heap, si, 1); 158 | s->cb = sizeof(si); 159 | return s; 160 | } 161 | 162 | static b32 __stdcall ignorectrlc(i32) 163 | { 164 | return 1; 165 | } 166 | 167 | static i32 aliasmain(void) 168 | { 169 | byte *heap_start = VirtualAlloc(0, 1<<18, 0x3000, 4); 170 | byte *heap = heap_start; 171 | if (!heap) { 172 | static const u8 msg[] = ERR("out of memory"); 173 | return fatal((u8 *)msg, lengthof(msg)); 174 | } 175 | 176 | // Construct a path to the .exe 177 | c16buf *exe = new(&heap, c16buf, 1); 178 | exe->cap = 2*MAX_PATH; // concatenating two paths 179 | exe->buf = new(&heap, c16, exe->cap); 180 | if (LEXE[1] != ':') { // relative path? 181 | getmoduledir(exe); 182 | } 183 | append(exe, LEXE, countof(LEXE)); 184 | if (exe->err) { 185 | static const u8 msg[] = ERR(".exe path too long"); 186 | return fatal((u8 *)msg, lengthof(msg)); 187 | } 188 | 189 | // Try to collapse relative components 190 | if (LEXE[0] == '.') { // relative components? 191 | c16buf *tmp = new(&heap, c16buf, 1); 192 | tmp->buf = new(&heap, c16, MAX_PATH); 193 | tmp->cap = MAX_PATH; 194 | tmp->len = GetFullPathNameW(exe->buf, tmp->cap, tmp->buf, 0); 195 | if (tmp->len>0 && tmp->lencap) { 196 | tmp->len++; // include null terminator 197 | *exe = *tmp; 198 | } 199 | } 200 | 201 | // Construct a new command line string 202 | c16buf *cmd = new(&heap, c16buf, 1); 203 | cmd->cap = MAX_CMDLINE; 204 | cmd->buf = new(&heap, c16, cmd->cap); 205 | append(cmd, LCMD, lengthof(LCMD)); 206 | c16 *args = findargs(GetCommandLineW()); 207 | append(cmd, args, c16len(args)+1); 208 | if (cmd->err) { 209 | static const u8 msg[] = ERR("command line too long"); 210 | return fatal((u8 *)msg, lengthof(msg)); 211 | } 212 | 213 | si *si = newstartupinfo(&heap); 214 | pi pi; 215 | SetConsoleCtrlHandler(ignorectrlc, 1); // NOTE: set as late a possible 216 | if (!CreateProcessW(exe->buf, cmd->buf, 0, 0, 1, 0, 0, 0, si, &pi)) { 217 | static const u8 msg[] = ERR("could not start process\n"); 218 | return fatal((u8 *)msg, lengthof(msg)); 219 | } 220 | 221 | // Wait for child to exit 222 | VirtualFree(heap_start, 0, 0x8000); 223 | u32 ret; 224 | WaitForSingleObject(pi.process, -1); 225 | GetExitCodeProcess(pi.process, &ret); 226 | return ret; 227 | } 228 | 229 | __attribute((force_align_arg_pointer)) 230 | void mainCRTStartup(void) 231 | { 232 | u32 r = aliasmain(); 233 | ExitProcess(r); 234 | } 235 | -------------------------------------------------------------------------------- /src/binutils-dlltool-zero-ordinals.patch: -------------------------------------------------------------------------------- 1 | If the .def author does not pick an ordinal then it has no meaningful 2 | ordinal. It never makes sense to invent unique hints. That populates 3 | binaries with noise and inhibits reproducible builds. Instead zero out 4 | all ordinal hints to indicate "no data." In Mingw-w64 import libraries 5 | virtually every implicit ordinal hint is nonsense. 6 | 7 | The singular exception might be ordinal-only, NONAME entries without an 8 | explicit ordinal, which instead relies on implicit, undocumented ordinal 9 | numbering. In that case it's not merely a hint. However, this is unsound 10 | and ought to be a syntax error, as it is with MSVC lib.exe. (This patch 11 | should probably make it so.) I could find no examples of this situation 12 | in the wild. 13 | 14 | --- a/binutils/dlltool.c 15 | +++ b/binutils/dlltool.c 16 | @@ -3701,2 +3701,3 @@ 17 | done:; 18 | + d_export_vec[i]->ordinal = 0; 19 | } 20 | @@ -3759,3 +3760,3 @@ 21 | if (!d_exports_lexically[i]->noname || show_allnames) 22 | - d_exports_lexically[i]->hint = hint++; 23 | + d_exports_lexically[i]->hint = 0; 24 | 25 | -------------------------------------------------------------------------------- /src/binutils-exclude-sysroot.patch: -------------------------------------------------------------------------------- 1 | In bfd is a list of known libraries whose symbols are excluded when 2 | exporting all symbols, the default behavior when no exports are chosen, 3 | or with --export-all-symbols. However, the list is incomplete, and the 4 | following "standard" libraries in w64dk also contain definitions that 5 | should never be exported: 6 | 7 | * libdloadhelper.a, 8 | * libdxerr8.a, 9 | * libdxerr9.a, 10 | * libgmon.a, 11 | * libmsvcr100.a, 12 | * libmsvcr110.a, 13 | * libmsvcr120.a, 14 | * libmsvcr120_app.a, 15 | * libmsvcr120d.a, 16 | * libmsvcr80.a, 17 | * libmsvcr90.a, 18 | * libmsvcr90d.a, 19 | * libpthread.a (!!!), 20 | * libucrtapp.a, and 21 | * libwinpthread.a 22 | 23 | The result is that Windows-unaware builds using any of these libraries, 24 | including any C++ program due to libpthread, tends to be broken. Instead 25 | of adding each library to the list, blanket-exclude all libraries that 26 | come from the sysroot. Since libpthread is so important, also add it to 27 | the known list. Its absence is essentially a Binutils bug. 28 | 29 | --- a/ld/pe-dll.c 30 | +++ b/ld/pe-dll.c 31 | @@ -347,4 +347,5 @@ 32 | static const autofilter_entry_type autofilter_liblist[] = 33 | { 34 | + { STRING_COMMA_LEN ("libpthread") }, 35 | { STRING_COMMA_LEN ("libcegcc") }, 36 | { STRING_COMMA_LEN ("libcygwin") }, 37 | @@ -575,4 +576,16 @@ 38 | libname = lbasename (bfd_get_filename (abfd->my_archive)); 39 | 40 | + if (abfd && abfd->my_archive && ld_canon_sysroot) 41 | + { 42 | + const char *path = bfd_get_filename (abfd->my_archive); 43 | + const char *real = lrealpath (path); 44 | + int len = ld_canon_sysroot_len; 45 | + int match = !filename_ncmp (real, ld_canon_sysroot, len) 46 | + && IS_DIR_SEPARATOR (real[len]); 47 | + free (real); 48 | + if (match) 49 | + return 0; 50 | + } 51 | + 52 | key.name = key.its_name = (char *) n; 53 | 54 | -------------------------------------------------------------------------------- /src/busybox-000-disable-beep.patch: -------------------------------------------------------------------------------- 1 | --- a/libbb/lineedit.c 2 | +++ b/libbb/lineedit.c 3 | @@ -537,5 +537,4 @@ 4 | static void beep(void) 5 | { 6 | - bb_putchar_stderr('\007'); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/busybox-001-disable-empty-complete.patch: -------------------------------------------------------------------------------- 1 | --- a/libbb/lineedit.c 2 | +++ b/libbb/lineedit.c 3 | @@ -860,6 +860,14 @@ 4 | const char *basecmd; 5 | char *dirbuf = NULL; 6 | 7 | + /* Discovering all commands in PATH is painfully expensive (typically 8 | + * several seconds) on Windows where file access is slow, so don't do 9 | + * it. Worse, due to an alt-tab handling bug in the Windows console 10 | + * it's very easy to activate this completion by accident. 11 | + */ 12 | + if (type == FIND_EXE_ONLY && !command[0]) 13 | + return 0; 14 | + 15 | npaths = 1; 16 | path1[0] = (char*)"."; 17 | 18 | -------------------------------------------------------------------------------- /src/busybox-002-default-noiconify.patch: -------------------------------------------------------------------------------- 1 | --- a/shell/ash.c 2 | +++ b/shell/ash.c 3 | @@ -12686,4 +12686,5 @@ 4 | #if ENABLE_ASH_NOCONSOLE 5 | noconsole = console_state(); 6 | + noiconify = 1; 7 | #endif 8 | if (login_sh != NULL) { /* if we came from startup code */ 9 | -------------------------------------------------------------------------------- /src/busybox-004-system-profile.patch: -------------------------------------------------------------------------------- 1 | --- a/shell/ash.c 2 | +++ b/shell/ash.c 3 | @@ -16349,3 +16349,3 @@ 4 | 5 | - hp = exe_relative_path("/etc/profile"); 6 | + hp = exe_relative_path("../src/profile"); 7 | read_profile(hp); 8 | -------------------------------------------------------------------------------- /src/busybox-005-disable-utf8-check.patch: -------------------------------------------------------------------------------- 1 | Allow it to run without UTF-8 support, such as on Windows 7. 2 | --- a/libbb/appletlib.c 3 | +++ b/libbb/appletlib.c 4 | @@ -1331,3 +1331,3 @@ 5 | #if ENABLE_PLATFORM_MINGW32 6 | -# if ENABLE_FEATURE_UTF8_MANIFEST 7 | +# if ENABLE_FEATURE_UTF8_MANIFEST && 0 8 | if (GetACP() != CP_UTF8) { 9 | -------------------------------------------------------------------------------- /src/busybox-alias.c: -------------------------------------------------------------------------------- 1 | // busybox-w32 command alias 2 | // Starts the adjacent busybox.exe with an unmodified command line. 3 | // $ cc -nostartfiles -fno-builtin -o COMMAND busybox-alias.c 4 | // This is free and unencumbered software released into the public domain. 5 | 6 | #define sizeof(x) (i32)sizeof(x) 7 | #define countof(a) (sizeof(a) / sizeof(*(a))) 8 | #define lengthof(s) (countof(s) - 1) 9 | 10 | typedef __UINT8_TYPE__ u8; 11 | typedef signed short i16; 12 | typedef signed int b32; 13 | typedef signed int i32; 14 | typedef unsigned int u32; 15 | typedef unsigned short char16_t; // for GDB 16 | typedef char16_t c16; 17 | 18 | #define MAX_PATH 260 19 | 20 | typedef struct {} *handle; 21 | 22 | typedef b32 __stdcall handler(i32); 23 | 24 | typedef struct { 25 | u32 cb; 26 | void *a, *b, *c; 27 | i32 d, e, f, g, h, i, j, k; 28 | i16 l, m; 29 | void *n, *o, *p, *q; 30 | } si; 31 | 32 | typedef struct { 33 | handle process; 34 | handle thread; 35 | u32 pid; 36 | u32 tid; 37 | } pi; 38 | 39 | #define W32 __attribute((dllimport, stdcall)) 40 | W32 b32 CreateProcessW(c16*,c16*,void*,void*,b32,u32,c16*,c16*,si*,pi*); 41 | W32 void ExitProcess(u32) __attribute((noreturn)); 42 | W32 c16 *GetCommandLineW(void); 43 | W32 i32 GetExitCodeProcess(handle, u32 *); 44 | W32 u32 GetModuleFileNameW(handle, c16 *, u32); 45 | W32 handle GetStdHandle(u32); 46 | W32 b32 SetConsoleCtrlHandler(handler, b32); 47 | W32 u32 WaitForSingleObject(handle, u32); 48 | W32 b32 WriteFile(handle, u8 *, u32, u32 *, void *); 49 | 50 | typedef struct { 51 | c16 *buf; 52 | i32 len; 53 | i32 cap; 54 | b32 err; 55 | } c16buf; 56 | 57 | static void append(c16buf *b, c16 *buf, i32 len) 58 | { 59 | i32 avail = b->cap - b->len; 60 | i32 count = availbuf + b->len; 62 | for (i32 i = 0; i < count; i++) { 63 | dst[i] = buf[i]; 64 | } 65 | b->len += count; 66 | b->err |= count < len; 67 | } 68 | 69 | static void getmoduledir(c16buf *b) 70 | { 71 | c16 path[MAX_PATH]; 72 | u32 len = GetModuleFileNameW(0, path, countof(path)); 73 | for (; len; len--) { 74 | switch (path[len-1]) { 75 | case '/': case '\\': 76 | append(b, path, len-1); 77 | return; 78 | } 79 | } 80 | } 81 | 82 | static u32 fatal(u8 *msg, i32 len) 83 | { 84 | handle stderr = GetStdHandle(-12); 85 | u32 dummy; 86 | WriteFile(stderr, msg, len, &dummy, 0); 87 | return 0x17e; 88 | } 89 | 90 | static b32 __stdcall ignorectrlc(i32) 91 | { 92 | return 1; 93 | } 94 | 95 | static u32 run(void) 96 | { 97 | c16 buf[MAX_PATH]; 98 | c16buf exe = {}; 99 | exe.buf = buf; 100 | exe.cap = countof(buf); 101 | 102 | getmoduledir(&exe); 103 | c16 busybox[] = u"\\busybox.exe"; 104 | append(&exe, busybox, countof(busybox)); 105 | if (exe.err) { 106 | static u8 msg[] = "w64devkit: busybox.exe path too long\n"; 107 | return fatal(msg, lengthof(msg)); 108 | } 109 | 110 | si si = {}; 111 | si.cb = sizeof(si); 112 | pi pi; 113 | c16 *cmdline = GetCommandLineW(); 114 | SetConsoleCtrlHandler(ignorectrlc, 1); // NOTE: set as late a possible 115 | if (!CreateProcessW(exe.buf, cmdline, 0, 0, 1, 0, 0, 0, &si, &pi)) { 116 | static u8 msg[] = "w64devkit: could not start busybox.exe\n"; 117 | return fatal(msg, lengthof(msg)); 118 | } 119 | 120 | u32 ret; 121 | WaitForSingleObject(pi.process, -1); 122 | GetExitCodeProcess(pi.process, &ret); 123 | return ret; 124 | } 125 | 126 | __attribute((force_align_arg_pointer)) 127 | void mainCRTStartup(void) 128 | { 129 | u32 r = run(); 130 | ExitProcess(r); 131 | } 132 | -------------------------------------------------------------------------------- /src/crossgcc-relocatable.patch: -------------------------------------------------------------------------------- 1 | Relocatable mingw paths (c/o Hannes Domani) 2 | --- a/gcc/config/mingw/mingw32.h 3 | +++ b/gcc/config/mingw/mingw32.h 4 | @@ -91,3 +91,3 @@ 5 | #undef NATIVE_SYSTEM_HEADER_COMPONENT 6 | -#define NATIVE_SYSTEM_HEADER_COMPONENT "MINGW" 7 | +#undef NATIVE_SYSTEM_HEADER_DIR 8 | 9 | @@ -234,3 +234,3 @@ 10 | #ifndef STANDARD_STARTFILE_PREFIX_1 11 | -#define STANDARD_STARTFILE_PREFIX_1 "/mingw/lib/" 12 | +#define STANDARD_STARTFILE_PREFIX_1 "" 13 | #endif 14 | @@ -240,7 +240,2 @@ 15 | 16 | -/* For native mingw-version we need to take care that NATIVE_SYSTEM_HEADER_DIR 17 | - macro contains POSIX-style path. See bug 52947. */ 18 | -#undef NATIVE_SYSTEM_HEADER_DIR 19 | -#define NATIVE_SYSTEM_HEADER_DIR "/mingw/include" 20 | - 21 | /* Output STRING, a string representing a filename, to FILE. 22 | --- a/gcc/incpath.cc 23 | +++ b/gcc/incpath.cc 24 | @@ -191,4 +191,3 @@ 25 | } 26 | - else if (!p->add_sysroot && relocated 27 | - && !filename_ncmp (p->fname, cpp_PREFIX, cpp_PREFIX_len)) 28 | + if (relocated && !filename_ncmp (p->fname, cpp_PREFIX, cpp_PREFIX_len)) 29 | { 30 | -------------------------------------------------------------------------------- /src/debugbreak.c: -------------------------------------------------------------------------------- 1 | // debugbreak - raise a breakpoint exception in all Win32 debuggees 2 | // 3 | // Graphical programs with windows can rely on the F12 hotkey to break in 4 | // the attached debugger, but console programs have no such hotkey. This 5 | // program fills that gap, breaking your console program mid-execution, 6 | // such as when it's stuck in an infinite loop. 7 | // 8 | // Mingw-w64: 9 | // gcc -Os -fno-asynchronous-unwind-tables -Wl,--gc-sections -s -nostdlib 10 | // -o debugbreak.exe debugbreak.c -lkernel32 11 | // 12 | // MSVC: 13 | // cl /GS- /Os debugbreak.c 14 | // 15 | // This is free and unencumbered software released into the public domain. 16 | #define WIN32_LEAN_AND_MEAN 17 | #include 18 | #include 19 | #if defined(_MSC_VER) 20 | # pragma comment(lib, "kernel32") 21 | # pragma comment(linker, "/subsystem:console") 22 | #endif 23 | 24 | // Try to put data in .text to pack the binary even smaller. 25 | #if __GNUC__ 26 | # define STATIC __attribute__((section(".text.data"))) static const 27 | #else 28 | # define STATIC static const 29 | #endif 30 | 31 | STATIC char usage[] = 32 | "Usage: debugbreak\n" 33 | " raise a breakpoint exception in all Win32 debuggees\n"; 34 | 35 | int 36 | mainCRTStartup(void) 37 | { 38 | // Skip argv[0] and space separator. This avoids linking shell32.dll 39 | // for CommandLineToArgvW just to count the arguments. 40 | wchar_t *cmd = GetCommandLineW(); 41 | switch (*cmd) { 42 | default : for (; *cmd && *cmd != '\t' && *cmd != ' '; cmd++); 43 | break; 44 | case '"': for (cmd++; *cmd && *cmd != '"'; cmd++); 45 | cmd += !!*cmd; 46 | } 47 | for (; *cmd == '\t' || *cmd == ' '; cmd++); 48 | 49 | // Print usage and fail if argc > 1. The program's purpose will be 50 | // more discoverable, including responding to -h/--help. 51 | if (*cmd) { 52 | DWORD n; 53 | HANDLE h = GetStdHandle(STD_ERROR_HANDLE); 54 | WriteFile(h, usage, sizeof(usage)-1, &n, 0); 55 | return 1; 56 | } 57 | 58 | // Cannot fail with this configuration 59 | HANDLE s = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 60 | 61 | PROCESSENTRY32W p = {sizeof(p)}; 62 | for (BOOL r = Process32FirstW(s, &p); r; r = Process32NextW(s, &p)) { 63 | // 64-bit only requires PROCESS_CREATE_THREAD. 32-bit additionally 64 | // requires PROCESS_VM_OPERATION and PROCESS_VM_WRITE. Since it's 65 | // not clearly documented, just ask for PROCESS_ALL_ACCESS in case 66 | // it changes in the future. 67 | HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, 0, p.th32ProcessID); 68 | if (h) { 69 | // If the process has no debugger attached, nothing happens. 70 | // Otherwise this would require CheckRemoteDebuggerPresent to 71 | // avoid terminating normal processes. 72 | DebugBreakProcess(h); 73 | CloseHandle(h); 74 | } 75 | } 76 | int status = GetLastError() != ERROR_NO_MORE_FILES; 77 | ExitProcess(status); 78 | } 79 | -------------------------------------------------------------------------------- /src/gcc-avx-misaligned.patch: -------------------------------------------------------------------------------- 1 | Treat AVX register spills as always unaligned, eliminating the 2 | requirement for -Wa,-muse-unaligned-vector-move when targeting 3 | AVX-capable hardware. 4 | 5 | https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412 6 | 7 | --- a/gcc/config/i386/predicates.md 8 | +++ b/gcc/config/i386/predicates.md 9 | @@ -1695,5 +1695,6 @@ 10 | (define_predicate "misaligned_operand" 11 | (and (match_code "mem") 12 | - (match_test "MEM_ALIGN (op) < GET_MODE_BITSIZE (mode)"))) 13 | + (ior (match_test "MEM_SIZE (op) > 16") 14 | + (match_test "MEM_ALIGN (op) < GET_MODE_BITSIZE (mode)")))) 15 | 16 | ;; Return true if OP is a parallel for an mov{d,q,dqa,ps,pd} vec_select, 17 | -------------------------------------------------------------------------------- /src/gcc-stdcall-align.patch: -------------------------------------------------------------------------------- 1 | GCC requires 16-byte stack alignment on function entry, but x86 calling 2 | conventions only provide 4-byte alignment. Entry points, such as thread 3 | procedures, callbacks, and process entry points, are likely called on a 4 | stack not aligned to GCC's expectations. Since these functions are also 5 | typically __stdcall, silently add the force_align_arg_pointer attribute 6 | to such functions so that GCC re-aligns the stack. 7 | 8 | Note: main, wmain, WinMain, and wWinMain are special cases which the CRT 9 | calls with 16-byte alignment. 10 | 11 | --- a/gcc/config/i386/i386-options.cc 12 | +++ b/gcc/config/i386/i386-options.cc 13 | @@ -3689,7 +3689,7 @@ 14 | arguments as in struct attribute_spec.handler. */ 15 | 16 | static tree 17 | -ix86_handle_cconv_attribute (tree *node, tree name, tree args, int, 18 | +ix86_handle_cconv_attribute (tree *node, tree name, tree args, int flags, 19 | bool *no_add_attrs) 20 | { 21 | if (TREE_CODE (*node) != FUNCTION_TYPE 22 | @@ -3773,6 +3773,11 @@ 23 | sseregparm. */ 24 | else if (is_attribute_p ("stdcall", name)) 25 | { 26 | + if (!TARGET_64BIT) 27 | + { 28 | + tree attr = tree_cons (get_identifier ("force_align_arg_pointer"), NULL_TREE, NULL_TREE); 29 | + decl_attributes (node, attr, flags); 30 | + } 31 | if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (*node))) 32 | { 33 | error ("stdcall and cdecl attributes are not compatible"); 34 | -------------------------------------------------------------------------------- /src/gcc-trap-terminate.patch: -------------------------------------------------------------------------------- 1 | Replace std::terminate's std::abort with a trap instruction 2 | 3 | MSVCRT abort() merely calls exit(), which does not properly signal a 4 | hard error, nor does it trap in debuggers. It's a poor experience. In 5 | contrast, a trap instruction accomplishes both. 6 | 7 | --- a/libstdc++-v3/libsupc++/eh_term_handler.h 8 | +++ b/libstdc++-v3/libsupc++/eh_term_handler.h 9 | @@ -36,4 +36,4 @@ 10 | #else 11 | # include 12 | -# define _GLIBCXX_DEFAULT_TERM_HANDLER std::abort 13 | +# define _GLIBCXX_DEFAULT_TERM_HANDLER __gnu_cxx::__trap_terminate_handler 14 | #endif 15 | --- a/libstdc++-v3/libsupc++/exception 16 | +++ b/libstdc++-v3/libsupc++/exception 17 | @@ -160,4 +160,6 @@ 18 | void __verbose_terminate_handler(); 19 | 20 | + void __trap_terminate_handler(); 21 | + 22 | _GLIBCXX_END_NAMESPACE_VERSION 23 | } // namespace 24 | --- a/libstdc++-v3/libsupc++/vterminate.cc 25 | +++ b/libstdc++-v3/libsupc++/vterminate.cc 26 | @@ -93,7 +93,9 @@ 27 | fputs("terminate called without an active exception\n", stderr); 28 | 29 | - abort(); 30 | + __builtin_trap(); 31 | } 32 | 33 | + void __trap_terminate_handler() { __builtin_trap(); } 34 | + 35 | _GLIBCXX_END_NAMESPACE_VERSION 36 | } // namespace 37 | -------------------------------------------------------------------------------- /src/gdb-000-alternate-main.patch: -------------------------------------------------------------------------------- 1 | --- a/gdb/symtab.c 2 | +++ b/gdb/symtab.c 3 | @@ -6599,6 +6599,23 @@ 4 | if (symbol_found_p) 5 | return; 6 | 7 | + if (gdbarch_osabi (current_inferior ()->arch ()) == GDB_OSABI_WINDOWS) 8 | + { 9 | + static const char *const mains[] = { 10 | + "WinMain", "wWinMain", "main", "wmain", "WinMainCRTStartup", "mainCRTStartup" 11 | + }; 12 | + for (const char *main : mains) 13 | + { 14 | + struct bound_minimal_symbol msym; 15 | + msym = lookup_minimal_symbol (current_program_space, main); 16 | + if (msym.minsym != NULL) 17 | + { 18 | + set_main_name (pspace, main, language_unknown); 19 | + return; 20 | + } 21 | + } 22 | + } 23 | + 24 | set_main_name (pspace, "main", language_unknown); 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/gdb-001-confirm-off.patch: -------------------------------------------------------------------------------- 1 | GDB's definition of "potentially dangerous" is far too broad. This option 2 | creates substantial friction and should be off by default. 3 | 4 | --- a/gdb/top.c 5 | +++ b/gdb/top.c 6 | @@ -133,3 +133,3 @@ 7 | 8 | -bool confirm = true; 9 | +bool confirm = false; 10 | 11 | -------------------------------------------------------------------------------- /src/gendef-silent.patch: -------------------------------------------------------------------------------- 1 | --- a/mingw-w64-tools/gendef/src/gendef.c 2 | +++ b/mingw-w64-tools/gendef/src/gendef.c 3 | @@ -319,5 +319,4 @@ 4 | { 5 | gPEPDta = NULL; 6 | - fprintf (stderr, " * [%s] Found PE image\n", fninput); 7 | } 8 | else if (gPEDta->FileHeader.SizeOfOptionalHeader >= offsetof(IMAGE_OPTIONAL_HEADER32, DataDirectory) 9 | @@ -325,5 +324,4 @@ 10 | { 11 | gPEPDta = NULL; 12 | - fprintf (stderr, " * [%s] Found PE image for I386 with zeroed NT magic\n", fninput); 13 | } 14 | else if (gPEPDta->FileHeader.SizeOfOptionalHeader >= offsetof(IMAGE_OPTIONAL_HEADER64, DataDirectory) 15 | @@ -331,5 +329,4 @@ 16 | { 17 | gPEDta = NULL; 18 | - fprintf (stderr, " * [%s] Found PE+ image\n", fninput); 19 | } 20 | else 21 | @@ -683,6 +680,4 @@ 22 | return; 23 | } 24 | - fprintf (fp,";\n; Definition file of %s\n; Automatic generated by gendef %s\n; written by Kai Tietz 2008\n; The def file has to be processed by --kill-at (-k) option of dlltool or ld\n;\n", 25 | - fndllname, VERSION); 26 | fprintf (fp,"LIBRARY \"%s\"\nEXPORTS\n",fndllname); 27 | while ((exp = gExp) != NULL) 28 | -------------------------------------------------------------------------------- /src/libchkstk.S: -------------------------------------------------------------------------------- 1 | #if 0 2 | # Implementations of ___chkstk_ms (GCC) and __chkstk (MSVC). Unlike 3 | # libgcc, no work happens if the stack is already committed. Execute 4 | # this source with a shell to build libchkstk.a. 5 | # This is free and unencumbered software released into the public domain. 6 | set -ex 7 | ${CC:-cc} -c -DCHKSTK_MS -Wa,--no-pad-sections -o chkstk_ms.o $0 8 | ${CC:-cc} -c -DCHKSTK -Wa,--no-pad-sections -o chkstk.o $0 9 | rm -f "${DESTDIR}libchkstk.a" 10 | ${AR:-ar} r "${DESTDIR}libchkstk.a" chkstk_ms.o chkstk.o 11 | rm chkstk_ms.o chkstk.o 12 | exit 0 13 | #endif 14 | 15 | #if __amd64 16 | // On x64, ___chkstk_ms and __chkstk have identical semantics. Unlike 17 | // x86 __chkstk, neither adjusts the stack pointer. This implementation 18 | // preserves all registers. 19 | // 20 | // The frame size is passed in rax, and this function ensures that 21 | // enough of the stack is committed for the frame. It commits stack 22 | // pages by writing to the guard page, one page at a time. 23 | # if CHKSTK_MS 24 | .globl ___chkstk_ms 25 | ___chkstk_ms: 26 | # elif CHKSTK 27 | .globl __chkstk 28 | __chkstk: 29 | # endif 30 | push %rax 31 | push %rcx 32 | mov %gs:(0x10), %rcx // rcx = stack low address 33 | neg %rax // rax = frame low address 34 | add %rsp, %rax // " 35 | jb 1f // frame low address overflow? 36 | xor %eax, %eax // overflowed: frame low address = null 37 | 0: sub $0x1000, %rcx // extend stack into guard page 38 | test %eax, (%rcx) // commit page (two instruction bytes) 39 | 1: cmp %rax, %rcx 40 | ja 0b 41 | pop %rcx 42 | pop %rax 43 | ret 44 | #endif // __amd64 45 | 46 | #if __i386 47 | # if CHKSTK_MS 48 | // Behaves exactly like x64 ___chkstk_ms. 49 | .globl ___chkstk_ms 50 | ___chkstk_ms: 51 | push %eax 52 | push %ecx 53 | mov %fs:(0x08), %ecx // ecx = stack low address 54 | neg %eax // eax = frame low address 55 | add %esp, %eax // " 56 | jb 1f // frame low address overflow? 57 | xor %eax, %eax // overflowed: frame low address = null 58 | 0: sub $0x1000, %ecx // extend stack into guard page 59 | test %eax, (%ecx) // commit page (two instruction bytes) 60 | 1: cmp %eax, %ecx 61 | ja 0b 62 | pop %ecx 63 | pop %eax 64 | ret 65 | # elif CHKSTK 66 | // On x86, __chkstk allocates the new stack frame. This implementation 67 | // clobbers eax. MSVC only seems to care about ebp and ecx (this). 68 | .globl __chkstk 69 | __chkstk: 70 | push %ecx // preserve ecx 71 | mov %fs:(0x08), %ecx // ecx = stack low address 72 | neg %eax // eax = frame low address 73 | lea 8(%esp,%eax), %eax // " 74 | cmp %esp, %eax // frame low address overflow? 75 | jb 1f // " 76 | xor %eax, %eax // overflowed: frame low address = null 77 | 0: sub $0x1000, %ecx // extend stack into guard page 78 | test %eax, (%ecx) // commit page (two instruction bytes) 79 | 1: cmp %eax, %ecx 80 | ja 0b 81 | pop %ecx // restore ecx 82 | xchg %eax, %esp // allocate frame 83 | jmp *(%eax) // return 84 | # endif 85 | #endif // __i386 86 | -------------------------------------------------------------------------------- /src/libmemory.c: -------------------------------------------------------------------------------- 1 | #if 0 2 | # memset, memcpy, memmove, and memcmp via x86 string instructions 3 | # Execute this source with a shell to build libmemory.a. 4 | # This is free and unencumbered software released into the public domain. 5 | set -e 6 | CFLAGS="-Os -fno-builtin -fno-asynchronous-unwind-tables -fno-ident" 7 | objects="" 8 | for func in memset memcpy memmove memcmp strlen; do 9 | FUNC="$(echo $func | tr '[:lower:]' '[:upper:]')" 10 | objects="$objects $func.o" 11 | (set -x; ${CC:-cc} -c -D$FUNC -Wa,--no-pad-sections $CFLAGS -o $func.o $0) 12 | done 13 | rm -f "${DESTDIR}libmemory.a" 14 | ${AR:-ar} r "${DESTDIR}libmemory.a" $objects 15 | rm $objects 16 | exit 0 17 | #endif 18 | 19 | typedef __SIZE_TYPE__ size_t; 20 | typedef __UINTPTR_TYPE__ uintptr_t; 21 | 22 | #ifdef MEMSET 23 | void *memset(void *dst, int c, size_t len) 24 | { 25 | void *r = dst; 26 | asm volatile ( 27 | "rep stosb" 28 | : "+D"(dst), "+c"(len) 29 | : "a"(c) 30 | : "memory" 31 | ); 32 | return r; 33 | } 34 | #endif 35 | 36 | #ifdef MEMCPY 37 | void *memcpy(void *restrict dst, void *restrict src, size_t len) 38 | { 39 | void *r = dst; 40 | asm volatile ( 41 | "rep movsb" 42 | : "+D"(dst), "+S"(src), "+c"(len) 43 | : 44 | : "memory" 45 | ); 46 | return r; 47 | } 48 | #endif 49 | 50 | #ifdef MEMMOVE 51 | void *memmove(void *dst, void *src, size_t len) 52 | { 53 | // Use uintptr_t to bypass pointer semantics: 54 | // (1) comparing unrelated pointers 55 | // (2) pointer arithmetic on null (i.e. gracefully handle null dst/src) 56 | // (3) pointer overflow ("one-before-the-beginning" in reversed copy) 57 | uintptr_t d = (uintptr_t)dst; 58 | uintptr_t s = (uintptr_t)src; 59 | if (d > s) { 60 | d += len - 1; 61 | s += len - 1; 62 | asm ("std"); 63 | } 64 | asm volatile ( 65 | "rep movsb; cld" 66 | : "+D"(d), "+S"(s), "+c"(len) 67 | : 68 | : "memory" 69 | ); 70 | return dst; 71 | } 72 | #endif 73 | 74 | #ifdef MEMCMP 75 | int memcmp(void *s1, void *s2, size_t len) 76 | { 77 | // CCa "after" == CF=0 && ZF=0 78 | // CCb "before" == CF=1 79 | int a, b; 80 | asm volatile ( 81 | "xor %%eax, %%eax\n" // CF=0, ZF=1 (i.e. CCa = CCb = 0) 82 | "repz cmpsb\n" 83 | : "+D"(s1), "+S"(s2), "+c"(len), "=@cca"(a), "=@ccb"(b) 84 | : 85 | : "ax", "memory" 86 | ); 87 | return b - a; 88 | } 89 | #endif 90 | 91 | #ifdef STRLEN 92 | size_t strlen(char *s) 93 | { 94 | size_t n = -1; 95 | asm volatile ( 96 | "repne scasb" 97 | : "+D"(s), "+c"(n) 98 | : "a"(0) 99 | : "memory" 100 | ); 101 | return -n - 2; 102 | } 103 | #endif 104 | 105 | #ifdef TEST 106 | // $ sh libmemory.c 107 | // $ cc -nostdlib -fno-builtin -DTEST -g3 -O -o test libmemory.c libmemory.a 108 | // $ gdb -ex r -ex q ./test 109 | 110 | #define assert(c) while (!(c)) __builtin_trap() 111 | void *memset(void *, int, size_t); 112 | int memcmp(void *, void *, size_t); 113 | void *memcpy(void *restrict, void *restrict, size_t); 114 | void *memmove(void *, void *, size_t); 115 | size_t strlen(char *); 116 | 117 | #if defined(__linux) && defined(__amd64) 118 | asm (" .global _start\n" 119 | "_start: call mainCRTStartup\n" 120 | " mov %eax, %edi\n" 121 | " mov $60, %eax\n" 122 | " syscall\n"); 123 | #elif defined(__linux) && defined(__i386) 124 | asm (" .global _start\n" 125 | "_start: call mainCRTStartup\n" 126 | " mov %eax, %ebx\n" 127 | " mov $1, %eax\n" 128 | " int $0x80\n"); 129 | #endif 130 | 131 | int mainCRTStartup(void) 132 | { 133 | { 134 | char buf[12] = "............"; 135 | memset(buf+4, 'x', 4); 136 | assert(!memcmp(buf, "....xxxx....", 12)); 137 | memset(buf, 0, 12); 138 | assert(!memcmp(buf, (char[12]){0}, 12)); 139 | memset(buf+8, 1, 0); 140 | assert(!memcmp(buf, (char[12]){0}, 12)); 141 | } 142 | 143 | { 144 | char buf[7] = "abcdefg"; 145 | memcpy(buf+0, buf+3, 3); 146 | assert(!memcmp(buf, "defdefg", 7)); 147 | memcpy(buf+5, buf+1, 2); 148 | assert(!memcmp(buf, "defdeef", 7)); 149 | memcpy(buf+1, buf+4, 0); 150 | assert(!memcmp(buf, "defdeef", 7)); 151 | } 152 | 153 | { 154 | char buf[] = "abcdefgh"; 155 | memmove(buf+0, buf+1, 7); 156 | assert(!memcmp(buf, "bcdefghh", 8)); 157 | buf[7] = 0; 158 | memmove(buf+1, buf+0, 7); 159 | assert(!memcmp(buf, "bbcdefgh", 8)); 160 | memmove(buf+2, buf+1, 0); 161 | assert(!memcmp(buf, "bbcdefgh", 8)); 162 | } 163 | 164 | assert(memcmp("\xff", "1", 1) > 0); 165 | assert(memcmp("", "", 0) == 0); // test empty after > result 166 | assert(memcmp("1", "\xff", 1) < 0); 167 | assert(memcmp("", "", 0) == 0); // test empty after < result 168 | assert(memcmp("ab", "aa", 2) > 0); 169 | assert(memcmp("aa", "ab", 2) < 0); 170 | assert(memcmp("x", "y", 0) == 0); 171 | 172 | assert(0 == strlen("")); 173 | assert(1 == strlen(" ")); 174 | assert(1 == strlen("\xff")); 175 | assert(5 == strlen("hello")); 176 | 177 | return 0; 178 | } 179 | #endif 180 | -------------------------------------------------------------------------------- /src/peports.c: -------------------------------------------------------------------------------- 1 | // PE export/import table listing 2 | // 3 | // $ peports c:/windows/system32/kernel32.dll 4 | // $ peports -i main.exe >imports.txt 5 | // $ peports -e library.dll >exports.txt 6 | // 7 | // Compilation requires GCC or Clang. Behaves like "dumpbin /exports" 8 | // and "dumpbin /imports" from MSVC, but open source, standalone, and 9 | // much faster. For C++ symbols, consider piping output through c++filt 10 | // or vc++filt. 11 | // 12 | // Dynamic linking only permits ASCII for module and symbol names, and 13 | // both the MSVC and GNU toolchains sometimes choke on non-ASCII names. 14 | // Therefore wide console output is unnecessary. All standard output is 15 | // ASCII, and non-ASCII name bytes are escape-printed. Angle brackets 16 | // in names are escaped, so brackets appearing in output are delimiters. 17 | // Command line argument paths may be wide, though, since these are not 18 | // so restricted. 19 | // 20 | // THIS IS NOT A SECURITY TOOL! While this program does not misbehave 21 | // given arbitrary, untrusted input, its interpretation of PE data may 22 | // not precisely match the Windows loader, which is itself inconsistent 23 | // between releases. For specially-crafted inputs, outputs may differ 24 | // from Windows' parsing of the same input. This is first and foremost a 25 | // debugging tool. 26 | // 27 | // Porting note: The platform layer implements osload and oswrite. To 28 | // run the application, it calls peports with command line arguments and 29 | // a scratch arena. The application calls osload and oswrite as needed 30 | // for reading file and writing output. 31 | // 32 | // Roadmap: 33 | // * Alternate format options? (e.g. DEF, a better gendef) 34 | // * A recursive option, to behave like a dependency walker? 35 | // * An option to automatically demangle C++ symbols? 36 | // 37 | // This is free and unencumbered software released into the public domain. 38 | 39 | #define assert(c) while (!(c)) __builtin_unreachable() 40 | #define countof(a) (iz)(sizeof(a) / sizeof(*(a))) 41 | #define new(a, n, t) (t *)alloc(a, n, sizeof(t), _Alignof(t)) 42 | #define s8(s) (s8){(u8 *)s, countof(s)-1} 43 | #define catch(e) __builtin_setjmp((e)->jmp) 44 | 45 | typedef unsigned char u8; 46 | typedef unsigned short u16; 47 | typedef signed int b32; 48 | typedef signed int i32; 49 | typedef unsigned int u32; 50 | typedef unsigned short char16_t; 51 | typedef char16_t c16; 52 | typedef __PTRDIFF_TYPE__ iz; 53 | typedef __SIZE_TYPE__ uz; 54 | typedef char byte; 55 | 56 | typedef struct { 57 | u8 *data; 58 | iz len; 59 | } s8; 60 | 61 | typedef struct { 62 | void *jmp[5]; 63 | s8 err; 64 | } escape; 65 | 66 | __attribute((noreturn)) 67 | static void throw(escape *e, s8 reason) 68 | { 69 | e->err = reason; 70 | __builtin_longjmp(e->jmp, 1); 71 | } 72 | 73 | typedef struct { 74 | byte *beg; 75 | byte *end; 76 | escape *esc; 77 | } arena; 78 | 79 | // Read an entire file into memory. Returns a null string on error. The 80 | // information we care about is probably at the very beginning of the 81 | // file, so this function does not fill the whole arena but truncates as 82 | // necessary to leave an 8th or so of the remaining space for parsing. 83 | // Truncation is not an error. The special path "-" is standard input. 84 | static s8 osload(arena *, s8); 85 | 86 | // Write some bytes to standard output (1) or standard error (2). 87 | static b32 oswrite(i32, u8 *, i32); 88 | 89 | static byte *alloc(arena *a, iz count, iz size, iz align) 90 | { 91 | assert(count >= 0); 92 | iz pad = -(uz)a->beg & (align - 1); 93 | if (count >= (a->end - a->beg - pad)/size) { 94 | throw(a->esc, s8("out of memory")); 95 | } 96 | byte *r = a->beg + pad; 97 | a->beg += pad + count*size; 98 | return __builtin_memset(r, 0, count*size); 99 | } 100 | 101 | static s8 span(u8 *beg, u8 *end) 102 | { 103 | assert(beg <= end); 104 | s8 r = {0}; 105 | r.data = beg; 106 | r.len = end - beg; 107 | return r; 108 | } 109 | 110 | static b32 equals(s8 a, s8 b) 111 | { 112 | if (a.len != b.len) { 113 | return 0; 114 | } 115 | for (iz i = 0; i < a.len; i++) { 116 | if (a.data[i] != b.data[i]) { 117 | return 0; 118 | } 119 | } 120 | return 1; 121 | } 122 | 123 | static s8 slice3(s8 s, iz beg, iz end, escape *e) 124 | { 125 | if (beg<0 || beg>end || end>s.len) { 126 | throw(e, s8("unexpected end of input (slice)")); 127 | } 128 | s.data += beg; 129 | s.len = end - beg; 130 | return s; 131 | } 132 | 133 | static s8 slice2(s8 s, iz beg, escape *e) 134 | { 135 | return slice3(s, beg, s.len, e); 136 | } 137 | 138 | static u16 readu16(s8 s, iz off, escape *e) 139 | { 140 | if (off > s.len-2) { 141 | throw(e, s8("unexpected end of input (uint16)")); 142 | } 143 | u8 *p = s.data + off; 144 | return (u16)((u16)p[1]<<8 | p[0]); 145 | } 146 | 147 | static u32 readu32(s8 s, iz off, escape *e) 148 | { 149 | if (off > s.len-4) { 150 | throw(e, s8("unexpected end of input (uint32)")); 151 | } 152 | u8 *p = s.data + off; 153 | return (u32)p[3]<<24 | (u32)p[2]<<16 | (u32)p[1]<<8 | p[0]; 154 | } 155 | 156 | static s8 nullterm(s8 s) 157 | { 158 | if (!s.data) return s; 159 | iz len = 0; 160 | for (; lenerr && b->len) { 185 | b->err |= !oswrite(b->fd, b->buf, b->len); 186 | b->len = 0; 187 | } 188 | } 189 | 190 | static void print(u8buf *b, s8 s) 191 | { 192 | for (iz off = 0; !b->err && offcap - b->len; 194 | i32 count = availbuf+b->len, s.data+off, count); 196 | off += count; 197 | b->len += count; 198 | if (b->len == b->cap) { 199 | flush(b); 200 | } 201 | } 202 | } 203 | 204 | static void printu32(u8buf *b, u32 x) 205 | { 206 | u8 buf[16]; 207 | u8 *end = buf + countof(buf); 208 | u8 *beg = end; 209 | do { 210 | *--beg = (u8)(x%10) + '0'; 211 | } while (x /= 10); 212 | print(b, span(beg, end)); 213 | } 214 | 215 | // Escape-print a module or symbol name. 216 | static void printname(u8buf *b, s8 s) 217 | { 218 | if (!s.len) { 219 | print(b, s8("<>")); // signify empty using brackets 220 | } else { 221 | for (iz i = 0; i < s.len; i++) { 222 | u8 c = s.data[i]; 223 | if (c<0x20 || c>0x7f || c=='<' || c=='>' || c=='\\') { 224 | u8 encode[4] = "\\x.."; 225 | encode[2] = "0123456789abcdef"[c>>4]; 226 | encode[3] = "0123456789abcdef"[c&15]; 227 | print(b, span(encode, encode+countof(encode))); 228 | } else { 229 | print(b, span(&c, &c+1)); 230 | } 231 | } 232 | } 233 | } 234 | 235 | typedef struct { 236 | u32 beg; 237 | u32 end; 238 | s8 mem; 239 | } region; 240 | 241 | typedef struct { 242 | region *regions; 243 | i32 len; 244 | } vm; 245 | 246 | // Parsing a PE means simulating a loader. A virtual memory (vm) object 247 | // represents sections mapped into a virtual address space, and this 248 | // function reads regions out of that address space. 249 | static s8 loadrva(vm m, u32 vaddr, escape *e) 250 | { 251 | s8 r = {0}; 252 | for (i32 i = 0; i < m.len; i++) { 253 | region s = m.regions[i]; 254 | if (vaddr>=s.beg && vaddr 0xffffffff-b) { 267 | throw(e, s8("overflow computing 32-bit offset")); 268 | } 269 | return a + b; 270 | } 271 | 272 | static void usage(u8buf *b) 273 | { 274 | print(b, s8( 275 | "usage: peports [-ehi] [files...]\n" 276 | " -e print the export table\n" 277 | " -h print this message\n" 278 | " -i print the import table\n" 279 | "Prints export and import tables of EXEs and DLLs.\n" 280 | "Given no arguments, reads data from standard input.\n" 281 | )); 282 | } 283 | 284 | typedef struct { 285 | u8buf *out; 286 | u8buf *err; 287 | i32 optind; 288 | b32 exports; 289 | b32 imports; 290 | } config; 291 | 292 | enum {OPT_OK, OPT_EXIT, OPT_ERR}; 293 | 294 | static i32 parseopts(config *c, i32 argc, s8 *argv) 295 | { 296 | for (c->optind = !!argc; c->optind < argc; c->optind++) { 297 | s8 arg = argv[c->optind]; 298 | if (!arg.len || arg.data[0]!='-') break; 299 | for (iz i = 1; i < arg.len; i++) { 300 | u8 x = arg.data[i]; 301 | switch (x) { 302 | case 'e': 303 | c->exports = 1; 304 | break; 305 | case 'h': 306 | usage(c->out); 307 | flush(c->out); 308 | return c->out->err ? OPT_ERR : OPT_EXIT; 309 | case 'i': 310 | c->imports = 1; 311 | break; 312 | default: 313 | print(c->err, s8("peports: unknown option: -")); 314 | print(c->err, span(&x, &x+1)); 315 | print(c->err, s8("\n")); 316 | usage(c->err); 317 | flush(c->err); 318 | return OPT_ERR; 319 | } 320 | } 321 | } 322 | 323 | if (!c->imports && !c->exports) { 324 | c->imports = c->exports = 1; 325 | } 326 | return OPT_OK; 327 | } 328 | 329 | static void processpe(s8 dll, config conf, arena scratch) 330 | { 331 | u8buf *out = conf.out; 332 | escape *esc = scratch.esc; 333 | 334 | u32 peoff = readu32(dll, 0x3c, esc); 335 | s8 pe = slice2(dll, peoff, esc); 336 | s8 pehdr = slice3(pe, 0, 4, esc); 337 | if (!equals(s8("PE\0\0"), pehdr)) { 338 | throw(esc, s8("not a PE file")); 339 | } 340 | 341 | u16 nsections = readu16(pe, 4+ 2, esc); 342 | u16 hdrsize = readu16(pe, 4+16, esc); 343 | 344 | enum { PE32, PE64 }; 345 | u16 magic = readu16(pe, 4+20, esc); 346 | i32 type = -1; 347 | switch (magic) { 348 | default: throw(esc, s8("unknown PE magic")); 349 | case 0x010b: type = PE32; break; 350 | case 0x020b: type = PE64; break; 351 | } 352 | 353 | vm map = {0}; 354 | i32 loadlen = nsections>96 ? 96 : nsections; 355 | map.regions = new(&scratch, loadlen, region); 356 | s8 sections = slice2(pe, 4+20+hdrsize, esc); 357 | for (i32 i = 0; i < loadlen; i++) { 358 | u32 vsize = readu32(sections, 40*i+ 8, esc); 359 | u32 vaddr = readu32(sections, 40*i+12, esc); 360 | u32 rsize = readu32(sections, 40*i+16, esc); 361 | u32 raddr = readu32(sections, 40*i+20, esc); 362 | 363 | i32 r = map.len++; 364 | map.regions[r].beg = vaddr; 365 | map.regions[r].end = checkadd(vaddr, vsize, esc); 366 | if (r) { 367 | region prev = map.regions[r-1]; 368 | if (prev.beg>=vaddr || prev.end>vaddr) { 369 | throw(esc, s8("invalid section order")); 370 | } 371 | } 372 | 373 | u32 rend = checkadd(raddr, rsize, esc); 374 | map.regions[r].mem = slice3(dll, raddr, rend, esc); 375 | if (vsize > rsize) { 376 | // Padded sections (e.g. .bss) unlikely interesting: discard 377 | map.len--; 378 | } else if (vsize < rsize) { 379 | // Truncated sections *are* usually interesting. Go figure. 380 | map.regions[r].mem.len = vsize; 381 | } 382 | } 383 | 384 | u32 edataoff = readu32(pe, 24+(type==PE32 ? 96 : 112), esc); 385 | u32 edatalen = readu32(pe, 24+(type==PE32 ? 100 : 116), esc); 386 | u32 edataend = checkadd(edataoff, edatalen, esc); 387 | s8 edata = loadrva(map, edataoff, esc); 388 | 389 | if (conf.exports && edatalen) { 390 | print(out, s8("EXPORTS\n")); 391 | 392 | u32 ordbase = readu32(edata, 4*4, esc); 393 | i32 naddrs = readu32(edata, 5*4, esc); 394 | i32 nnames = readu32(edata, 6*4, esc); 395 | if (naddrs<0 || nnames<0) { 396 | throw(esc, s8("invalid export count")); 397 | } 398 | 399 | // If naddrs is huge, this will fail here with OOM 400 | u8 *seen = new(&scratch, naddrs, u8); 401 | 402 | u32 addrsoff = readu32(edata, 7*4, esc); 403 | s8 addrs = loadrva(map, addrsoff, esc); 404 | u32 namesoff = readu32(edata, 8*4, esc); 405 | s8 names = loadrva(map, namesoff, esc); 406 | u32 ordsoff = readu32(edata, 9*4, esc); 407 | s8 ordinals = loadrva(map, ordsoff, esc); 408 | 409 | // If nnames is huge, the loop will EOF before overflow 410 | for (i32 i = 0; i < nnames; i++) { 411 | u16 ordinal = readu16(ordinals, i*2, esc); 412 | if (ordinal >= naddrs) { 413 | throw(esc, s8("invalid export ordinal")); 414 | } 415 | seen[ordinal] = 1; 416 | 417 | // If RVA points in .edata it's a forwarder name 418 | s8 module = {0}; 419 | u32 addr = readu32(addrs, ordinal*4, esc); 420 | if (addr>=edataoff && addr")); 436 | } 437 | print(out, s8("\n")); 438 | } 439 | 440 | for (i32 i = 0; i < naddrs; i++) { 441 | if (!seen[i]) { 442 | print(out, s8("\t")); 443 | printu32(out, checkadd(i, ordbase, esc)); 444 | print(out, s8("\t\n")); 445 | } 446 | } 447 | } 448 | 449 | u32 idataoff = readu32(pe, 24+(type==PE32 ? 104 : 120), esc); 450 | u32 idatalen = readu32(pe, 24+(type==PE32 ? 108 : 124), esc); 451 | s8 idata = loadrva(map, idataoff, esc); 452 | 453 | if (conf.imports && idatalen) { 454 | // The PE specification says the last import directory table 455 | // entry is all zeros, indicating the directory end. However, 456 | // MSVC link.exe is buggy and does not reliably produce this 457 | // null entry. Instead the directory runs into import lookup 458 | // tables and string table, causing the directory to read as 459 | // garbage. We have two workarounds: 460 | // 461 | // 1. Track the earliest import lookup table RVA, and stop 462 | // reading if the directory would overlap it. 463 | // 2. Don't treat garbage RVA fields as errors, just stop 464 | // reading the table (Binutils strategy). 465 | // 466 | // This issue was crashing objdump back in 2005. See Binutils 467 | // commit a50b216054a4. 468 | u32 firsttable = -1; 469 | 470 | for (i32 i = 0;; i++) { 471 | if (idataoff + i*20 == firsttable) { 472 | // Probably the link.exe bug. We're now overlapping an 473 | // import lookup table, so stop reading the import 474 | // directory. The left-side sum might overflow, but 475 | // that's fine. This is just a heuristic. 476 | break; 477 | } 478 | 479 | u32 tableoff = readu32(idata, i*20+ 0, esc); 480 | u32 nameoff = readu32(idata, i*20+12, esc); 481 | if (!tableoff || !nameoff) break; 482 | 483 | s8 name = nullterm(loadrva(map, nameoff, esc)); 484 | if (!name.data) break; // ignore link.exe bug 485 | 486 | printname(out, name); 487 | print(out, s8("\n")); 488 | 489 | s8 table = loadrva(map, tableoff, esc); 490 | if (!table.data) break; // ignore link.exe bug 491 | firsttable = firsttable>31) { 508 | printu32(out, addr&0x7fffffff); 509 | print(out, s8("\t")); 510 | } else { 511 | s8 entry = loadrva(map, addr, esc); 512 | u16 hint = readu16(entry, 0, esc); 513 | printu32(out, hint); 514 | print(out, s8("\t")); 515 | s8 name = nullterm(slice2(entry, 2, esc)); 516 | printname(out, name); 517 | } 518 | print(out, s8("\n")); 519 | } 520 | } 521 | } 522 | } 523 | 524 | static void processpath(s8 path, config conf, arena scratch) 525 | { 526 | s8 dll = osload(&scratch, path); 527 | if (!dll.data) { 528 | throw(scratch.esc, s8("could not load file")); 529 | } 530 | processpe(dll, conf, scratch); 531 | } 532 | 533 | static b32 peports(i32 argc, s8 *argv, arena scratch) 534 | { 535 | b32 ok = 1; 536 | u8buf out[1] = {newu8buf(&scratch, 1)}; 537 | u8buf err[1] = {newu8buf(&scratch, 2)}; 538 | 539 | config conf = {0}; 540 | conf.out = out; 541 | conf.err = err; 542 | switch (parseopts(&conf, argc, argv)) { 543 | case OPT_OK: break; 544 | case OPT_EXIT: return 1; 545 | case OPT_ERR: return 0; 546 | } 547 | 548 | if (conf.optind == argc) { 549 | static s8 fakeargv[] = {s8("peports"), s8("-")}; 550 | argc = countof(fakeargv); 551 | argv = fakeargv; 552 | conf.optind = 1; 553 | } 554 | 555 | escape esc = {0}; 556 | scratch.esc = &esc; 557 | for (i32 i = conf.optind; i < argc; i++) { 558 | if (catch(&esc)) { 559 | flush(out); 560 | print(err, s8("peports: ")); 561 | print(err, esc.err); 562 | print(err, s8(": ")); 563 | print(err, argv[i]); // NOTE: UTF-8 564 | print(err, s8("\n")); 565 | flush(err); 566 | ok &= 1; 567 | continue; 568 | } 569 | processpath(argv[i], conf, scratch); 570 | } 571 | 572 | flush(out); 573 | ok &= !out->err; 574 | return ok; 575 | } 576 | 577 | 578 | #if _WIN32 579 | // $ gcc -nostartfiles -o peports.exe peports.c 580 | // $ clang-cl peports.c /link /subsystem:console 581 | // kernel32.lib shell32.lib libvcruntime.lib 582 | 583 | #define W32(r) __declspec(dllimport) r __stdcall 584 | W32(b32) CloseHandle(uz); 585 | W32(c16 **) CommandLineToArgvW(c16 *, i32 *); 586 | W32(b32) CreateFileW(c16 *, i32, i32, uz, i32, i32, uz); 587 | W32(void) ExitProcess(i32) __attribute((noreturn)); 588 | W32(c16 *) GetCommandLineW(void); 589 | W32(uz) GetStdHandle(i32); 590 | W32(i32) MultiByteToWideChar(i32, i32, u8 *, i32, c16 *, i32); 591 | W32(b32) ReadFile(uz, u8 *, i32, i32 *, uz); 592 | W32(i32) WideCharToMultiByte(i32, i32, c16 *, i32, u8 *, i32, uz, uz); 593 | W32(b32) WriteFile(uz, u8 *, i32, i32 *, uz); 594 | 595 | static i32 truncsize(iz len, i32 max) 596 | { 597 | return maxbeg; 640 | iz avail = a->end - a->beg; 641 | avail -= avail / 8; // don't fill the arena to the brim 642 | for (;;) { 643 | i32 max = 1<<21; 644 | i32 len; 645 | ReadFile(handle, r.data+r.len, truncsize(avail-r.len, max), &len, 0); 646 | if (len < 1) break; 647 | r.len += len; 648 | } 649 | a->beg += r.len; 650 | 651 | if (close) CloseHandle(handle); 652 | return r; 653 | } 654 | 655 | static b32 oswrite(i32 fd, u8 *buf, i32 len) 656 | { 657 | uz h = GetStdHandle(-10 - fd); 658 | return WriteFile(h, buf, len, &len, 0); 659 | } 660 | 661 | __attribute((force_align_arg_pointer)) 662 | void mainCRTStartup(void) 663 | { 664 | static byte mem[sizeof(uz)<<26]; // 256/512 MiB 665 | arena scratch = {0}; 666 | scratch.beg = mem; 667 | asm ("" : "+r"(scratch.beg)); // launder the pointer 668 | scratch.end = scratch.beg + countof(mem); 669 | 670 | c16 *cmd = GetCommandLineW(); 671 | i32 argc = 0; 672 | c16 **argvw = CommandLineToArgvW(cmd, &argc); 673 | s8 *argv = new(&scratch, argc, s8); 674 | for (i32 i = 0; i < argc; i++) { 675 | i32 len = WideCharToMultiByte(65001, 0, argvw[i], -1, 0, 0, 0, 0); 676 | argv[i].data = new(&scratch, len, u8); 677 | argv[i].len = len ? len-1 : len; 678 | WideCharToMultiByte(65001, 0, argvw[i], -1, argv[i].data, len, 0, 0); 679 | } 680 | 681 | b32 ok = peports(argc, argv, scratch); 682 | ExitProcess(!ok); 683 | } 684 | 685 | 686 | #elif __AFL_COMPILER 687 | // $ afl-gcc-fast -g3 -fsanitize=undefined peports.c 688 | // $ mkdir i 689 | // $ cp corpus.dll i/ 690 | // $ afl-fuzz -ii -oo ./a.out 691 | #include 692 | #include 693 | 694 | __AFL_FUZZ_INIT(); 695 | 696 | static b32 oswrite(i32, u8 *, i32) { return 1; } 697 | static s8 osload(arena *, s8) { __builtin_trap(); } 698 | 699 | int main(void) 700 | { 701 | __AFL_INIT(); 702 | 703 | iz cap = 1<<20; 704 | arena a = {0}; 705 | a.beg = malloc(cap); 706 | a.end = a.beg + cap; 707 | 708 | config c = {0}; 709 | c.exports = 1; 710 | c.imports = 1; 711 | c.out = new(&a, 1, u8buf); 712 | *c.out = newu8buf(&a, 1); 713 | c.err = new(&a, 1, u8buf); 714 | *c.err = newu8buf(&a, 2); 715 | 716 | s8 dll = {0}; 717 | dll.data = __AFL_FUZZ_TESTCASE_BUF; 718 | while (__AFL_LOOP(10000)) { 719 | dll.len = __AFL_FUZZ_TESTCASE_LEN; 720 | a.esc = &(escape){0}; 721 | if (!catch(a.esc)) { 722 | processpe(dll, c, a); 723 | } 724 | } 725 | } 726 | 727 | 728 | #else // POSIX-ish? 729 | // $ cc -o peports peports.c 730 | #include 731 | #include 732 | #include 733 | 734 | static s8 osload(arena *a, s8 path) 735 | { 736 | s8 r = {0}; 737 | int fd = 0; 738 | b32 closeit = 1; 739 | 740 | if (equals(path, s8("-"))) { 741 | closeit = 0; 742 | } else { 743 | // NOTE: Assume the path is null-terminated because it came 744 | // straight from argv. This program does not construct paths. 745 | char *cpath = (char *)path.data; 746 | fd = open(cpath, O_RDONLY); 747 | if (fd == -1) return r; 748 | } 749 | 750 | r.data = (u8 *)a->beg; 751 | iz avail = a->end - a->beg; 752 | avail -= avail / 8; // don't fill the arena to the brim 753 | while (r.len < avail) { 754 | iz len = read(fd, r.data+r.len, avail-r.len); 755 | if (len < 1) break; 756 | r.len += len; 757 | } 758 | a->beg += r.len; 759 | 760 | if (closeit) close(fd); 761 | return r; 762 | } 763 | 764 | static b32 oswrite(i32 fd, u8 *buf, i32 len) 765 | { 766 | for (i32 off = 0; off < len;) { 767 | i32 r = (i32)write(fd, buf+off, len-off); 768 | if (r < 1) return 0; 769 | off += r; 770 | } 771 | return 1; 772 | } 773 | 774 | int main(int argc, char **argv) 775 | { 776 | static byte mem[sizeof(uz)<<26]; // 256/512 MiB 777 | arena scratch = {0}; 778 | scratch.beg = mem; 779 | asm ("" : "+r"(scratch.beg)); // launder the pointer 780 | scratch.end = scratch.beg + countof(mem); 781 | 782 | s8 *args = new(&scratch, argc, s8); 783 | for (int i = 0; i < argc; i++) { 784 | args[i].data = (u8 *)argv[i]; 785 | args[i].len = strlen(argv[i]); 786 | } 787 | b32 ok = peports(argc, args, scratch); 788 | return !ok; 789 | } 790 | #endif 791 | -------------------------------------------------------------------------------- /src/profile: -------------------------------------------------------------------------------- 1 | # GNU Autotools "configure" detects w64devkit as an MSYS2 environment, so 2 | # set some environment variables to disabuse it. Goes hand-in-hand with 3 | # the busybox-w32 ash patch for libtool. 4 | export PATH_SEPARATOR=';' 5 | export ac_executable_extensions=.exe 6 | export build_alias=ARCH 7 | -------------------------------------------------------------------------------- /src/variant-x86.patch: -------------------------------------------------------------------------------- 1 | --- a/Dockerfile 2 | +++ b/Dockerfile 3 | @@ -62,3 +62,3 @@ 4 | 5 | -ARG ARCH=x86_64-w64-mingw32 6 | +ARG ARCH=i686-w64-mingw32 7 | 8 | @@ -90,2 +90,3 @@ 9 | --with-default-msvcrt=msvcrt-os \ 10 | + --with-default-win32-winnt=0x0501 \ 11 | && make -j$(nproc) \ 12 | @@ -102,2 +103,3 @@ 13 | --with-sysroot=/bootstrap \ 14 | + --with-arch=pentium4 \ 15 | --target=$ARCH \ 16 | @@ -138,4 +140,4 @@ 17 | --disable-dependency-tracking \ 18 | - --disable-lib32 \ 19 | - --enable-lib64 \ 20 | + --enable-lib32 \ 21 | + --disable-lib64 \ 22 | CFLAGS="-Os" \ 23 | @@ -223,2 +225,3 @@ 24 | --with-default-msvcrt=msvcrt-os \ 25 | + --with-default-win32-winnt=0x0501 \ 26 | && make -j$(nproc) \ 27 | @@ -233,4 +236,4 @@ 28 | --disable-dependency-tracking \ 29 | - --disable-lib32 \ 30 | - --enable-lib64 \ 31 | + --enable-lib32 \ 32 | + --disable-lib64 \ 33 | CFLAGS="-Os" \ 34 | @@ -257,2 +260,3 @@ 35 | --with-native-system-header-dir=/include \ 36 | + --with-arch=pentium4 \ 37 | --target=$ARCH \ 38 | @@ -277,2 +281,3 @@ 39 | --disable-win32-registry \ 40 | + --disable-win32-utf8-manifest \ 41 | --enable-mingw-wildcard \ 42 | @@ -384,3 +389,3 @@ 43 | RUN cat $PREFIX/src/busybox-*.patch | patch -p1 \ 44 | - && make mingw64u_defconfig \ 45 | + && make mingw64_defconfig \ 46 | && sed -ri 's/^(CONFIG_AR)=y/\1=n/' .config \ 47 | -------------------------------------------------------------------------------- /src/vc++filt.c: -------------------------------------------------------------------------------- 1 | // vc++filt: c++filt for Microsoft Visual C++, like undname 2 | // $ cc -nostartfiles -O -o vc++filt.exe vc++filt.c -ldbghelp 3 | // $ cl /O1 vc++filt.c /link /subsystem:console 4 | // kernel32.lib shell32.lib dbghelp.lib libvcruntime.lib 5 | // * Full Unicode support, including UTF-8 and wide console output 6 | // * Tiny, slim, fast binary, low resource use, CRT-free 7 | // This is free and unencumbered software released into the public domain. 8 | #include 9 | 10 | enum { 11 | BUFFER_CAP = 1<<14, 12 | CONVERT_CAP = 1<<10, 13 | SYMBOL_MAX = 1<<16, 14 | MEMORY_CAP = 1<<24, 15 | }; 16 | 17 | #define countof(a) (size)(sizeof(a) / sizeof(*(a))) 18 | #define new(h, t, n) (t *)alloc(h, sizeof(t)*n) 19 | 20 | typedef unsigned char u8; 21 | typedef unsigned short u16; 22 | typedef signed int b32; 23 | typedef signed int i32; 24 | typedef unsigned int u32; 25 | typedef unsigned long long u64; 26 | typedef char byte; 27 | typedef size_t uptr; 28 | typedef ptrdiff_t size; 29 | 30 | #define W32(r) __declspec(dllimport) r __stdcall 31 | W32(u16 **) CommandLineToArgvW(u16 *, i32 *); 32 | W32(void) ExitProcess(i32); 33 | W32(u16 *) GetCommandLineW(void); 34 | W32(b32) GetConsoleMode(uptr, i32 *); 35 | W32(i32) GetStdHandle(i32); 36 | W32(b32) ReadFile(uptr, u8 *, i32, i32 *, uptr); 37 | W32(i32) UnDecorateSymbolName(u8 *, u8 *, i32, i32); // dbghelp.dll 38 | W32(byte *) VirtualAlloc(byte *, size, i32, i32); 39 | W32(i32) WideCharToMultiByte(i32, i32, u16 *, i32, u8 *, i32, uptr, uptr); 40 | W32(b32) WriteConsoleW(uptr, u16 *, i32, i32 *, uptr); 41 | W32(b32) WriteFile(uptr, u8 *, i32, i32 *, uptr); 42 | 43 | typedef struct { 44 | byte *beg; 45 | byte *end; 46 | } arena; 47 | 48 | // Allocate pointer-aligned, zero-initialized memory, or null on failure. 49 | static void *alloc(arena *a, size len) 50 | { 51 | size available = a->end - a->beg; 52 | size alignment = -len & (sizeof(void *) - 1); 53 | if (len > available-alignment) { 54 | return 0; // out of memory 55 | } 56 | return a->end -= len + alignment; // NOTE: assume arena is zeroed 57 | } 58 | 59 | typedef struct { 60 | u8 *data; 61 | size len; 62 | } s8; 63 | 64 | static u64 s8hash(s8 s) 65 | { 66 | // NOTE: hash is fast enough; faster has no throughput impact 67 | u64 h = 0x100; 68 | for (size i = 0; i < s.len; i++) { 69 | h ^= s.data[i]; 70 | h *= 1111111111111111111u; 71 | } 72 | return h; 73 | } 74 | 75 | static b32 s8equals(s8 a, s8 b) 76 | { 77 | if (a.len != b.len) { 78 | return 0; 79 | } 80 | for (size i = 0; i < a.len; i++) { 81 | if (a.data[i] != b.data[i]) { 82 | return 0; 83 | } 84 | } 85 | return 1; 86 | } 87 | 88 | static s8 s8push(s8 s, u8 c) 89 | { 90 | s.data[s.len++] = c; 91 | return s; 92 | } 93 | 94 | static s8 s8dup(s8 s, arena *perm) 95 | { 96 | s8 r = {0}; 97 | r.data = new(perm, u8, s.len); 98 | if (!r.data) { 99 | return r; // out of memory 100 | } 101 | for (size i = 0; i < s.len; i++) { 102 | r.data[i] = s.data[i]; 103 | } 104 | r.len = s.len; 105 | return r; 106 | } 107 | 108 | static s8 s8cuthead(s8 s, size len) 109 | { 110 | s.data += len; 111 | s.len -= len; 112 | return s; 113 | } 114 | 115 | typedef struct { 116 | s8 remain; 117 | i32 rune; 118 | } utf8result; 119 | 120 | static utf8result utf8decode(s8 s) 121 | { 122 | utf8result r = {0}; 123 | switch (s.data[0]&0xf0) { 124 | default : r.rune = s.data[0]; 125 | if (r.rune > 0x7f) break; 126 | r.remain = s8cuthead(s, 1); 127 | return r; 128 | case 0xc0: 129 | case 0xd0: if (s.len < 2) break; 130 | if ((s.data[1]&0xc0) != 0x80) break; 131 | r.rune = (i32)(s.data[0]&0x1f) << 6 | 132 | (i32)(s.data[1]&0x3f) << 0; 133 | if (r.rune < 0x80) break; 134 | r.remain = s8cuthead(s, 2); 135 | return r; 136 | case 0xe0: if (s.len < 3) break; 137 | if ((s.data[1]&0xc0) != 0x80) break; 138 | if ((s.data[2]&0xc0) != 0x80) break; 139 | r.rune = (i32)(s.data[0]&0x0f) << 12 | 140 | (i32)(s.data[1]&0x3f) << 6 | 141 | (i32)(s.data[2]&0x3f) << 0; 142 | if (r.rune < 0x800) break; 143 | if (r.rune>=0xd800 && r.rune<=0xdfff) break; 144 | r.remain = s8cuthead(s, 3); 145 | return r; 146 | case 0xf0: if (s.len < 4) break; 147 | if ((s.data[1]&0xc0) != 0x80) break; 148 | if ((s.data[2]&0xc0) != 0x80) break; 149 | if ((s.data[3]&0xc0) != 0x80) break; 150 | r.rune = (i32)(s.data[0]&0x0f) << 18 | 151 | (i32)(s.data[1]&0x3f) << 12 | 152 | (i32)(s.data[2]&0x3f) << 6 | 153 | (i32)(s.data[3]&0x3f) << 0; 154 | if (r.rune < 0x10000) break; 155 | if (r.rune > 0x10ffff) break; 156 | r.remain = s8cuthead(s, 4); 157 | return r; 158 | } 159 | r.rune = 0xfffd; // Replacement Character 160 | r.remain = s8cuthead(s, 1); 161 | return r; 162 | } 163 | 164 | // Encode code point returning the output length (1-2). 165 | static i32 utf16encode(u16 *dst, i32 rune) 166 | { 167 | if (rune >= 0x10000) { 168 | rune -= 0x10000; 169 | dst[0] = (u16)((rune >> 10) + 0xd800); 170 | dst[1] = (u16)((rune&0x3ff) + 0xdc00); 171 | return 2; 172 | } 173 | dst[0] = (u16)rune; 174 | return 1; 175 | } 176 | 177 | typedef struct { 178 | u8 buf[BUFFER_CAP]; 179 | i32 len; 180 | i32 off; 181 | i32 fd; 182 | b32 eof; 183 | } bufin; 184 | 185 | static bufin *newbufin(arena *perm) 186 | { 187 | bufin *b = new(perm, bufin, 1); 188 | b->fd = GetStdHandle(-10); 189 | return b; 190 | } 191 | 192 | static u8 readu8(bufin *b) 193 | { 194 | if (b->eof) { 195 | return 0; 196 | } else if (b->off == b->len) { 197 | ReadFile(b->fd, b->buf, BUFFER_CAP, &b->len, 0); 198 | if (!b->len) { 199 | b->eof = 1; 200 | return 0; 201 | } 202 | b->off = 0; 203 | } 204 | return b->buf[b->off++]; 205 | } 206 | 207 | typedef struct { 208 | u8 buf[BUFFER_CAP]; 209 | i32 len; 210 | i32 fd; 211 | b32 err; 212 | u16 *convert; // scratch space for WriteConsoleW 213 | } bufout; 214 | 215 | static bufout *newbufout(arena *perm) 216 | { 217 | bufout *b = new(perm, bufout, 1); 218 | b->fd = GetStdHandle(-11); 219 | i32 dummy; 220 | if (GetConsoleMode(b->fd, &dummy)) { 221 | b->convert = new(perm, u16, CONVERT_CAP); 222 | } 223 | return b; 224 | } 225 | 226 | static void flushconsole(bufout *b) 227 | { 228 | u16 *convert = b->convert; 229 | utf8result r = {0}; 230 | r.remain.data = b->buf; 231 | r.remain.len = b->len; 232 | for (i32 len = 0; !b->err && r.remain.len;) { 233 | r = utf8decode(r.remain); 234 | len += utf16encode(convert+len, r.rune); 235 | if (len>=CONVERT_CAP-1 || !r.remain.len) { 236 | i32 dummy; 237 | b->err = !WriteConsoleW(b->fd, convert, len, &dummy, 0); 238 | len = 0; 239 | } 240 | } 241 | b->len = 0; 242 | } 243 | 244 | static void flush(bufout *b) 245 | { 246 | if (!b->err && b->len) { 247 | if (b->convert) { 248 | flushconsole(b); 249 | } else { 250 | i32 dummy; 251 | b->err = !WriteFile(b->fd, b->buf, b->len, &dummy, 0); 252 | b->len = 0; 253 | } 254 | } 255 | } 256 | 257 | static void writes8(bufout *b, s8 s) 258 | { 259 | for (size off = 0; !b->err && offlen; 261 | i32 count = s.len-offbuf + b->len; 263 | for (i32 i = 0; i < count; i++) { 264 | dst[i] = s.data[off+i]; 265 | } 266 | off += count; 267 | b->len += count; 268 | if (b->len == BUFFER_CAP) { 269 | flush(b); 270 | } 271 | } 272 | } 273 | 274 | static void writeu8(bufout *b, u8 c) 275 | { 276 | s8 s = {0}; 277 | s.data = &c; 278 | s.len = 1; 279 | writes8(b, s); 280 | } 281 | 282 | static b32 ident(u8 c) 283 | { 284 | // matches $0-9?@A-Z_a-z and 0x80-0xff 285 | static const u32 t[] = { 286 | 0x00000000, 0x83ff0010, 0x87ffffff, 0x07fffffe, 287 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 288 | }; 289 | return t[c>>5] & (u32)1<<(c&31); 290 | } 291 | 292 | typedef struct map map; 293 | struct map { 294 | map *child[4]; 295 | s8 input; 296 | s8 output; 297 | }; 298 | 299 | static map *upsert(map **m, s8 input, arena *perm) 300 | { 301 | for (u64 h = s8hash(input); *m; h <<= 2) { 302 | if (s8equals(input, (*m)->input)) { 303 | return *m; 304 | } 305 | m = &(*m)->child[h>>62]; 306 | } 307 | input = s8dup(input, perm); 308 | *m = input.data ? new(perm, map, 1) : 0; 309 | if (*m) { 310 | (*m)->input = input; 311 | } 312 | return *m; 313 | } 314 | 315 | // Caches UnDecorateSymbolName calls. Typically ~90% of the symbols are 316 | // repeats. A hash map lookup is significantly faster than undecorating 317 | // through this API, so this measurably improves performance. The cache 318 | // operates correctly when out of memory, just with less caching. 319 | typedef struct { 320 | map *seen; 321 | arena *perm; 322 | u8 *temp; 323 | i32 flags; 324 | i32 hits; // for debugging / tuning 325 | i32 misses; // " 326 | } callcache; 327 | 328 | static callcache *newcache(i32 flags, arena *perm) 329 | { 330 | callcache *c = new(perm, callcache, 1); 331 | c->perm = perm; 332 | c->temp = new(perm, u8, SYMBOL_MAX); 333 | c->flags = flags; 334 | return c; 335 | } 336 | 337 | static s8 undecorate(callcache *c, s8 sym) 338 | { 339 | map *m = upsert(&c->seen, sym, c->perm); 340 | if (m && m->output.data) { 341 | c->hits++; 342 | return m->output; 343 | } 344 | 345 | c->misses++; 346 | s8 r = {0}; 347 | r.data = c->temp; 348 | r.len = UnDecorateSymbolName(sym.data, c->temp, SYMBOL_MAX, c->flags); 349 | if (m) { 350 | m->output = s8dup(r, c->perm); 351 | } 352 | return r; 353 | } 354 | 355 | static b32 usage(i32 fd) 356 | { 357 | static const u8 usage[] = 358 | "usage: vc++filt [-_ahnp] [symbols...] args.index) { 459 | // Do not process standard input 460 | flush(stdout); 461 | return stdout->err; 462 | } 463 | 464 | bufin *stdin = newbufin(&scratch); 465 | for (u8 c = readu8(stdin); !stdin->eof; c = readu8(stdin)) { 466 | if (sym_start && c=='?') { 467 | // Start of a new identifier? 468 | sym = s8push(sym, c); 469 | 470 | } else if (sym.len) { 471 | if (ident(c)) { 472 | // Continue the current identifier 473 | sym = s8push(sym, c); 474 | sym_at |= c=='@'; 475 | if (sym.len == SYMBOL_MAX) { 476 | // Too long, give up on this one 477 | writes8(stdout, sym); 478 | sym.len = sym_at = 0; 479 | } 480 | 481 | } else if (sym_at) { 482 | // Contains at least one @? Try to decode it. 483 | sym = s8push(sym, 0); 484 | s8 out = undecorate(cache, sym); 485 | writes8(stdout, out); 486 | writeu8(stdout, c); 487 | sym.len = sym_at = 0; 488 | 489 | } else { 490 | // Don't try to decode it, just pass it through 491 | writes8(stdout, sym); 492 | writeu8(stdout, c); 493 | sym.len = sym_at = 0; 494 | } 495 | 496 | } else { 497 | // Pass it through 498 | writeu8(stdout, c); 499 | } 500 | 501 | sym_start = !ident(c); 502 | } 503 | 504 | if (sym_at) { 505 | sym = s8push(sym, 0); 506 | s8 out = undecorate(cache, sym); 507 | writes8(stdout, out); 508 | } else { 509 | writes8(stdout, sym); 510 | } 511 | 512 | flush(stdout); 513 | return stdout->err; 514 | } 515 | 516 | void mainCRTStartup(void) 517 | { 518 | arena scratch = {0}; 519 | scratch.beg = VirtualAlloc(0, MEMORY_CAP, 0x3000, 4); 520 | scratch.end = scratch.beg + MEMORY_CAP; 521 | i32 r = run(scratch); 522 | ExitProcess(r); 523 | } 524 | -------------------------------------------------------------------------------- /src/w64devkit.c: -------------------------------------------------------------------------------- 1 | // Tiny, standalone launcher for w64devkit 2 | // * Sets $W64DEVKIT to the release version (-DVERSION) 3 | // * Sets $W64DEVKIT_HOME to the install location 4 | // * Maybe sets $HOME according to w64devkit.ini 5 | // * Starts a login shell with "sh -l" 6 | // 7 | // $ gcc -DVERSION="$VERSION" -nostartfiles -fno-builtin 8 | // -o w64devkit.exe w64devkit.c 9 | // 10 | // This is free and unencumbered software released into the public domain. 11 | 12 | #define countof(a) (size)(sizeof(a) / sizeof(*(a))) 13 | #define assert(c) while (!(c)) __builtin_unreachable() 14 | #define new(a, t, n) (t *)alloc(a, sizeof(t), _Alignof(t), n) 15 | 16 | typedef unsigned char byte; 17 | typedef __UINT8_TYPE__ u8; 18 | typedef unsigned short u16; 19 | typedef signed int i32; 20 | typedef signed int b32; 21 | typedef unsigned int u32; 22 | typedef __UINTPTR_TYPE__ uptr; 23 | typedef __PTRDIFF_TYPE__ size; 24 | typedef __SIZE_TYPE__ usize; 25 | typedef unsigned short char16_t; // for GDB 26 | typedef char16_t c16; 27 | 28 | typedef struct {} *handle; 29 | 30 | typedef struct { 31 | u32 cb; 32 | uptr a, b, c; 33 | i32 d, e, f, g, h, i, j, k; 34 | u16 l, m; 35 | uptr n, o, p, q; 36 | } si; 37 | 38 | typedef struct { 39 | handle process; 40 | handle thread; 41 | u32 pid; 42 | u32 tid; 43 | } pi; 44 | 45 | #define MAX_ENVVAR 32767 46 | #define CP_UTF8 65001 47 | #define PAGE_READWRITE 0x04 48 | #define MEM_COMMIT 0x1000 49 | #define MEM_RESERVE 0x2000 50 | #define MEM_RELEASE 0x8000 51 | #define GENERIC_READ 0x80000000 52 | #define OPEN_EXISTING 3 53 | #define FILE_SHARE_ALL 7 54 | 55 | #define W32 __attribute((dllimport,stdcall)) 56 | W32 b32 CloseHandle(handle); 57 | W32 handle CreateFileW(c16 *, u32, u32, void *, u32, u32, handle); 58 | W32 b32 CreateProcessW(c16*,c16*,void*,void*,i32,u32,c16*,c16*,si*,pi*); 59 | W32 void ExitProcess(u32) __attribute((noreturn)); 60 | W32 u32 ExpandEnvironmentStringsW(c16 *, c16 *, u32); 61 | W32 u32 GetEnvironmentVariableW(c16 *, c16 *, u32); 62 | W32 i32 GetExitCodeProcess(handle, u32 *); 63 | W32 u32 GetFullPathNameW(c16 *, u32, c16 *, c16 *); 64 | W32 u32 GetModuleFileNameW(handle, c16 *, u32); 65 | W32 i32 MessageBoxW(handle, c16 *, c16 *, u32); 66 | W32 i32 MultiByteToWideChar(u32, u32, u8 *, i32, c16 *, i32); 67 | W32 b32 ReadFile(handle, u8 *, i32, i32 *, void *); 68 | W32 b32 SetConsoleTitleW(c16 *); 69 | W32 b32 SetCurrentDirectoryW(c16 *); 70 | W32 b32 SetEnvironmentVariableW(c16 *, c16 *); 71 | W32 byte *VirtualAlloc(byte *, usize, u32, u32); 72 | W32 b32 VirtualFree(byte *, usize, u32); 73 | W32 u32 WaitForSingleObject(handle, u32); 74 | 75 | #define S(s) (s8){(u8 *)s, countof(s)-1} 76 | typedef struct { 77 | u8 *s; 78 | size len; 79 | } s8; 80 | 81 | #define U(s) (s16){s, countof(s)-1} 82 | typedef struct { 83 | c16 *s; 84 | size len; 85 | } s16; 86 | 87 | static s8 s8span(u8 *beg, u8 *end) 88 | { 89 | s8 s = {}; 90 | s.s = beg; 91 | s.len = end - beg; 92 | return s; 93 | } 94 | 95 | static b32 s8equals(s8 a, s8 b) 96 | { 97 | if (a.len != b.len) { 98 | return 0; 99 | } 100 | for (size i = 0; i < a.len; i++) { 101 | if (a.s[i] != b.s[i]) { 102 | return 0; 103 | } 104 | } 105 | return 1; 106 | } 107 | 108 | static void fatal(c16 *msg) 109 | { 110 | MessageBoxW(0, msg, u"w64devkit launcher", 0x10); 111 | ExitProcess(2); 112 | } 113 | 114 | typedef struct { 115 | byte *beg; 116 | byte *end; 117 | } arena; 118 | 119 | static void outofmemory(void) 120 | { 121 | fatal(u"Out of memory"); 122 | } 123 | 124 | __attribute((malloc, alloc_size(2, 4))) 125 | static byte *alloc(arena *a, size objsize, size align, size count) 126 | { 127 | size padding = (uptr)a->end & (align - 1); 128 | if (count > (a->end - a->beg - padding)/objsize) { 129 | outofmemory(); 130 | } 131 | size total = count*objsize; 132 | byte *p = a->end -= total + padding; 133 | for (size i = 0; i < total; i++) { 134 | p[i] = 0; 135 | } 136 | return p; 137 | } 138 | 139 | typedef struct { 140 | byte *base; 141 | arena perm; 142 | arena scratch; 143 | } memory; 144 | 145 | static memory newmemory(size cap) 146 | { 147 | memory r = {}; 148 | r.base = VirtualAlloc(0, cap, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); 149 | if (!r.base) return r; 150 | r.perm.beg = r.base; 151 | r.perm.end = r.base + cap/2; 152 | r.scratch.beg = r.base + cap/2; 153 | r.scratch.end = r.base + cap; 154 | return r; 155 | } 156 | 157 | static void freememory(memory m) 158 | { 159 | VirtualFree(m.base, 0, MEM_RELEASE); 160 | } 161 | 162 | static i32 truncsize(size len) 163 | { 164 | i32 max = 0x7fffffff; 165 | return len>max ? max : (i32)len; 166 | } 167 | 168 | typedef enum { 169 | INI_eof, 170 | INI_section, 171 | INI_key, 172 | INI_value 173 | } initype; 174 | 175 | typedef struct { 176 | s8 name; 177 | initype type; 178 | } initoken; 179 | 180 | typedef struct { 181 | u8 *beg; 182 | u8 *end; 183 | b32 invalue; 184 | } iniparser; 185 | 186 | static b32 inidone(iniparser *p) 187 | { 188 | return p->beg == p->end; 189 | } 190 | 191 | static u8 ininext(iniparser *p) 192 | { 193 | return *p->beg++; 194 | } 195 | 196 | static b32 iniblank(u8 c) 197 | { 198 | return c==' ' || c=='\t' || c=='\r'; 199 | } 200 | 201 | static void iniskip(iniparser *p) 202 | { 203 | for (; !inidone(p) && iniblank(*p->beg); p->beg++) {} 204 | } 205 | 206 | static u8 *initok(iniparser *p, u8 term) 207 | { 208 | u8 *end = p->beg; 209 | while (!inidone(p)) { 210 | u8 c = ininext(p); 211 | if (c == term) { 212 | return end; 213 | } else if (c == '\n') { 214 | break; 215 | } else if (!iniblank(c)) { 216 | end = p->beg; 217 | } 218 | } 219 | return term=='\n' ? end : 0; 220 | } 221 | 222 | static b32 iniquoted(s8 s) 223 | { 224 | return s.len>1 && s.s[0]=='"' && s.s[s.len-1]=='"'; 225 | } 226 | 227 | // Parses like GetPrivateProfileString except sections cannot contain 228 | // newlines. Invalid input lines are ignored. Comment lines begin with 229 | // ';' following any whitespace. No trailing comments. Trims leading and 230 | // trailing whitespace from sections, keys, and values. To preserve 231 | // whitespace, values may be double-quoted. No quote escapes. Content on 232 | // a line following a closing section ']' is ignored. Token encoding 233 | // matches input encoding. An INI_value always follows an INI_key key. 234 | static initoken iniparse(iniparser *p) 235 | { 236 | initoken r = {}; 237 | 238 | if (p->invalue) { 239 | p->invalue = 0; 240 | iniskip(p); 241 | u8 *beg = p->beg; 242 | u8 *end = initok(p, '\n'); 243 | r.name = s8span(beg, end); 244 | r.type = INI_value; 245 | if (iniquoted(r.name)) { 246 | r.name.s++; 247 | r.name.len -= 2; 248 | } 249 | return r; 250 | } 251 | 252 | for (;;) { 253 | iniskip(p); 254 | if (inidone(p)) { 255 | return r; 256 | } 257 | 258 | u8 *end; 259 | u8 *beg = p->beg; 260 | switch (ininext(p)) { 261 | case ';': 262 | while (!inidone(p) && ininext(p)!='\n') {} 263 | break; 264 | 265 | case '[': 266 | iniskip(p); 267 | beg = p->beg; 268 | end = initok(p, ']'); 269 | if (end) { 270 | // skip over anything else on the line 271 | while (!inidone(p) && ininext(p)!='\n') {} 272 | r.name = s8span(beg, end); 273 | r.type = INI_section; 274 | return r; 275 | } 276 | break; 277 | 278 | case '\n': 279 | break; 280 | 281 | default: 282 | end = initok(p, '='); 283 | if (end) { 284 | p->invalue = 1; 285 | r.name = s8span(beg, end); 286 | r.type = INI_key; 287 | return r; 288 | } 289 | } 290 | } 291 | } 292 | 293 | typedef enum { 294 | sym_null = 0, 295 | sym_w64devkit, 296 | sym_home, 297 | sym_title, 298 | } symbol; 299 | 300 | static symbol intern(s8 s) 301 | { 302 | static struct { 303 | s8 name; 304 | symbol symbol; 305 | } symbols[] = { 306 | {S("w64devkit"), sym_w64devkit}, 307 | {S("home"), sym_home}, 308 | {S("title"), sym_title}, 309 | }; 310 | for (size i = 0; i < countof(symbols); i++) { 311 | if (s8equals(symbols[i].name, s)) { 312 | return symbols[i].symbol; 313 | } 314 | } 315 | return sym_null; 316 | } 317 | 318 | static c16 *expandvalue(s8 value, arena *perm, arena scratch) 319 | { 320 | assert(value.len < 0x7fffffff); 321 | 322 | // First temporarily convert to a null-terminated wide string 323 | i32 len = MultiByteToWideChar(CP_UTF8, 0, value.s, (i32)value.len, 0, 0); 324 | if (len == 0x7fffffff) outofmemory(); 325 | s16 w = {}; 326 | w.len = len + 1; // append null terminator 327 | w.s = new(&scratch, c16, w.len); 328 | MultiByteToWideChar(CP_UTF8, 0, value.s, (i32)value.len, w.s, len); 329 | 330 | len = ExpandEnvironmentStringsW(w.s, 0, 0); 331 | if (len < 0) outofmemory(); 332 | c16 *r = new(perm, c16, len); 333 | ExpandEnvironmentStringsW(w.s, r, len); 334 | return r; 335 | } 336 | 337 | // Expand to a full path via GetFullPathNameW. 338 | static c16 *tofullpath(c16 *path, arena *perm) 339 | { 340 | i32 len = GetFullPathNameW(path, 0, 0, 0); 341 | if (len < 0) outofmemory(); 342 | c16 *r = new(perm, c16, len); 343 | GetFullPathNameW(path, len, r, 0); 344 | return r; 345 | } 346 | 347 | static s8 loadfile(c16 *path, arena *perm) 348 | { 349 | s8 r = {}; 350 | 351 | handle h = CreateFileW( 352 | path, 353 | GENERIC_READ, 354 | FILE_SHARE_ALL, 355 | 0, 356 | OPEN_EXISTING, 357 | 0, 358 | 0 359 | ); 360 | if (h == (handle)-1) { 361 | return r; 362 | } 363 | 364 | r.s = (u8 *)perm->beg; 365 | r.len = truncsize((perm->end - perm->beg)/sizeof(u8)); 366 | i32 len; 367 | b32 ok = ReadFile(h, r.s, (i32)r.len, &len, 0); 368 | CloseHandle(h); 369 | r.len = len; 370 | r.s = ok ? r.s : 0; 371 | perm->beg += r.len / sizeof(u8); 372 | return r; 373 | } 374 | 375 | typedef struct { 376 | c16 *home; 377 | c16 *title; 378 | b32 ok; 379 | } config; 380 | 381 | static config newconfig(void) 382 | { 383 | config r = {}; 384 | r.title = u"w64devkit"; 385 | return r; 386 | } 387 | 388 | // Read entries from w64devkit.ini. Expands environment variables, and 389 | // if "home" is relative, converts it to an absolute path. Before the 390 | // call, the working directory must be location of w64devkit.exe. 391 | static config loadconfig(arena *perm, arena scratch) 392 | { 393 | config conf = newconfig(); 394 | 395 | s8 ini = loadfile(u"w64devkit.ini", &scratch); 396 | if (!ini.s) return conf; 397 | 398 | iniparser *p = new(&scratch, iniparser, 1); 399 | p->beg = ini.s; 400 | p->end = ini.s + ini.len; 401 | 402 | for (symbol section = 0, key = 0;;) { 403 | initoken t = iniparse(p); 404 | switch (t.type) { 405 | case INI_eof: 406 | conf.ok = 1; 407 | return conf; 408 | case INI_section: 409 | section = intern(t.name); 410 | break; 411 | case INI_key: 412 | key = intern(t.name); 413 | break; 414 | case INI_value: 415 | if (section == sym_w64devkit) { 416 | if (!conf.home && key==sym_home) { 417 | arena temp = scratch; 418 | conf.home = expandvalue(t.name, &temp, *perm); 419 | conf.home = tofullpath(conf.home, perm); 420 | } else if (key == sym_title) { 421 | conf.title = expandvalue(t.name, perm, scratch); 422 | } 423 | } 424 | break; 425 | } 426 | } 427 | } 428 | 429 | typedef struct { 430 | c16 *buf; 431 | size cap; 432 | size len; 433 | b32 err; 434 | } buf16; 435 | 436 | static buf16 newbuf16(arena *a, size cap) 437 | { 438 | buf16 buf = {}; 439 | buf.buf = new(a, c16, cap); 440 | buf.cap = cap; 441 | return buf; 442 | } 443 | 444 | static void buf16cat(buf16 *buf, s16 s) 445 | { 446 | size avail = buf->cap - buf->len; 447 | size count = s.lenbuf + buf->len; 449 | for (size i = 0; i < count; i++) { 450 | dst[i] = s.s[i]; 451 | } 452 | buf->len += count; 453 | buf->err |= count < s.len; 454 | } 455 | 456 | static void buf16c16(buf16 *buf, c16 c) 457 | { 458 | s16 s = {&c, 1}; 459 | buf16cat(buf, s); 460 | } 461 | 462 | static void buf16moduledir(buf16 *buf, arena scratch) 463 | { 464 | // GetFullPathNameW does not allow querying the output size, instead 465 | // indicating whether or not the buffer was large enough. So simply 466 | // offer the entire scratch buffer, then crop out the actual result. 467 | scratch.beg += -(uptr)scratch.beg & (sizeof(c16) - 1); // align 468 | i32 len = truncsize((scratch.end - scratch.beg)/sizeof(c16)); 469 | s16 path = {}; 470 | path.s = (c16 *)scratch.beg; 471 | path.len = GetModuleFileNameW(0, path.s, len); 472 | if (len == path.len) outofmemory(); 473 | for (; path.len; path.len--) { 474 | switch (path.s[path.len-1]) { 475 | case '/': 476 | case '\\': path.len--; 477 | buf16cat(buf, path); 478 | return; 479 | } 480 | } 481 | } 482 | 483 | static void buf16getenv(buf16 *buf, c16 *key, arena scratch) 484 | { 485 | i32 len = GetEnvironmentVariableW(key, 0, 0); 486 | if (len < 0) outofmemory(); 487 | s16 var = {}; 488 | var.s = new(&scratch, c16, len); 489 | var.len = GetEnvironmentVariableW(key, var.s, len); 490 | buf16cat(buf, var); 491 | } 492 | 493 | static void toslashes(c16 *path) 494 | { 495 | for (size i = 0; i < path[i]; i++) { 496 | path[i] = path[i]=='\\' ? '/' : path[i]; 497 | } 498 | } 499 | 500 | static u32 w64devkit(void) 501 | { 502 | memory mem = newmemory(1<<22); 503 | if (!mem.base) { 504 | fatal(u"Out of memory on startup"); 505 | } 506 | arena *perm = &mem.perm; 507 | arena scratch = mem.scratch; 508 | 509 | // First load the module directory into the fresh buffer, and use it 510 | // for a few different operations. 511 | buf16 path = newbuf16(perm, MAX_ENVVAR); 512 | buf16moduledir(&path, scratch); 513 | buf16 moduledir = path; // to truncate back to the module dir 514 | 515 | buf16c16(&path, 0); // null terminator 516 | SetEnvironmentVariableW(u"W64DEVKIT_HOME", path.buf); // ignore errors 517 | 518 | #ifdef VERSION 519 | #define LSTR(s) XSTR(s) 520 | #define XSTR(s) u ## # s 521 | SetEnvironmentVariableW(u"W64DEVKIT", LSTR(VERSION)); // ignore errors 522 | #endif 523 | 524 | // Maybe set HOME from w64devkit.ini 525 | config conf = newconfig(); 526 | if (SetCurrentDirectoryW(path.buf)) { 527 | conf = loadconfig(perm, scratch); 528 | if (conf.home) { 529 | toslashes(conf.home); 530 | SetEnvironmentVariableW(u"HOME", conf.home); // ignore errors 531 | } 532 | } 533 | 534 | // Continue building PATH 535 | path = moduledir; 536 | buf16cat(&path, U(u"\\bin;")); 537 | buf16getenv(&path, u"PATH", scratch); 538 | buf16c16(&path, 0); // null terminator 539 | if (path.err || !SetEnvironmentVariableW(u"PATH", path.buf)) { 540 | fatal(u"Failed to configure $PATH"); 541 | } 542 | 543 | // Set the console title as late as possible, but not after starting 544 | // the shell because .profile might change it. 545 | if (conf.title) { 546 | SetConsoleTitleW(conf.title); // ignore errors 547 | } 548 | 549 | path = moduledir; 550 | buf16cat(&path, U(u"\\bin\\busybox.exe")); 551 | buf16c16(&path, 0); // null terminator 552 | 553 | // Start a BusyBox login shell 554 | si si = {}; 555 | si.cb = sizeof(si); 556 | pi pi; 557 | c16 cmdline[] = u"sh -l"; // NOTE: must be mutable! 558 | if (!CreateProcessW(path.buf, cmdline, 0, 0, 1, 0, 0, 0, &si, &pi)) { 559 | fatal(u"Failed to launch a login shell"); 560 | } 561 | 562 | // Wait for shell to exit 563 | freememory(mem); 564 | u32 ret; 565 | WaitForSingleObject(pi.process, -1); 566 | GetExitCodeProcess(pi.process, &ret); 567 | return ret; 568 | } 569 | 570 | __attribute((force_align_arg_pointer)) 571 | void mainCRTStartup(void) 572 | { 573 | u32 r = w64devkit(); 574 | ExitProcess(r); 575 | } 576 | -------------------------------------------------------------------------------- /src/w64devkit.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skeeto/w64devkit/ff4bb190f1b071069389d9187f0e03566fd44b97/src/w64devkit.ico -------------------------------------------------------------------------------- /src/w64devkit.ini: -------------------------------------------------------------------------------- 1 | ; w64devkit.ini -- configuration for w64devkit.exe 2 | 3 | [w64devkit] 4 | ; home: Sets the HOME environment variable for the shell. Place a 5 | ; .profile in this directory to do further environment configuration 6 | ; using the shell itself. 7 | ; 8 | ; This path may be relative and reference environment variables using 9 | ; percent symbols, like a batch script. Variables will be expanded, a 10 | ; la ExpandEnvironmentStrings, and a relative path will be converted to 11 | ; an absolute path relative to this .ini file. 12 | ; 13 | ; title: Sets the initial title for the new console window. This value 14 | ; may also contain environment variables. 15 | ; 16 | ;home = ..\home 17 | ;home = %HOMEDRIVE%%HOMEPATH% 18 | ;title = %USERNAME%@%COMPUTERNAME% [%W64DEVKIT_HOME% %W64DEVKIT%] 19 | --------------------------------------------------------------------------------