/* Copyright 2020 Zhuowei Zhang, Oliver Smith * SPDX-License-Identifier: GPL-3.0-or-later * * This program gets built to a set of wrapper executables, which launch native * cross compilers inside foreign arch chroots. Speeds up cross compilation a * lot, compared to using just qemu user mode emulation or using the previous * distcc-based methods. * * How this program gets called: * - pmbootstrap creates one native arch chroot and one foreign arch chroot. * - The native chroot gets mounted as /native in the foreign chroot. * - When calling "abuild", pmbootstrap sets PATH to: * /native/usr/lib/crossdirect/:$PATH * - That crossdirect directory contains a crossdirect- binary built with * the matching HOSTSPEC (e.g. crossdirect-aarch64 binary with HOSTSPEC * aarch64-alpine-linux-musl). This binary is symlinked to: * - -c++ * - -cc * - -clang * - -clang++ * - -cpp * - -g++ * - -gcc * - c++ * - cc * - clang * - clang++ * - cpp * - g++ * - gcc */ #include #include #include #include #include #include #include #include #define NATIVE_BIN_DIR "/native/usr/lib/ccache/bin" void exit_userfriendly() { fprintf(stderr, "Please report this at: https://gitlab.com/postmarketOS/pmaports/issues\n"); fprintf(stderr, "As a workaround, you can compile without crossdirect.\n"); fprintf(stderr, "See 'pmbootstrap -h' for related options.\n"); exit(1); } bool argv_has_arg(int argc, char **argv, const char *arg) { size_t arg_len = strlen(arg); for (int i = 1; i < argc; i++) { if (strlen(argv[i]) < arg_len) continue; if (!memcmp(argv[i], arg, arg_len)) return true; } return false; } int main(int argc, char **argv) { // we have a max of four extra args ("-target", "HOSTSPEC", "--sysroot=/", "-Wl,-rpath-link=/lib:/usr/lib"), plus one ending null char *newargv[argc + 5]; char *executableName = basename(argv[0]); char newExecutable[PATH_MAX]; bool isClang = (strcmp(executableName, "clang") == 0 || strcmp(executableName, "clang++") == 0); bool startsWithHostSpec = (strncmp(HOSTSPEC, executableName, sizeof(HOSTSPEC) - 1) == 0); // linker is involved: just use qemu binary (to avoid broken cross-ld, pmaports#227) if (!argv_has_arg(argc, argv, "-c")) { snprintf(newExecutable, sizeof(newExecutable), "/usr/bin/%s", executableName); if (execv(newExecutable, argv) == -1) { fprintf(stderr, "ERROR: crossdirect: failed to execute %s: %s\n", newExecutable, strerror(errno)); fprintf(stderr, "NOTE: this is a foreign arch binary that would run with qemu (linker is involved).\n"); exit_userfriendly(); } } // Translate "cc" to "gcc": /usr/bin/cc is a symlink to /usr/bin/gcc, // but there is no -cc symlink (pmaports#732) if (strcmp(executableName, "cc") == 0) executableName = "gcc"; // prepend the HOSTSPEC to GCC binaries if (isClang || startsWithHostSpec) { snprintf(newExecutable, sizeof(newExecutable), NATIVE_BIN_DIR "/%s", executableName); } else { snprintf(newExecutable, sizeof(newExecutable), NATIVE_BIN_DIR "/" HOSTSPEC "-%s", executableName); } // prepare new arguments for GCC / clang char **newArgsPtr = newargv; *newArgsPtr++ = newExecutable; if (isClang) { // clang does not use a HOSTSPEC prefix for the cross compiler // binary, but instead have a -target argument *newArgsPtr++ = "-target"; *newArgsPtr++ = HOSTSPEC; } *newArgsPtr++ = "--sysroot=/"; // add all arguments passed to this executable to GCC / clang and set // the last arg in newargv to NULL to mark its end memcpy(newArgsPtr, argv + 1, sizeof(char *) * (argc - 1)); newArgsPtr += (argc - 1); *newArgsPtr = NULL; // new arguments prepared; now setup environmental vars char *env[] = { "LD_PRELOAD=", "LD_LIBRARY_PATH=/native/lib:/native/usr/lib", "CCACHE_PATH=/native/usr/bin", NULL }; char *ldPreload = getenv("LD_PRELOAD"); if (ldPreload) { if (strcmp(ldPreload, "/usr/lib/libfakeroot.so") == 0) { env[0] = "LD_PRELOAD=/native/usr/lib/libfakeroot.so"; } else { fprintf(stderr, "ERROR: crossdirect: can't handle LD_PRELOAD: %s\n", ldPreload); exit_userfriendly(); } } // finally exec GCC / clang if (execve(newExecutable, newargv, env) == -1) { fprintf(stderr, "ERROR: crossdirect: failed to execute %s: %s\n", newExecutable, strerror(errno)); fprintf(stderr, "Maybe the target arch is missing in the ccache-cross-symlinks package?\n"); exit_userfriendly(); } return 1; }