Skip to content

Instantly share code, notes, and snippets.

@androm3da
Created July 21, 2026 18:38
Show Gist options
  • Select an option

  • Save androm3da/40788432d6ffb006d86ea97e5bc4d83a to your computer and use it in GitHub Desktop.

Select an option

Save androm3da/40788432d6ffb006d86ea97e5bc4d83a to your computer and use it in GitHub Desktop.
Combine Linux + Baremetal/QuRT hexagon toolchain
#!/bin/bash
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
# SPDX-License-Identifier: BSD-3-Clause
# Take a baremetal or QuRT toolchain from e.g. https://github.com/snapdragon-toolchain/hexagon-sdk/releases
# and combine it with a linux toolchain from https://github.com/qualcomm/toolchain_for_hexagon/releases
set -euo pipefail
usage() {
echo "Usage: $0 <dest-toolchain-root> <src-linux-toolchain>"
echo ""
echo "Augments a baremetal-only Hexagon toolchain with Linux cross-compilation"
echo "support by copying the sysroot, QEMU, symlinks, and driver config from"
echo "a reference hexagon-unknown-linux-musl cross-toolchain."
echo ""
echo " dest-toolchain-root Path to the baremetal toolchain (e.g. .../Tools)"
echo " src-linux-toolchain Path to the reference Linux cross-toolchain host dir"
echo " (e.g. .../clang+llvm-22.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu)"
exit 1
}
[ $# -eq 2 ] || usage
DEST="$(cd "$1" && pwd)"
SRC="$(cd "$2" && pwd)"
BIN="${DEST}/bin"
TARGET="${DEST}/target"
SYSROOT="${TARGET}/hexagon-unknown-linux-musl"
# Validate inputs
[ -x "${BIN}/clang" ] || { echo "Error: ${BIN}/clang not found"; exit 1; }
[ -d "${SRC}/bin" ] || { echo "Error: ${SRC}/bin not found"; exit 1; }
[ -d "${SRC}/target/hexagon-unknown-linux-musl" ] || { echo "Error: source sysroot not found"; exit 1; }
# Resolve the real clang binary name for symlinks
CLANG_NAME="$(basename "$(readlink -f "${BIN}/clang")")"
echo "=== Adding hexagon-unknown-linux-musl support ==="
echo " dest: ${DEST}"
echo " src: ${SRC}"
echo ""
# --- 1. Copy Linux/musl sysroot ---
echo "Copying Linux/musl sysroot..."
if [ -d "${SYSROOT}" ]; then
echo " WARNING: ${SYSROOT} already exists, skipping"
else
cp -a "${SRC}/target/hexagon-unknown-linux-musl" "${TARGET}/"
fi
# Convenience symlink (skip if target/hexagon-linux-musl already exists)
ln -sfn hexagon-unknown-linux-musl "${TARGET}/hexagon-linux-musl" 2>/dev/null || true
# --- 2. Copy ld.lld from the reference toolchain ---
# The SDK driver hardcodes "hexagon-linux-link" as the linker, and ld.eld
# produces 4K-aligned ELF segments. QEMU Hexagon requires 64K (0x10000)
# alignment, so we use ld.lld instead.
echo "Copying ld.lld..."
if [ -x "${SRC}/bin/ld.lld" ]; then
cp -aL "${SRC}/bin/ld.lld" "${BIN}/"
elif [ -x "${SRC}/bin/lld" ]; then
cp -aL "${SRC}/bin/lld" "${BIN}/ld.lld"
fi
# Create hexagon-linux-link wrapper. The SDK driver invokes the linker as
# "hexagon-linux-link" which lld doesn't recognise via argv[0] (it needs
# to be called as "ld.lld" for the ELF flavour). This wrapper also strips
# eld-specific flags that lld doesn't understand and fixes --hash-style.
echo "Creating hexagon-linux-link wrapper..."
cat > "${BIN}/hexagon-linux-link" << 'LINK_WRAPPER'
#!/bin/sh
# Translate eld-specific linker flags for ld.lld
args=""
skip_next=0
for arg in "$@"; do
if [ "$skip_next" = 1 ]; then
skip_next=0
continue
fi
case "$arg" in
-march=hexagon) ;; # eld-specific, lld uses -m hexagonelf
-mcpu=hexagon*) ;; # eld-specific
-G[0-9]*) ;; # small-data threshold, eld-specific
-G) skip_next=1 ;; # -G <n> form
--hash-style=*) ;; # override below; musl needs .gnu.hash
*) args="$args $arg" ;;
esac
done
exec "$(dirname "$0")/ld.lld" --hash-style=both $args
LINK_WRAPPER
chmod +x "${BIN}/hexagon-linux-link"
# --- 3. Copy QEMU ---
echo "Copying QEMU..."
for qemu in qemu-hexagon qemu-system-hexagon; do
if [ -x "${SRC}/bin/${qemu}" ]; then
cp -a "${SRC}/bin/${qemu}" "${BIN}/"
echo " ${qemu}"
fi
done
# Create a relocatable qemu wrapper
cat > "${BIN}/qemu_wrapper.sh" << 'WRAPPER'
#!/bin/bash
set -euo pipefail
DIR="$(cd "$(dirname "$0")" && pwd)"
export QEMU_LD_PREFIX="${DIR}/../target/hexagon-unknown-linux-musl/usr"
exec "${DIR}/qemu-hexagon" "$@"
WRAPPER
chmod +x "${BIN}/qemu_wrapper.sh"
# --- 4. Create target-triple symlinks ---
echo "Creating hexagon-unknown-linux-musl-* symlinks..."
for tool in clang clang++; do
ln -sf "${CLANG_NAME}" "${BIN}/hexagon-unknown-linux-musl-${tool}"
done
ln -sf ld.lld "${BIN}/hexagon-unknown-linux-musl-ld.lld"
# Map tool names to their underlying binaries
declare -A TOOL_MAP=(
[ar]=llvm-ar
[nm]=llvm-nm
[objcopy]=llvm-objcopy
[objdump]=llvm-objdump
[ranlib]=llvm-ar
[readelf]=llvm-readobj
[size]=llvm-size
[strip]=llvm-objcopy
)
for tool in "${!TOOL_MAP[@]}"; do
target="${TOOL_MAP[$tool]}"
# Try the exact name first, then fall back to hexagon-prefixed names
if [ -e "${BIN}/${target}" ] || [ -L "${BIN}/${target}" ]; then
ln -sf "${target}" "${BIN}/hexagon-unknown-linux-musl-${tool}"
elif [ -e "${BIN}/hexagon-${target}" ] || [ -L "${BIN}/hexagon-${target}" ]; then
ln -sf "hexagon-${target}" "${BIN}/hexagon-unknown-linux-musl-${tool}"
fi
done
# Also create short-form aliases (hexagon-linux-musl-*)
echo "Creating hexagon-linux-musl-* alias symlinks..."
for f in "${BIN}"/hexagon-unknown-linux-musl-*; do
short="hexagon-linux-musl-$(basename "$f" | sed 's/^hexagon-unknown-linux-musl-//')"
ln -sf "$(basename "$f")" "${BIN}/${short}"
done
# --- 5. Create driver config file ---
# The SDK Hexagon driver hardcodes target/hexagon/ as the sysroot
# regardless of target triple. This cfg file overrides that for
# hexagon-unknown-linux-musl so that clang finds musl headers/libs.
echo "Creating hexagon-unknown-linux-musl.cfg..."
cat > "${BIN}/hexagon-unknown-linux-musl.cfg" << 'CFG'
# Clang driver configuration for hexagon-unknown-linux-musl
#
# This file is auto-discovered when clang is invoked as
# hexagon-unknown-linux-musl-clang (or with --target=hexagon-unknown-linux-musl
# and a matching --config).
# Point the sysroot at the musl-based Linux sysroot
--sysroot=<CFGDIR>/../target/hexagon-unknown-linux-musl
# Use the musl C and libc++ headers
-isystem <CFGDIR>/../target/hexagon-unknown-linux-musl/usr/include/c++/v1
-isystem <CFGDIR>/../target/hexagon-unknown-linux-musl/usr/include
# Use lld as the linker (lld produces 64K-aligned ELF segments required by QEMU)
--ld-path=<CFGDIR>/ld.lld
# Use compiler-rt and libunwind (the SDK driver defaults to --rtlib=libgcc
# which pulls in -lc_eh, a library that doesn't exist in the musl sysroot)
--rtlib=compiler-rt
--unwindlib=libunwind
CFG
echo ""
echo "=== Done ==="
echo ""
echo "Test with:"
echo " ${BIN}/hexagon-unknown-linux-musl-clang -o hello hello.c -static"
echo " ${BIN}/qemu_wrapper.sh ./hello"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment