Compare commits

..

10 Commits

Author SHA1 Message Date
John Ericson
022caabb5f Merge pull request #185612 from obsidiansystems/buildRustCrate-21.05-no-choke-on-dep
[Backport release 21.05] buildRustCrate: don't try to set CARGO_FEATURE_ variables for dep: fe…
2022-08-07 19:21:33 -04:00
Faye Duxovni
d5baefeb34 buildRustCrate: don't try to set CARGO_FEATURE_ variables for dep: features
These features are internal-only, have special characters that bash
doesn't support in variable names, and aren't normally given
environment variables by cargo as far as I can tell.

(cherry picked from commit ede639a8d6)
(cherry picked from commit a94a643de9)
2022-08-07 19:11:15 -04:00
John Ericson
7ca652795e Merge pull request #184824 from obsidiansystems/backport-21.05
[backport release-21.05] Two recent backports from 21.11
2022-08-02 10:40:23 -04:00
John Ericson
15f7c3baa8 buildRustCrate: Add support for standard library deps
We are replicating one mechanism behind `-Z build-std`.

There isn't yet crate2nix support for this, but one can (and I do) add
the missing stdlib deps (for this feature to pick up) with overrides.

(cherry picked from commit cc29693a09)
(cherry picked from commit 5aef865cef)
(cherry picked from commit c19d19615c)
2022-08-02 09:11:47 -04:00
John Ericson
84d01504b2 compiler-rt: Fix "bare metal" case boolean logic
It is possible to both be bare metal and have a libc (newlib).

This libc doesn't provide very much --- not enough for CMake to think
the C toolchain works. We therefore adjust our logic so we hit the "bare
metal" case with or without libc.

The "use LLVM" bootstrap is intentionally not affected.

(cherry picked from commit bf39e32272)
(cherry picked from commit c820cd8cee)
(cherry picked from commit 0a284ae9b9)
2022-08-02 09:06:27 -04:00
John Ericson
7618fa361a Merge pull request #182180 from obsidiansystems/rust-platform-respect-fields-21.05
[Backport release-21.06] build-support/rust/lib: make arch and os functions respect target JSON
2022-07-20 11:05:54 -04:00
John Ericson
bb97a48cea build-support/rust/lib: make arch and os functions respect target JSON
(cherry picked from commit 39811b1da9)
(cherry picked from commit b49c1ce29f)
(cherry picked from commit 38d5ec716a)
2022-07-20 02:48:33 -04:00
Noah Fontes
046ee4af7a autoPatchelfHook: fix packages that use stdenvNoCC
autoPatchelfHook actually doesn't depend on stdenv and only needs
bintools (with its wrapper). This change uses $NIX_BINTOOLS instead of
$NIX_CC and makes the dependency on bintools explicit.

(cherry picked from commit a7f5e8321e)

Conflicts:
  pkgs/top-level/all-packages.nix
2022-06-12 13:42:59 +02:00
Noah Fontes
c96de39707 autoPatchelfHook: improve arch/ABI compatibility
Fully enabling crossSystem support for autoPatchelfHook came with some
perhaps unintended consequences of being a bit more aggressive about
patching ELF files from architectures/ABIs that differ from the target
(previously, those files would be ignored because ldd usually couldn't
handle them).

This change adds architecture and rough OS ABI detection to the script
so that it doesn't try to blindly replace the interpreter of files that
can't possibly use that interpreter, and also makes sure it doesn't
accidentally use libraries of other architectures/ABIs.

(cherry picked from commit 4765a3e153)
2022-06-12 13:42:59 +02:00
Noah Fontes
ff01aca26f autoPatchelfHook: fix detection under crossSystem
In #84415, autoPatchelfHook was taught to use the correct path to the
readelf binary when a crossSystem is specified. Unfortunately, the
remainder of the functionality in the script depended on ldd, which only
reads ELF files of its own architecture. It has the further unfortunate
quality of not reporting any useful error, but rather that the file is
not a dynamic executable.

