diff --git a/Dockerfile b/Dockerfile index 27e3831..e504e94 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,8 +21,18 @@ ENV TOOLCHAIN_ARCHS="i686 x86_64 armv7 aarch64" # Build everything and clean up, in one step COPY *.sh libssp-Makefile ./ COPY wrappers/*.sh ./wrappers/ +COPY wrappers/*.c ./wrappers/ RUN ./build-all.sh $TOOLCHAIN_PREFIX && \ ./strip-llvm.sh $TOOLCHAIN_PREFIX && \ rm -rf /build +# Cheating: Pull strip and objcopy from the normal binutils package. +RUN apt-get update -qq && \ + apt-get install -qqy binutils-mingw-w64-x86-64 && \ + cp /usr/bin/x86_64-w64-mingw32-strip $TOOLCHAIN_PREFIX/bin/binutils-strip && \ + cp /usr/bin/x86_64-w64-mingw32-objcopy $TOOLCHAIN_PREFIX/bin/binutils-objcopy && \ + apt-get remove -qqy binutils-mingw-w64-x86-64 && \ + apt-get clean -y && \ + rm -rf /var/lib/apt/lists/* + ENV PATH=$TOOLCHAIN_PREFIX/bin:$PATH diff --git a/Dockerfile.dev b/Dockerfile.dev index 3e32d72..cdcc6d1 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -25,6 +25,7 @@ ENV TOOLCHAIN_ARCHS="i686 x86_64 armv7 aarch64" # Install the usual $TUPLE-clang binaries COPY wrappers/*.sh ./wrappers/ +COPY wrappers/*.c ./wrappers/ COPY install-wrappers.sh . RUN ./install-wrappers.sh $TOOLCHAIN_PREFIX @@ -72,3 +73,12 @@ RUN ./build-libssp.sh $TOOLCHAIN_PREFIX # size as long as it is in a separate RUN layer though.) COPY strip-llvm.sh . RUN ./strip-llvm.sh $TOOLCHAIN_PREFIX + +# Cheating: Pull strip and objcopy from the normal binutils package. +RUN apt-get update -qq && \ + apt-get install -qqy binutils-mingw-w64-x86-64 && \ + cp /usr/bin/x86_64-w64-mingw32-strip $TOOLCHAIN_PREFIX/bin/binutils-strip && \ + cp /usr/bin/x86_64-w64-mingw32-objcopy $TOOLCHAIN_PREFIX/bin/binutils-objcopy && \ + apt-get remove -qqy binutils-mingw-w64-x86-64 && \ + apt-get clean -y && \ + rm -rf /var/lib/apt/lists/* diff --git a/install-wrappers.sh b/install-wrappers.sh index 08c53b2..22538e6 100755 --- a/install-wrappers.sh +++ b/install-wrappers.sh @@ -12,6 +12,7 @@ PREFIX="$1" mkdir -p $PREFIX/bin cp wrappers/*-wrapper.sh $PREFIX/bin +cc wrappers/change-pe-arch.c -o $PREFIX/bin/change-pe-arch cd $PREFIX/bin for arch in $ARCHS; do for exec in clang clang++ gcc g++; do @@ -20,10 +21,10 @@ for arch in $ARCHS; do for exec in ar ranlib nm strings; do ln -sf llvm-$exec $arch-w64-mingw32-$exec || true done - for exec in strip; do - ln -sf $(which true) $arch-w64-mingw32-$exec - done for exec in ld objdump windres dlltool; do ln -sf $exec-wrapper.sh $arch-w64-mingw32-$exec done + for exec in objcopy strip; do + ln -sf objcopy-wrapper.sh $arch-w64-mingw32-$exec + done done diff --git a/wrappers/change-pe-arch.c b/wrappers/change-pe-arch.c new file mode 100644 index 0000000..36562c8 --- /dev/null +++ b/wrappers/change-pe-arch.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2018 Martin Storsjo + * + * This file is part of llvm-mingw. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#define IMAGE_DOS_SIGNATURE 0x5A4D +#define IMAGE_NT_SIGNATURE 0x00004550 + +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_ARMNT 0x1C4 +#define IMAGE_FILE_MACHINE_ARM64 0xAA64 +#define IMAGE_FILE_MACHINE_I386 0x14C + +static uint16_t readUint16(FILE *f) { + uint8_t buf[2]; + if (fread(buf, 1, 2, f) != 2) + return -1; + return buf[0] | (buf[1] << 8); +} + +static uint32_t readUint32(FILE *f) { + uint8_t buf[4]; + if (fread(buf, 1, 4, f) != 4) + return -1; + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + +static void writeUint16(FILE *f, uint16_t val) { + uint8_t buf[2]; + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; + if (fwrite(buf, 1, 2, f) != 2) + perror("fwrite"); +} + +static void writeUint32(FILE *f, uint32_t val) { + uint8_t buf[4]; + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[2] = (val >> 16) & 0xff; + buf[3] = (val >> 24) & 0xff; + if (fwrite(buf, 1, 4, f) != 4) + perror("fwrite"); +} + +static uint16_t archToInt(const char *str) { + if (!strcmp(str, "i686")) + return IMAGE_FILE_MACHINE_I386; + else if (!strcmp(str, "x86_64")) + return IMAGE_FILE_MACHINE_AMD64; + else if (!strcmp(str, "armv7")) + return IMAGE_FILE_MACHINE_ARMNT; + else if (!strcmp(str, "aarch64")) + return IMAGE_FILE_MACHINE_ARM64; + fprintf(stderr, "Unknown architecture %s\n", str); + return 0; +} + +static int is64Bit(uint16_t arch) { + switch (arch) { + case IMAGE_FILE_MACHINE_AMD64: + case IMAGE_FILE_MACHINE_ARM64: + return 1; + default: + return 0; + } +} + +void help(const char *argv0) { + printf("%s [-check] [-from arch] [-to arch] file\n", argv0); +} + +int main(int argc, char *argv[]) { + int check = 0; + uint16_t from = 0, to = 0; + int i; + const char *filename = NULL; + FILE *f; + uint32_t offset; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-check")) { + check = 1; + } else if (!strcmp(argv[i], "-from") && i + 1 < argc) { + from = archToInt(argv[++i]); + } else if (!strcmp(argv[i], "-to") && i + 1 < argc) { + to = archToInt(argv[++i]); + } else if (!filename) { + filename = argv[i]; + } else { + help(argv[0]); + return 1; + } + } + if ((!check && (!from || !to)) || !filename) { + help(argv[0]); + return 1; + } + if (!check && is64Bit(from) != is64Bit(to)) { + fprintf(stderr, "Mismatched atchitecture bitness\n"); + return 1; + } + f = fopen(filename, "r+b"); + if (!f) { + perror(filename); + return 1; + } + if (readUint16(f) != IMAGE_DOS_SIGNATURE) { + if (!check) + fprintf(stderr, "Incorrect DOS header signature\n"); + goto fail; + } + if (fseek(f, 0x3c, SEEK_SET) == -1) { // offsetof(IMAGE_DOS_HEADER, e_lfanew) + if (!check) + fprintf(stderr, "Unable to seek\n"); + goto fail; + } + offset = readUint32(f); + if (fseek(f, offset, SEEK_SET) == -1) { + if (!check) + fprintf(stderr, "Unable to seek\n"); + goto fail; + } + if (readUint32(f) != IMAGE_NT_SIGNATURE) { + if (!check) + fprintf(stderr, "Incorrect NT header signature\n"); + goto fail; + } + if (check) + goto done; + if (readUint16(f) != from) { + fprintf(stderr, "Unexpected architecture\n"); + goto fail; + } + fseek(f, -2, SEEK_CUR); + writeUint16(f, to); + if (fseek(f, offset + 0x58, SEEK_SET) == -1) { // offsetof(IMAGE_NT_HEADERS, OptionalHeader.CheckSum) + fprintf(stderr, "Unable to seek\n"); + goto fail; + } + writeUint32(f, 0); +done: + fclose(f); + return 0; +fail: + fclose(f); + return 1; +} diff --git a/wrappers/objcopy-wrapper.sh b/wrappers/objcopy-wrapper.sh new file mode 100755 index 0000000..8e6ef64 --- /dev/null +++ b/wrappers/objcopy-wrapper.sh @@ -0,0 +1,90 @@ +#!/bin/sh + +set -e + +DIR="$(cd "$(dirname "$0")" && pwd)" +TARGET="$(basename $0 | sed 's/-[^-]*$//')" +EXE=$(basename $0 | sed 's/.*-\([^-]*\)/\1/') +ARCH=$(echo $TARGET | sed 's/-.*//') + +IN="" +OUT="" +ARGS="" +STRIP="" +while [ $# -gt 0 ]; do + case $1 in + --strip*|-g|-S) + STRIP=1 + ;; + -*) + ;; + *) + if [ -z "$IN" ]; then + IN="$1" + elif [ -z "$OUT" ]; then + OUT="$1" + else + echo Unhandled arg $1 + exit 1 + fi + ;; + esac + ARGS="$ARGS $1" + shift +done +if [ -n "$IN" ] && [ -z "$OUT" ]; then + OUT="$IN" +fi + +case $ARCH in +i686|x86_64) COMPAT_ARCH=$ARCH;; +armv7) COMPAT_ARCH=i686;; +aarch64) COMPAT_ARCH=x86_64;; +esac + +convert() { + if [ "$ARCH" = "$COMPAT_ARCH" ]; then + return + fi + $DIR/change-pe-arch "$@" +} + +checkstrip() { + if [ "$EXE" = "strip" ]; then + return 0 + elif [ -n "$STRIP" ]; then + if [ "$IN" != "$OUT" ]; then + cp "$IN" "$OUT" + fi + return 0 + else + return 1 + fi +} + +if [ "$ARCH" != "$COMPAT_ARCH" ] && ! $DIR/change-pe-arch -check "$IN"; then + if checkstrip; then + echo Ignoring strip of non-executable \(object file\?\) >&2 + exit 0 + else + echo Unsupported "$0 $ARGS", not supported for non-executable \(object file\?\) >&2 + exit 1 + fi +fi + +if [ -x $DIR/binutils-$EXE ]; then + convert -from $ARCH -to $COMPAT_ARCH "$IN" + $DIR/binutils-$EXE $ARGS + convert -from $COMPAT_ARCH -to $ARCH "$OUT" + if [ "$OUT" != "$IN" ]; then + convert -from $COMPAT_ARCH -to $ARCH "$IN" + fi +else + if checkstrip; then + echo Ignoring strip, missing binutils-$EXE >&2 + exit 0 + else + echo Unsupported "$0 $ARGS", missing binutils-$EXE >&2 + exit 1 + fi +fi