# shellcheck shell=bash # The link step of the compiler drivers. # # A driver links by handing clang its link line with -fuse-ld=; clang # turns the line into the linker's arguments and starts the linker. In an # installed toolchain (libexec/brint-tool is there) the driver makes that # translation itself and calls the linker directly, so that a link needs no # process started by clang. In a wasm32 guest clang cannot start one: LLVM # spawns with posix_spawn, which the kernel refuses (linux#14). # # The translation is the one clang's wasm::Linker::ConstructJob makes for the # link lines these drivers build: target wasm32-hwjs-unknown, no sysroot, # -nostdlib. For such a line clang hands the linker # # -m wasm32 [--strip-all] <-L and -u, in order> -L/lib # [--entry _initialize -shared] [--shared-memory] -o # # where an input is a file, -l, the pieces of -Wl,a,b, the word after # -Xlinker, or -z . Everything else on the line is a compile option the # link job does not read. test/link-direct.sh holds this to what clang itself # produces, line by line. # # A line this file does not model is not guessed at: hwjs_link_direct_args # refuses it, and hwjs_link runs it through clang as before. What differs on # the direct path, by intent: # - clang runs wasm-opt on the output when the line carries -O and a wasm-opt # sits beside clang or on PATH. The direct path never does. # - a clang that is not cross-compiling (a guest's) puts $LIBRARY_PATH in # front of the inputs as -L. The direct path does not read it, which is # what the cross clang on a build host does. # - a failed link returns the linker's own status, without clang's # "linker command failed" line. # # Usage: # source libexec/link-direct.sh # hwjs_link "$CLANG" "$LINKER" # hwjs_link_direct_args : the linker's arguments in HWJS_LD_ARGS. # Returns 1, with the reason in HWJS_LD_WHY, for a line it does not model. hwjs_link_direct_args() { HWJS_LD_ARGS=(); HWJS_LD_WHY="" local search=() inputs=() out="a.out" local strip=0 shared=0 pthread=0 nostdlib=0 target="" local a piece local -a pieces while (($#)); do a="$1"; shift case "$a" in -o) (($#)) || { HWJS_LD_WHY="-o without a value"; return 1; } out="$1"; shift ;; -o?*) out="${a#-o}" ;; -L|-u|-l|-z|-Xlinker) (($#)) || { HWJS_LD_WHY="$a without a value"; return 1; } case "$a" in -L) search+=( "-L$1" ) ;; -u) search+=( -u "$1" ) ;; -l) inputs+=( "-l$1" ) ;; -z) inputs+=( -z "$1" ) ;; -Xlinker) inputs+=( "$1" ) ;; esac shift ;; -L?*) search+=( "$a" ) ;; -u?*) search+=( -u "${a#-u}" ) ;; -l?*) inputs+=( "$a" ) ;; -Wl,*) IFS=',' read -r -a pieces <<< "${a#-Wl,}" for piece in "${pieces[@]}"; do inputs+=( "$piece" ); done ;; -s) strip=1 ;; -shared) shared=1 ;; -pthread) pthread=1 ;; -no-pthread) pthread=0 ;; -nostdlib) nostdlib=1 ;; --target=*) target="${a#--target=}" ;; # Options whose link job is not the one written out above. -fuse-ld=*|-fprofile*|-fcoverage*|-fcs-profile*|-fsanitize*|-flto*|-fveclib*|-fopenmp*|-mexec-model=*|-mllvm) HWJS_LD_WHY="'$a' changes the link job"; return 1 ;; # Compile options that carry their value in the next word. -isystem|-I|-D|-U|-include|-idirafter|-iquote|-MF|-MT|-MQ) (($#)) || { HWJS_LD_WHY="$a without a value"; return 1; } shift ;; # Compile options in one word. -f?*|-m?*|-O*|-g*|-W*|-D?*|-U?*|-I?*|-std=*|-stdlib=*|-nostdlibinc|-nostdinc|-nostdinc++|-nostdlib++|-nodefaultlibs|-pipe|-w|-MD|-MMD|-MP|-static|-rdynamic|-pie|-no-pie) ;; -*|@*) HWJS_LD_WHY="'$a' is not an option this path models"; return 1 ;; # Files the linker reads as they are. Any other name clang would compile # first, or might. *.o|*.a|*.so|*.so.[0-9]*|*.lo|*.obj) inputs+=( "$a" ) ;; *) HWJS_LD_WHY="input '$a' is not an object, an archive or a shared library by name"; return 1 ;; esac done [[ "$target" == wasm32-hwjs-unknown ]] || { HWJS_LD_WHY="the link target is '${target:-unset}', not wasm32-hwjs-unknown"; return 1; } [[ "$nostdlib" == 1 ]] || { HWJS_LD_WHY="the line has no -nostdlib, so clang would add its own startfile and libraries"; return 1; } HWJS_LD_ARGS=( -m wasm32 ) [[ "$strip" == 1 ]] && HWJS_LD_ARGS+=( --strip-all ) HWJS_LD_ARGS+=( "${search[@]}" -L/lib ) [[ "$shared" == 1 ]] && HWJS_LD_ARGS+=( --entry _initialize -shared ) HWJS_LD_ARGS+=( "${inputs[@]}" ) [[ "$pthread" == 1 ]] && HWJS_LD_ARGS+=( --shared-memory ) HWJS_LD_ARGS+=( -o "$out" ) return 0 } # hwjs_link : run one link and return its status. # Reads HWJS_HOME (the toolchain root) to tell an installed toolchain from a # source checkout. HWJS_LINK_VIA_CLANG=1 keeps an installed toolchain on the # clang path. hwjs_link() { local clang="$1" linker="$2"; shift 2 if [[ -x "$HWJS_HOME/libexec/brint-tool" && "${HWJS_LINK_VIA_CLANG:-0}" != "1" ]]; then if hwjs_link_direct_args "$@"; then "$linker" "${HWJS_LD_ARGS[@]}" return $? fi echo "${0##*/}: NOTE (direct link): linking through clang instead: $HWJS_LD_WHY. In a guest clang cannot start the linker (linux#14)." >&2 fi "$clang" "$@" -fuse-ld="$linker" }