This change uses patchelf to directly analyze the DT_NEEDED tags in the
target files instead, which correctly works across architectures. It
also updates the use of objdump to be prefix-aware $OBJDUMP (which would
have been required in the PR mentioned above, but we never made it that
far into the script execution).

(cherry picked from commit b79483d2b7)
2022-06-12 13:42:59 +02:00
7 changed files with 183 additions and 44 deletions

View File

@@ -1,4 +1,8 @@
{ lib, stdenv, mkRustcDepArgs, mkRustcFeatureArgs, rust }:
{ lib, stdenv
, mkRustcDepArgs, mkRustcFeatureArgs, needUnstableCLI
, rust
}:
{ crateName,
dependencies,
crateFeatures, crateRenames, libName, release, libPath,
@@ -18,6 +22,8 @@
(mkRustcFeatureArgs crateFeatures)
] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
"--target" (rust.toRustTargetSpec stdenv.hostPlatform)
] ++ lib.optionals (needUnstableCLI dependencies) [
"-Z" "unstable-options"
] ++ extraRustcOpts
# since rustc 1.42 the "proc_macro" crate is part of the default crate prelude
# https://github.com/rust-lang/cargo/commit/4d64eb99a4#diff-7f98585dbf9d30aa100c8318e2c77e79R1021-R1022

View File

