diff --git a/CMakeLists.txt b/CMakeLists.txt index 1310d09c..8576f663 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,16 @@ endif() set(CMAKE_CXX_STANDARD 26) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# Every string the build bakes in (asserts, log prefixes, __FILE__) has to name a path that exists in +# the package, not on the machine that built it: the shipped binaries are gated on it +# (scripts/pack-elf.sh, pack_artifact_hygiene). -ffile-prefix-map covers -fmacro-prefix-map and +# -fdebug-prefix-map on GCC and clang alike; ONEBIT_PATH_MAPS is also handed to the externals below. +set(ONEBIT_PATH_MAP_FLAGS + "-ffile-prefix-map=${CMAKE_SOURCE_DIR}=." + "-ffile-prefix-map=${CMAKE_BINARY_DIR}=build" + CACHE STRING "Compiler flags that rewrite build-machine paths out of embedded strings") +add_compile_options(${ONEBIT_PATH_MAP_FLAGS}) + # The public binary drop (docs/releases.md): prebuilt proprietary parts for a source build outside # the org. Sets ONEBIT_BINARY_DROP_* and verifies the drop; a no-op when ONEBIT_BINARY_DROP is unset. include(cmake/binary_drop.cmake) diff --git a/cmake/hrx.cmake b/cmake/hrx.cmake index 3d399bf6..3e559d68 100644 --- a/cmake/hrx.cmake +++ b/cmake/hrx.cmake @@ -34,6 +34,14 @@ include(ExternalProject) set(ONEBIT_HRX_TOOLCHAIN "/opt/rocm-therock" CACHE PATH "ROCm/TheRock root: its amdclang builds llama.cpp and HRX") +# The HRX binaries carry strings from this tree and, with -DONEBIT_GPU_PRIVATE, from the add-on +# checkout; both are rewritten so the shipped libraries name no build-machine path. +set(ONEBIT_HRX_PATH_MAP_FLAGS "-ffile-prefix-map=${CMAKE_SOURCE_DIR}=." + CACHE STRING "Path mapping for the HRX llama.cpp build") +if(ONEBIT_GPU_PRIVATE) + string(APPEND ONEBIT_HRX_PATH_MAP_FLAGS " -ffile-prefix-map=${ONEBIT_GPU_PRIVATE}=addon") +endif() + # A source build outside the org has no private HIP kernels to compile, so it links the prebuilt # HRX tree from the public binary drop instead (docs/releases.md, "The public binary drop"): the # pinned llama-server and its libraries, libggml-hrx.so with those kernels compiled in. Nothing is @@ -132,6 +140,8 @@ ExternalProject_Add(llama_hrx -DHRX_SOURCE_DIR=${CMAKE_SOURCE_DIR}/third_party/hrx-system -DGGML_HRX=ON -DGGML_VULKAN=OFF -DGGML_CUDA=OFF -DGGML_HIP=OFF -DGGML_NATIVE=ON -DGGML_CPU=ON -DLLAMA_BUILD_SERVER=ON -DLLAMA_CURL=OFF + "-DCMAKE_C_FLAGS=${ONEBIT_HRX_PATH_MAP_FLAGS}" + "-DCMAKE_CXX_FLAGS=${ONEBIT_HRX_PATH_MAP_FLAGS}" ${_hrx_gpu_private_args} BUILD_COMMAND ${CMAKE_COMMAND} --build --target llama-server llama-bench INSTALL_COMMAND "" diff --git a/scripts/pack-elf.sh b/scripts/pack-elf.sh index 0a0ceab0..f067212e 100644 --- a/scripts/pack-elf.sh +++ b/scripts/pack-elf.sh @@ -89,10 +89,36 @@ pack_localize() { pack_strip() { local f while IFS= read -r -d '' f; do - if pack_is_elf "$f"; then strip --strip-unneeded "$f"; fi + if pack_is_elf "$f"; then + strip --strip-unneeded "$f" + # .comment names the compiler and its version; it is not needed to load or link + objcopy --remove-section=.comment --remove-section=.note.gnu.gold-version "$f" 2>/dev/null || true + fi done < <(find "$1" -type f -print0) } +# Everything a shipped program and library may not carry: a compiler identification section, a path +# into a build machine, or an absolute RUNPATH. Prints one line per violation, empty when clean. +pack_artifact_hygiene() { + local f out="" + while IFS= read -r -d '' f; do + pack_is_elf "$f" || continue + readelf -p .comment "$f" 2>/dev/null | grep -qE "clang version|GCC:" && out="$out +$f: .comment names the compiler" + strings -a "$f" 2>/dev/null | grep -qE "^/(home|Users|root)/[A-Za-z0-9_./-]+" && + out="$out +$f: embedded build-machine path" + # An absolute RUNPATH is only acceptable under the toolchain root: some shipped programs link the + # SDK's own libraries (whose closure is far larger than the package), and the release needs that + # toolchain to run anyway. + readelf -d "$f" 2>/dev/null | grep -E 'R(UN)?PATH' | + sed -n 's/.*Library \(runpath\|rpath\): \[\(.*\)\]/\2/p' | tr ':' '\n' | + grep -E "^/" | grep -vE "^(${ONEBIT_TOOLCHAIN_ROOT:-/opt/rocm-therock})/" | grep -q . && + out="$out\n$f: absolute RUNPATH outside the toolchain root" + done < <(find "$1" -type f -print0) + [ -z "$out" ] || printf "%b\n" "$out" +} + pack_build_rpaths() { local f find "$1" -type f -print0 | while IFS= read -r -d '' f; do diff --git a/scripts/package-linux.sh b/scripts/package-linux.sh index 0279ecbb..d66923cb 100755 --- a/scripts/package-linux.sh +++ b/scripts/package-linux.sh @@ -93,15 +93,20 @@ fi # (libomp.so, which ggml's CPU code links when TheRock's amdclang builds llama.cpp) is copied beside # it; RUNPATH entries outside the build tree (TheRock in /opt) stay, since HRX needs TheRock anyway. pack_localize "$stage" "$build" +# pack_localize gives every program and library $ORIGIN plus whatever SDK entries its link needed; the +# engine binary at the top of the tree is the one it does not visit (depth 1), so drop its rpath here. patchelf --remove-rpath "$stage/1bit" # symbol tables and debug info out of every program and library: the release ships compiled code # only (docs/releases.md, "Private components"); the dynamic symbols they link by stay pack_strip "$stage" -# nothing may still point into the build machine's home +# nothing may still point into the build machine's home, carry the compiler's identification, or load +# through an absolute RUNPATH bad=$(pack_build_rpaths "$stage" "$build") [ -z "$bad" ] || { echo "RPATH into the build machine left:"; echo "$bad"; exit 1; } +hygiene=$(pack_artifact_hygiene "$stage") +[ -z "$hygiene" ] || { echo "artifact hygiene:"; echo "$hygiene"; exit 1; } cp "$src/LICENSE" "$src/NOTICE" "$src/LICENSE-BINARY" "$src/NOTICE-BINARY" "$stage/" # the stock Lemonade PPA's lemond (Ubuntu 26.04) runs our HRX llama-server through lemonade-hrx