diff --git a/.github/workflows/build_crosscompilation.yml b/.github/workflows/build_crosscompilation.yml new file mode 100644 index 00000000000..9619512218f --- /dev/null +++ b/.github/workflows/build_crosscompilation.yml @@ -0,0 +1,27 @@ +name: Cross-compilation +on: + push: + branches: + - dev + pull_request: + branches: + - dev + types: [opened, synchronize, reopened] + release: + types: [created] + workflow_dispatch: + +jobs: + crosscompilation: + name: AArch64 on ubuntu-latest x86_64 + runs-on: ubuntu-latest + env: + CFLAGS: -Werror -DNDPI_EXTENDED_SANITY_CHECKS + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Run script + run: | + ./utils/cross-compile-test.sh --full + #No real tests, only `ndpiReader -H` diff --git a/configure.ac b/configure.ac index 56b08530e2d..1f792eb83d9 100644 --- a/configure.ac +++ b/configure.ac @@ -123,22 +123,28 @@ AS_IF([test "x${enable_fuzztargets}" = "xyes" -a "x${enable_static}" = "xno"],[ AC_MSG_ERROR([--enable-fuzztargets requires static library. Cannot use with --disable-static.]) ]) -PKG_PROG_PKG_CONFIG - # Detect build and host system for cross-compilation support # Must be called early, before AC_PROG_CC AC_CANONICAL_HOST +PKG_PROG_PKG_CONFIG + +# Prefer clang on Darwin if user didn't explicitly set CC. +# Must be done before AC_PROG_CC so the preference takes effect. +case "$host_os" in + darwin*) + : ${CC:=clang} + : ${CXX:=clang++} + ;; +esac + AC_PROG_CC -AC_PROG_CPP_WERROR AC_C_INLINE -# Prefer clang on Darwin if user didn't explicitly set CC case "$host_os" in darwin*) - dnl> AC_PROG_CC(clang gcc) - AM_PROG_CC_C_O(clang gcc) - AC_PROG_CXX(clang++ g++) + AM_PROG_CC_C_O + AC_PROG_CXX ;; *) AC_PROG_CC_C_O @@ -146,12 +152,25 @@ case "$host_os" in ;; esac +# Warn when cross-compiling without proper sysroot/pkg-config setup. +# PKG_PROG_PKG_CONFIG uses AC_PATH_TOOL so it finds ${host}-pkg-config, +# but PKG_CONFIG_SYSROOT_DIR / PKG_CONFIG_LIBDIR must still be set for +# the target sysroot. Build-host paths (Homebrew, DPDK, PCAP_HOME with +# PF_RING) are also silently wrong for the target in this case. +AS_IF([test "$cross_compiling" = yes], [ + AC_MSG_WARN([Cross-compiling: build=$build -> host=$host]) + AS_IF([test -z "$PKG_CONFIG_SYSROOT_DIR" -a -z "$PKG_CONFIG_LIBDIR"], [ + AC_MSG_WARN([PKG_CONFIG_SYSROOT_DIR and PKG_CONFIG_LIBDIR are not set.]) + AC_MSG_WARN([pkg-config may query the build host instead of the target sysroot.]) + AC_MSG_WARN([Set PKG_CONFIG_SYSROOT_DIR and PKG_CONFIG_LIBDIR for correct library detection.]) + ]) + AC_MSG_WARN([Build-host paths (Homebrew, DPDK, PCAP_HOME with PF_RING) may not apply to the target.]) +]) + dnl> Can't use AM_PROG_AR because not all of our Makefiles are automake (yet?) AC_CHECK_TOOL(AR, ar, [false]) AC_CHECK_TOOL([RANLIB], [ranlib], [:]) -AC_LANG_WERROR - # Set OS_TYPE for Makefiles based on host system case "$host_os" in darwin*) @@ -166,7 +185,7 @@ case "$host_os" in esac AC_SUBST(OS_TYPE) -if test $OS_TYPE = "Darwin"; then +if test "x$OS_TYPE" = "xDarwin"; then NDPI_CFLAGS="${NDPI_CFLAGS} -fno-color-diagnostics -fPIE" NDPI_LDFLAGS="${NDPI_LDFLAGS} -fno-color-diagnostics -fPIE" fi @@ -258,7 +277,9 @@ else GIT_TAG="tarball" # Better fallback for GIT_DATE when not in git repo if test -f "${srcdir}/CHANGELOG.md"; then - GIT_DATE=`date -u -r ${srcdir}/CHANGELOG.md 2>/dev/null || date -u` + GIT_DATE=`stat -c '%y' "${srcdir}/CHANGELOG.md" 2>/dev/null || \ + stat -f '%Sm' "${srcdir}/CHANGELOG.md" 2>/dev/null || \ + date -u` else GIT_DATE=`date -u` AC_MSG_WARN([CHANGELOG.md not found, using current date]) @@ -283,7 +304,7 @@ NDPI_CFLAGS="-W -Wall -Wextra -Wno-address-of-packed-member ${NDPI_CFLAGS}" dnl> MacOS brew.sh HOMEBREW_DIR=/opt/homebrew -if test -d $HOMEBREW_DIR; then +if test -d "$HOMEBREW_DIR" && test "$cross_compiling" != yes; then # On latest macOS versions on M* archs, some (all?) libraries are not installed # "system-wide" anymore. NDPI_CFLAGS="${NDPI_CFLAGS} -I ${HOMEBREW_DIR}/include" @@ -369,14 +390,14 @@ PCAP_HOME=$HOME/PF_RING/userland DPDK_TARGET= AC_MSG_CHECKING([DPDK (used by ndpiReader)]) -if test -d $HOME/DPDK; then : +if test -d "$HOME/DPDK"; then : AC_MSG_RESULT(yes) DPDK_TARGET=dpdk else AC_MSG_RESULT([no (missing $HOME/DPDK)]) fi -if ! test -d $PCAP_HOME; then : +if ! test -d "$PCAP_HOME"; then : PCAP_HOME=${srcdir}/../../PF_RING/userland fi @@ -457,14 +478,17 @@ case "$host" in dnl> Try to get additional dependencies from pkg-config if available if test -f "$LIBPCAP_PATH/lib/pkgconfig/libpcap.pc"; then : + ax_save_pkg_config_path="$PKG_CONFIG_PATH" export PKG_CONFIG_PATH="$LIBPCAP_PATH/lib/pkgconfig:$PKG_CONFIG_PATH" PKG_CHECK_MODULES([PCAP_DEPS], [libpcap], [ - PCAP_PRIVATE_LIBS=`$PKG_CONFIG --libs --static libpcap | sed "s|-L$LIBPCAP_PATH/lib *||" | sed 's|-lpcap *||'` + PCAP_PRIVATE_LIBS=`"$PKG_CONFIG" --libs --static libpcap | sed "s|-L$LIBPCAP_PATH/lib *||" | sed 's|-lpcap *||'` PCAP_LIB="$PCAP_LIB $PCAP_PRIVATE_LIBS" echo " Added dependencies from pkg-config: $PCAP_PRIVATE_LIBS" ], [ echo " Warning: Could not get dependencies from pkg-config, using defaults" ]) + PKG_CONFIG_PATH="$ax_save_pkg_config_path" + export PKG_CONFIG_PATH fi elif test -f $PCAP_HOME/libpcap/libpcap.a; then : echo "Using libpcap from $PCAP_HOME" @@ -474,7 +498,11 @@ case "$host" in PFRING_LIB=$PCAP_HOME/lib/libpfring.a fi - PCAP_LIB="$PCAP_HOME/libpcap/libpcap.a $PFRING_LIB $LIBNUMA `$PCAP_HOME/lib/pfring_config --libs`" + PFRING_CONFIG_LIBS="" + AS_IF([test -x "$PCAP_HOME/lib/pfring_config"], [ + PFRING_CONFIG_LIBS=`"$PCAP_HOME/lib/pfring_config" --libs 2>/dev/null` + ]) + PCAP_LIB="$PCAP_HOME/libpcap/libpcap.a $PFRING_LIB $LIBNUMA $PFRING_CONFIG_LIBS" AC_CHECK_LIB([rt], [clock_gettime], [PCAP_LIB="$PCAP_LIB -lrt"]) AC_CHECK_LIB([nl], [nl_handle_alloc], [PCAP_LIB="$PCAP_LIB -lnl"]) # The dlopen() function is in libdl on GLIBC-based systems diff --git a/utils/cross-compile-test.sh b/utils/cross-compile-test.sh new file mode 100755 index 00000000000..b546239ab55 --- /dev/null +++ b/utils/cross-compile-test.sh @@ -0,0 +1,500 @@ +#!/usr/bin/env bash +# ============================================================================= +# cross-compile-test.sh — Test nDPI cross-compilation +# Host : Ubuntu x86-64 +# Target: AArch64 (aarch64-linux-gnu) +# +# Usage: +# ./utils/cross-compile-test.sh [OPTIONS] +# +# Modes (mutually exclusive): +# --library-only (default) Build libndpi only; no cross-compiled host libs +# needed. Fast and self-contained. +# --full Add arm64 multiarch, install cross-compiled dev libs, +# build examples and unit tests, then run ndpiReader -H +# on the host via QEMU user-mode emulation. +# +# Options: +# --clean Remove the build directory and exit. +# --jobs N Parallel make jobs (default: all CPUs via nproc). +# --build-dir DIR Override the default build directory path. +# --help Show this message. +# +# What is tested: +# 1. Cross-compiler selection (CC/CXX set before AC_PROG_CC). +# 2. configure.ac cross-compilation warning (fires when PKG_CONFIG_SYSROOT_DIR +# is unset; suppressed in --full mode where it is explicitly set). +# 3. Homebrew path guard (should be skipped on non-Darwin host). +# 4. pfring_config conditional execution. +# 5. date portability fix (stat-based file mtime). +# 6. The final library/binary is AArch64, not x86-64. +# 7. (--full) ndpiReader -H executes correctly under QEMU user-mode emulation. +# ============================================================================= +set -euo pipefail + +# --------------------------------------------------------------------------- +# Globals +# --------------------------------------------------------------------------- +SCRIPT_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" +NDPI_ROOT="$(realpath "${SCRIPT_DIR}/..")" +TARGET_TRIPLE="aarch64-linux-gnu" +DEFAULT_BUILD_DIR="${NDPI_ROOT}/build-cross-${TARGET_TRIPLE}" +JOBS="$(nproc)" +MODE="library-only" +DO_CLEAN=false +BUILD_DIR="${DEFAULT_BUILD_DIR}" + +# --------------------------------------------------------------------------- +# Colour helpers +# --------------------------------------------------------------------------- +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; NC='\033[0m' +info() { echo -e "${BOLD}[INFO]${NC} $*"; } +ok() { echo -e "${GREEN}[ OK ]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +die() { echo -e "${RED}[FAIL]${NC} $*" >&2; exit 1; } +sep() { echo -e "${BOLD}------------------------------------------------------------------------${NC}"; } + +# --------------------------------------------------------------------------- +# Argument parsing +# --------------------------------------------------------------------------- +while [[ $# -gt 0 ]]; do + case "$1" in + --library-only) MODE="library-only" ;; + --full) MODE="full" ;; + --clean) DO_CLEAN=true ;; + --jobs) shift; JOBS="$1" ;; + --build-dir) shift; BUILD_DIR="$1" ;; + --help|-h) sed -n '2,30p' "$0"; exit 0 ;; + *) die "Unknown argument: $1. Run with --help for usage." ;; + esac + shift +done + +# --------------------------------------------------------------------------- +# Clean +# --------------------------------------------------------------------------- +if $DO_CLEAN; then + info "Removing ${BUILD_DIR}" + rm -rf "${BUILD_DIR}" + ok "Done." + exit 0 +fi + +# --------------------------------------------------------------------------- +# Host sanity checks +# --------------------------------------------------------------------------- +sep +info "Host checks" + +[[ "$(uname -s)" == "Linux" ]] || die "This script is designed for Linux hosts." +[[ "$(uname -m)" == "x86_64" ]] || die "This script requires an x86-64 host." + +if command -v lsb_release &>/dev/null && lsb_release -si 2>/dev/null | grep -qi ubuntu; then + UBUNTU_VER="$(lsb_release -sr)" + ok "Ubuntu ${UBUNTU_VER} detected." +else + warn "Not Ubuntu — package names may differ; continuing anyway." +fi + +# --------------------------------------------------------------------------- +# Install toolchain packages +# --------------------------------------------------------------------------- +sep +info "Installing cross-compilation toolchain (${TARGET_TRIPLE})" + +COMMON_PKGS=(autoconf automake libtool make file) + +# crossbuild-essential-arm64 is available on Ubuntu 18.04+ +if apt-cache show crossbuild-essential-arm64 &>/dev/null 2>&1; then + TOOLCHAIN_PKGS=(crossbuild-essential-arm64) +else + TOOLCHAIN_PKGS=( + binutils-${TARGET_TRIPLE} + gcc-${TARGET_TRIPLE} + g++-${TARGET_TRIPLE} + ) +fi + +sudo apt-get install -y --no-install-recommends "${TOOLCHAIN_PKGS[@]}" "${COMMON_PKGS[@]}" +ok "Toolchain installed." + +# Verify cross-compiler is reachable +CC_CROSS="${TARGET_TRIPLE}-gcc" +command -v "${CC_CROSS}" &>/dev/null \ + || die "Cross-compiler ${CC_CROSS} not found after install." +ok "Cross-compiler: $(command -v "${CC_CROSS}") ($(${CC_CROSS} --version | head -1))" + +# --------------------------------------------------------------------------- +# Helper: add ports.ubuntu.com as the arm64 apt source. +# +# On a standard Ubuntu amd64 install, archive.ubuntu.com and +# security.ubuntu.com only carry amd64/i386 packages. arm64 packages live +# on ports.ubuntu.com. Without this, 'apt-get update' returns 404s for +# every arm64 index file and ':arm64' package installs fail. +# +# We also add an Architectures: restriction to the existing main sources so +# that apt no longer tries (and fails) to fetch arm64 indices from the +# amd64-only mirrors. +# --------------------------------------------------------------------------- +setup_arm64_apt_sources() { + local codename + codename="$(lsb_release -sc 2>/dev/null || echo "noble")" + + if [[ -f /etc/apt/sources.list.d/ubuntu.sources ]]; then + # Ubuntu 24.04+ deb822 format + local ports_file="/etc/apt/sources.list.d/ubuntu-ports-arm64.sources" + if [[ ! -f "${ports_file}" ]]; then + info "Creating ${ports_file} (ports.ubuntu.com, arm64)" + sudo tee "${ports_file}" >/dev/null <"${tmpfile}" + sudo cp "${tmpfile}" /etc/apt/sources.list.d/ubuntu.sources + rm -f "${tmpfile}" + ok "Restricted ubuntu.sources to amd64 i386." + else + ok "ubuntu.sources already has Architectures field." + fi + + elif [[ -f /etc/apt/sources.list ]]; then + # Ubuntu 22.04 and earlier: plain sources.list + local ports_file="/etc/apt/sources.list.d/ubuntu-ports-arm64.list" + if [[ ! -f "${ports_file}" ]]; then + info "Creating ${ports_file} (ports.ubuntu.com, arm64)" + sudo tee "${ports_file}" >/dev/null </dev/null; then + PKG_CONFIG_CROSS="${TARGET_TRIPLE}-pkg-config" + ok "Cross pkg-config: $(command -v "${PKG_CONFIG_CROSS}")" +else + PKG_CONFIG_CROSS="pkg-config" + warn "${TARGET_TRIPLE}-pkg-config not found; using plain pkg-config with sysroot vars." +fi + +# --------------------------------------------------------------------------- +# Regenerate configure if needed +# --------------------------------------------------------------------------- +sep +info "Checking autotools artefacts" + +if [[ ! -f "${NDPI_ROOT}/configure" || \ + "${NDPI_ROOT}/configure.ac" -nt "${NDPI_ROOT}/configure" ]]; then + info "Running autoreconf (configure is missing or stale)" + (cd "${NDPI_ROOT}" && autoreconf -fi) + ok "autoreconf done." +else + ok "configure is up to date." +fi + +# --------------------------------------------------------------------------- +# Prepare VPATH build directory +# --------------------------------------------------------------------------- +sep +info "Preparing build directory: ${BUILD_DIR}" +mkdir -p "${BUILD_DIR}" + +# --------------------------------------------------------------------------- +# Build up configure arguments +# --------------------------------------------------------------------------- +CONFIGURE_ARGS=( + --host="${TARGET_TRIPLE}" + --build="$(cc -dumpmachine 2>/dev/null || gcc -dumpmachine 2>/dev/null || echo "$(uname -m)-linux-gnu")" + CC="${CC_CROSS}" + CXX="${TARGET_TRIPLE}-g++" + AR="${TARGET_TRIPLE}-ar" + RANLIB="${TARGET_TRIPLE}-ranlib" + STRIP="${TARGET_TRIPLE}-strip" + PKG_CONFIG="${PKG_CONFIG_CROSS}" +) + +if [[ "$MODE" == "library-only" ]]; then + # No external deps needed — fastest path + CONFIGURE_ARGS+=(--with-only-libndpi) + info "Mode: library-only (--with-only-libndpi)" +else + # Tell pkg-config where the target .pc files and sysroot live. + # When ${TARGET_TRIPLE}-pkg-config exists it already knows this, but + # setting these explicitly also satisfies the configure warning we emit + # when PKG_CONFIG_SYSROOT_DIR is empty during a cross build. + CONFIGURE_ARGS+=( + PKG_CONFIG_LIBDIR="/usr/lib/${TARGET_TRIPLE}/pkgconfig:/usr/share/pkgconfig" + PKG_CONFIG_SYSROOT_DIR="/" + --with-pcre2 + --with-maxminddb + ) + info "Mode: full (examples + unit tests, with cross-compiled dev libs)" +fi + +# --------------------------------------------------------------------------- +# Configure +# --------------------------------------------------------------------------- +sep +info "Configuring..." + +CONFIGURE_LOG="${BUILD_DIR}/configure.log" +( + cd "${BUILD_DIR}" + "${NDPI_ROOT}/configure" "${CONFIGURE_ARGS[@]}" 2>&1 | tee "${CONFIGURE_LOG}" +) + +echo +info "--- configure warnings related to cross-compilation ---" +# Surface any warnings that our configure.ac emits for cross-builds +grep -E "Cross-compil|PKG_CONFIG_SYSROOT|PKG_CONFIG_LIBDIR|build-host path|Homebrew|DPDK|PCAP_HOME" \ + "${CONFIGURE_LOG}" || echo " (none)" +echo + +ok "configure complete." + +# --------------------------------------------------------------------------- +# Build +# --------------------------------------------------------------------------- +sep +info "Building (${JOBS} jobs)..." +make -C "${BUILD_DIR}" -j"${JOBS}" +ok "Build complete." + +# --------------------------------------------------------------------------- +# Verify outputs +# --------------------------------------------------------------------------- +sep +info "=== Verification ===" + +READELF="${TARGET_TRIPLE}-readelf" + +# Locate the built library (prefer shared, fall back to static) +LIB_SO="$(find "${BUILD_DIR}/src/lib" -name 'libndpi.so.*' ! -name '*.la' \ + 2>/dev/null | sort -V | tail -1 || true)" +LIB_A="$(find "${BUILD_DIR}/src/lib" -name 'libndpi.a' \ + 2>/dev/null | head -1 || true)" +LIB="${LIB_SO:-${LIB_A}}" +[[ -n "${LIB}" ]] || die "No libndpi.{so,a} found under ${BUILD_DIR}/src/lib" + +check_arch() { + local path="$1" + local label="$2" + local file_out arch_ok=false + + file_out="$(file "${path}")" + echo " file: ${file_out}" + + # 'file' on .so reports "ARM aarch64" or "ELF 64-bit LSB ... ARM" + # 'file' on .a reports "current ar archive" — probe with readelf instead + if echo "${file_out}" | grep -qiE "aarch64|ARM aarch64|64-bit.*ARM"; then + arch_ok=true + elif echo "${file_out}" | grep -qi "current ar archive"; then + if command -v "${READELF}" &>/dev/null; then + local re_out + re_out="$("${READELF}" -h "${path}" 2>/dev/null || true)" + echo " readelf machine: $(echo "${re_out}" | grep 'Machine:' || echo ' (unknown)')" + echo "${re_out}" | grep -qiE "aarch64|AArch64" && arch_ok=true + fi + fi + + if $arch_ok; then + ok "${label} is AArch64." + else + die "${label} is NOT AArch64! Cross-compilation may have failed.\n file output: ${file_out}" + fi + + # Sanity: no x86 code should be present + if command -v "${READELF}" &>/dev/null; then + local machine + machine="$("${READELF}" -h "${path}" 2>/dev/null | grep 'Machine:' || true)" + if echo "${machine}" | grep -qiE "x86|386|x86-64"; then + die "x86 machine type found in ${path} — cross-compilation broken!" + fi + fi +} + +check_arch "${LIB}" "libndpi" + +# Headers +NDPI_H="${BUILD_DIR}/src/include/ndpi_api.h" +if [[ -f "${NDPI_H}" ]]; then + ok "Header present: ${NDPI_H}" +fi + +# Full mode: also check ndpiReader and optionally run it via QEMU +READER="" +if [[ "$MODE" == "full" ]]; then + READER="$(find "${BUILD_DIR}/example" -name 'ndpiReader' 2>/dev/null | head -1 || true)" + if [[ -n "${READER}" ]]; then + check_arch "${READER}" "ndpiReader" + else + warn "ndpiReader not found — check configure output for missing dependencies." + fi + + # Unit tests binary (if built) + UNIT="$(find "${BUILD_DIR}/tests/unit" -name 'ndpi_unit_test' 2>/dev/null | head -1 || true)" + if [[ -n "${UNIT}" ]]; then + check_arch "${UNIT}" "ndpi_unit_test" + fi +fi + +# --------------------------------------------------------------------------- +# QEMU smoke test: run ndpiReader -H on the host via user-mode emulation +# +# qemu-aarch64-static executes AArch64 ELFs on the x86-64 kernel using two +# independent mechanisms: +# +# QEMU_LD_PREFIX=/ +# Tells QEMU to prepend '/' to the ELF interpreter path embedded in the +# binary (/lib/ld-linux-aarch64.so.1). With Ubuntu multiarch that file +# exists at /lib/aarch64-linux-gnu/ld-linux-aarch64.so.1, installed as a +# dependency of libc6:arm64 (pulled in by the cross-compiled dev libs). +# +# LD_LIBRARY_PATH= +# Passed through QEMU to the AArch64 dynamic linker so it can find +# libndpi.so in the build directory (it is not installed system-wide). +# --------------------------------------------------------------------------- +if [[ -n "${READER}" ]]; then + sep + info "QEMU smoke test: ndpiReader -H" + + QEMU_BIN="" + if command -v qemu-aarch64-static &>/dev/null; then + QEMU_BIN="qemu-aarch64-static" + else + info "qemu-user-static not installed; installing now..." + sudo apt-get install -y --no-install-recommends qemu-user-static + QEMU_BIN="qemu-aarch64-static" + fi + + # Verify the aarch64 dynamic linker is accessible (installed via libc6:arm64 + # which is pulled in as a dependency of the cross-compiled dev libs above). + AARCH64_LD="/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1" + if [[ ! -f "${AARCH64_LD}" ]]; then + warn "AArch64 dynamic linker not found at ${AARCH64_LD}." + warn "Install libc6:arm64 to enable QEMU execution." + else + ok "AArch64 dynamic linker: ${AARCH64_LD}" + + # LD_LIBRARY_PATH points QEMU at the cross-compiled shared libndpi.so + # so ndpiReader can find it at runtime. + READER_LIB_DIR="$(dirname "${LIB_SO:-}")" + + echo + info "Running: QEMU_LD_PREFIX=/ LD_LIBRARY_PATH=${READER_LIB_DIR} ${QEMU_BIN} ${READER} -H" + echo "----------------------------------------------------------------------" + QEMU_LD_PREFIX=/ LD_LIBRARY_PATH="${READER_LIB_DIR:-.}" \ + "${QEMU_BIN}" "${READER}" -H 2>&1 || true + echo "----------------------------------------------------------------------" + ok "ndpiReader -H executed successfully under QEMU." + fi +fi + +# --------------------------------------------------------------------------- +# Summary +# --------------------------------------------------------------------------- +sep +ok "Cross-compilation test PASSED" +echo +echo " Mode : ${MODE}" +echo " Target triple : ${TARGET_TRIPLE}" +echo " Library : ${LIB}" +echo " Build dir : ${BUILD_DIR}" +echo +echo "To inspect the library manually:" +echo " ${TARGET_TRIPLE}-readelf -h '${LIB}'" +echo " ${TARGET_TRIPLE}-nm '${LIB}' | head -20" +echo +if [[ -n "${READER}" ]]; then +echo "To run the binary again:" +echo " QEMU_LD_PREFIX=/ LD_LIBRARY_PATH='${READER_LIB_DIR:-${BUILD_DIR}/src/lib}' qemu-aarch64-static '${READER}' -H" +echo +fi