@@ -34,15 +34,25 @@ let
)
else
extern;
in (if lib.any (x: x == "lib" || x == "rlib") dep.crateType then
" --extern ${name}=${dep.lib}/lib/lib${extern}-${dep.metadata}.rlib"
else
" --extern ${name}=${dep.lib}/lib/lib${extern}-${dep.metadata}${stdenv.hostPlatform.extensions.sharedLibrary}")
opts = lib.optionalString (dep.stdlib or false) "noprelude:";
filename = if lib.any (x: x == "lib" || x == "rlib") dep.crateType
then "${dep.metadata}.rlib"
else "${dep.metadata}${stdenv.hostPlatform.extensions.sharedLibrary}";
in " --extern ${opts}${name}=${dep.lib}/lib/lib${extern}-${filename}"
) dependencies;
# Create feature arguments for rustc.
mkRustcFeatureArgs = lib.concatMapStringsSep " " (f: ''--cfg feature=\"${f}\"'');
# Whether we need to use unstable command line flags
#
# Currently just needed for standard library dependencies, which have a
# special "noprelude:" modifier. If in later versions of Rust this is
# stabilized we can account for that here, too, so we don't opt into
# instability unnecessarily.
needUnstableCLI = dependencies:
lib.any (dep: dep.stdlib or false) dependencies;
inherit (import ./log.nix { inherit lib; }) noisily echo_colored;
configureCrate = import ./configure-crate.nix {
@@ -50,7 +60,7 @@ let
};
buildCrate = import ./build-crate.nix {
inherit lib stdenv mkRustcDepArgs mkRustcFeatureArgs rust;
inherit lib stdenv mkRustcDepArgs mkRustcFeatureArgs needUnstableCLI rust;
};
installCrate = import ./install-crate.nix { inherit stdenv; };
@@ -247,9 +257,14 @@ stdenv.mkDerivation (rec {
# Create a list of features that are enabled by the crate itself and
# through the features argument of buildRustCrate. Exclude features
# with a forward slash, since they are passed through to dependencies.
# with a forward slash, since they are passed through to dependencies,
# and dep: features, since they're internal-only and do nothing except
# enable optional dependencies.
crateFeatures = lib.optionals (crate ? features)
(builtins.filter (f: !lib.hasInfix "/" f) (crate.features ++ features));
(builtins.filter
(f: !(lib.hasInfix "/" f || lib.hasPrefix "dep:" f))
(crate.features ++ features)
);
libName = if crate ? libName then crate.libName else crate.crateName;
libPath = if crate ? libPath then crate.libPath else "";

View File

@@ -3,12 +3,14 @@
rec {
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
toTargetArch = platform:
if platform.isAarch32 then "arm"
/**/ if platform ? rustc.platform then platform.rustc.platform.arch
else if platform.isAarch32 then "arm"
else platform.parsed.cpu.name;
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
toTargetOs = platform:
if platform.isDarwin then "macos"
/**/ if platform ? rustc.platform then platform.rustc.platform.os or "none"
else if platform.isDarwin then "macos"
else platform.parsed.kernel.name;
# Returns the name of the rust target, even if it is custom. Adjustments are
@@ -31,7 +33,7 @@ rec {
# Returns the name of the rust target if it is standard, or the json file
# containing the custom target spec.
toRustTargetSpec = platform:
if (platform.rustc or {}) ? platform
if platform ? rustc.platform
then builtins.toFile (toRustTarget platform + ".json") (builtins.toJSON platform.rustc.platform)
else toRustTarget platform;
}

View File

@@ -37,7 +37,6 @@ isExecutable() {
declare -Ag autoPatchelfCachedDepsAssoc
declare -ag autoPatchelfCachedDeps
addToDepCache() {
if [[ ${autoPatchelfCachedDepsAssoc[$1]+f} ]]; then return; fi
@@ -53,25 +52,113 @@ declare -gi depCacheInitialised=0
declare -gi doneRecursiveSearch=0
declare -g foundDependency
getDepsFromSo() {
ldd "$1" 2> /dev/null | sed -n -e 's/[^=]*=> *\(.\+\) \+([^)]*)$/\1/p'
getDepsFromElfBinary() {
# NOTE: This does not use runPatchelf because it may encounter non-ELF
# files. Caller is expected to check the return code if needed.
patchelf --print-needed "$1" 2> /dev/null
}
getRpathFromElfBinary() {
# NOTE: This does not use runPatchelf because it may encounter non-ELF
# files. Caller is expected to check the return code if needed.
local rpath
IFS=':' read -ra rpath < <(patchelf --print-rpath "$1" 2> /dev/null) || return $?
printf "%s\n" "${rpath[@]}"
}
populateCacheForDep() {
local so="$1"
local rpath found
rpath="$(getRpathFromElfBinary "$so")" || return 1
for found in $(getDepsFromElfBinary "$so"); do
local rpathElem
for rpathElem in $rpath; do
# Ignore empty element or $ORIGIN magic variable which should be
# deterministically resolved by adding this package's library
# files early anyway.
#
# shellcheck disable=SC2016
# (Expressions don't expand in single quotes, use double quotes for
# that.)
if [[ -z "$rpathElem" || "$rpathElem" == *'$ORIGIN'* ]]; then
continue
fi
local soname="${found%.so*}"
local foundso=
for foundso in "$rpathElem/$soname".so*; do
addToDepCache "$foundso"
done
# Found in this element of the rpath, no need to check others.
if [ -n "$foundso" ]; then
break
fi
done
done
# Not found in any rpath element.
return 1
}
populateCacheWithRecursiveDeps() {
local so found foundso
for so in "${autoPatchelfCachedDeps[@]}"; do
for found in $(getDepsFromSo "$so"); do
local base="${found##*/}"
local soname="${base%.so*}"
for foundso in "${found%/*}/$soname".so*; do
addToDepCache "$foundso"
done
done
# Dependencies may add more to the end of this array, so we use a counter
# with while instead of a regular for loop here.
local -i i=0
while [ $i -lt ${#autoPatchelfCachedDeps[@]} ]; do
populateCacheForDep "${autoPatchelfCachedDeps[$i]}"
i=$i+1
done
}
getSoArch() {
objdump -f "$1" | sed -ne 's/^architecture: *\([^,]\+\).*/\1/p'
getBinArch() {
$OBJDUMP -f "$1" 2> /dev/null | sed -ne 's/^architecture: *\([^,]\+\).*/\1/p'
}
# Returns the specific OS ABI for an ELF file in the format produced by
# readelf(1), like "UNIX - System V" or "UNIX - GNU".
getBinOsabi() {
$READELF -h "$1" 2> /dev/null | sed -ne 's/^[ \t]*OS\/ABI:[ \t]*\(.*\)/\1/p'
}
# Tests whether two OS ABIs are compatible, taking into account the generally
# accepted compatibility of SVR4 ABI with other ABIs.
areBinOsabisCompatible() {
local wanted="$1"
local got="$2"
if [[ -z "$wanted" || -z "$got" ]]; then
# One of the types couldn't be detected, so as a fallback we'll assume
# they're compatible.
return 0
fi
# Generally speaking, the base ABI (0x00), which is represented by
# readelf(1) as "UNIX - System V", indicates broad compatibility with other
# ABIs.
#
# TODO: This isn't always true. For example, some OSes embed ABI
# compatibility into SHT_NOTE sections like .note.tag and .note.ABI-tag.
# It would be prudent to add these to the detection logic to produce better
# ABI information.
if [[ "$wanted" == "UNIX - System V" ]]; then
return 0
fi
# Similarly here, we should be able to link against a superset of features,
# so even if the target has another ABI, this should be fine.
if [[ "$got" == "UNIX - System V" ]]; then
return 0
fi
# Otherwise, we simply return whether the ABIs are identical.
if [[ "$wanted" == "$got" ]]; then
return 0
fi
return 1
}
# NOTE: If you want to use this function outside of the autoPatchelf function,
@@ -82,6 +169,7 @@ getSoArch() {
findDependency() {
local filename="$1"
local arch="$2"
local osabi="$3"
local lib dep
if [ $depCacheInitialised -eq 0 ]; then
@@ -93,7 +181,7 @@ findDependency() {
for dep in "${autoPatchelfCachedDeps[@]}"; do
if [ "$filename" = "${dep##*/}" ]; then
if [ "$(getSoArch "$dep")" = "$arch" ]; then
if [ "$(getBinArch "$dep")" = "$arch" ] && areBinOsabisCompatible "$osabi" "$(getBinOsabi "$dep")"; then
foundDependency="$dep"
return 0
fi
@@ -117,7 +205,24 @@ autoPatchelfFile() {
local dep rpath="" toPatch="$1"
local interpreter
interpreter="$(< "$NIX_CC/nix-support/dynamic-linker")"
interpreter="$(< "$NIX_BINTOOLS/nix-support/dynamic-linker")"
local interpreterArch interpreterOsabi toPatchArch toPatchOsabi
interpreterArch="$(getBinArch "$interpreter")"
interpreterOsabi="$(getBinOsabi "$interpreter")"
toPatchArch="$(getBinArch "$toPatch")"
toPatchOsabi="$(getBinOsabi "$toPatch")"
if [ "$interpreterArch" != "$toPatchArch" ]; then
# Our target architecture is different than this file's architecture,
# so skip it.
echo "skipping $toPatch because its architecture ($toPatchArch) differs from target ($interpreterArch)" >&2
return 0
elif ! areBinOsabisCompatible "$interpreterOsabi" "$toPatchOsabi"; then
echo "skipping $toPatch because its OS ABI ($toPatchOsabi) is not compatible with target ($interpreterOsabi)" >&2
return 0
fi
if isExecutable "$toPatch"; then
runPatchelf --set-interpreter "$interpreter" "$toPatch"
# shellcheck disable=SC2154
@@ -129,27 +234,34 @@ autoPatchelfFile() {
fi
fi
local libcLib
libcLib="$(< "$NIX_BINTOOLS/nix-support/orig-libc")/lib"
echo "searching for dependencies of $toPatch" >&2
# We're going to find all dependencies based on ldd output, so we need to
# clear the RPATH first.
runPatchelf --remove-rpath "$toPatch"
# If the file is not a dynamic executable, ldd/sed will fail,
# in which case we return, since there is nothing left to do.
local missing
missing="$(
ldd "$toPatch" 2> /dev/null | \
sed -n -e 's/^[\t ]*\([^ ]\+\) => not found.*/\1/p'
)" || return 0
missing="$(getDepsFromElfBinary "$toPatch")" || return 0
# This ensures that we get the output of all missing dependencies instead
# of failing at the first one, because it's more useful when working on a
# new package where you don't yet know its dependencies.
for dep in $missing; do
if [[ "$dep" == /* ]]; then
# This is an absolute path. If it exists, just use it. Otherwise,
# we probably want this to produce an error when checked (because
# just updating the rpath won't satisfy it).
if [ -f "$dep" ]; then
continue
fi
elif [ -f "$libcLib/$dep" ]; then
# This library exists in libc, and will be correctly resolved by
# the linker.
continue
fi
echo -n " $dep -> " >&2
if findDependency "$dep" "$(getSoArch "$toPatch")"; then
if findDependency "$dep" "$toPatchArch" "$toPatchOsabi"; then
rpath="$rpath${rpath:+:}${foundDependency%/*}"
echo "found: $foundDependency" >&2
else
@@ -184,7 +296,7 @@ addAutoPatchelfSearchPath() {
done
while IFS= read -r -d '' file; do
addToDepCache "$file"
addToDepCache "$file"
done < <(find "$@" "${findOpts[@]}" \! -type d \
\( -name '*.so' -o -name '*.so.*' \) -print0)
}
@@ -220,10 +332,10 @@ autoPatchelf() {
segmentHeaders="$(LANG=C $READELF -l "$file")"
# Skip if the ELF file doesn't have segment headers (eg. object files).
# not using grep -q, because it can cause Broken pipe
[ -n "$(echo "$segmentHeaders" | grep '^Program Headers:')" ] || continue
grep -q '^Program Headers:' <<<"$segmentHeaders" || continue
if isExecutable "$file"; then
# Skip if the executable is statically linked.
[ -n "$(echo "$segmentHeaders" | grep "^ *INTERP\\>")" ] || continue
grep -q "^ *INTERP\\>" <<<"$segmentHeaders" || continue
fi
# Jump file if patchelf is unable to parse it
# Some programs contain binary blobs for testing,
@@ -255,6 +367,9 @@ autoPatchelf() {
# So what we do here is basically run in postFixup and emulate the same
# behaviour as fixupOutputHooks because the setup hook for patchelf is run in
# fixupOutput and the postFixup hook runs later.
#
# shellcheck disable=SC2016
# (Expressions don't expand in single quotes, use double quotes for that.)
postFixupHooks+=('
if [ -z "${dontAutoPatchelf-}" ]; then
autoPatchelf -- $(for output in $outputs; do

View File

@@ -30,7 +30,7 @@ stdenv.mkDerivation {
"-DCOMPILER_RT_BUILD_XRAY=OFF"
"-DCOMPILER_RT_BUILD_LIBFUZZER=OFF"
"-DCOMPILER_RT_BUILD_PROFILE=OFF"
] ++ lib.optionals ((useLLVM || bareMetal) && !haveLibc) [
] ++ lib.optionals ((useLLVM && !haveLibc) || bareMetal) [
"-DCMAKE_C_COMPILER_WORKS=ON"
"-DCMAKE_CXX_COMPILER_WORKS=ON"
"-DCOMPILER_RT_BAREMETAL_BUILD=ON"

View File

@@ -32,7 +32,7 @@ stdenv.mkDerivation {
"-DCOMPILER_RT_BUILD_XRAY=OFF"
"-DCOMPILER_RT_BUILD_LIBFUZZER=OFF"
"-DCOMPILER_RT_BUILD_PROFILE=OFF"
] ++ lib.optionals ((useLLVM || bareMetal) && !haveLibc) [
] ++ lib.optionals ((useLLVM && !haveLibc) || bareMetal) [
"-DCMAKE_C_COMPILER_WORKS=ON"
"-DCMAKE_CXX_COMPILER_WORKS=ON"
"-DCOMPILER_RT_BAREMETAL_BUILD=ON"

View File

@@ -169,7 +169,8 @@ in
autoconf = autoconf269;
};
autoPatchelfHook = makeSetupHook { name = "auto-patchelf-hook"; }
autoPatchelfHook = makeSetupHook
{ name = "auto-patchelf-hook"; deps = [ bintools ]; }
../build-support/setup-hooks/auto-patchelf.sh;
appimageTools = callPackage ../build-support/appimage {