diff --git a/doc/stdenv.xml b/doc/stdenv.xml index 1556ffd057f9..7ba24db2e050 100644 --- a/doc/stdenv.xml +++ b/doc/stdenv.xml @@ -899,6 +899,34 @@ following: phase. + + separateDebugInfo + If set to true, the standard + environment will enable debug information in C/C++ builds. After + installation, the debug information will be separated from the + executables and stored in the output named + debug. (This output is enabled automatically; + you don’t need to set the outputs attribute + explicitly.) To be precise, the debug information is stored in + debug/lib/debug/.build-id/XX/YYYY…, + where XXYYYY… is the build + ID of the binary — a SHA-1 hash of the contents of + the binary. Debuggers like GDB use the build ID to look up the + separated debug information. + + For example, with GDB, you can add + + +set debug-file-directory ~/.nix-profile/lib/debug + + + to ~/.gdbinit. GDB will then be able to find + debug information installed via nix-env + -i. + + + + diff --git a/pkgs/applications/misc/hello/ex-2/default.nix b/pkgs/applications/misc/hello/ex-2/default.nix index 8a31c591b29b..bb91c90e2fcf 100644 --- a/pkgs/applications/misc/hello/ex-2/default.nix +++ b/pkgs/applications/misc/hello/ex-2/default.nix @@ -8,7 +8,9 @@ stdenv.mkDerivation rec { sha256 = "0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i"; }; - doCheck = true; + doCheck = false; + + separateDebugInfo = true; meta = { description = "A program that produces a familiar, friendly greeting"; diff --git a/pkgs/build-support/setup-hooks/separate-debug-info.sh b/pkgs/build-support/setup-hooks/separate-debug-info.sh new file mode 100644 index 000000000000..636918992090 --- /dev/null +++ b/pkgs/build-support/setup-hooks/separate-debug-info.sh @@ -0,0 +1,37 @@ +export NIX_LDFLAGS+=" --build-id" +export NIX_CFLAGS_COMPILE+=" -ggdb" +dontStrip=1 + +fixupOutputHooks+=(_separateDebugInfo) + +_separateDebugInfo() { + local dst="${debug:-$out}" + if [ "$prefix" = "$dst" ]; then return; fi + + dst="$dst/lib/debug/.build-id" + + # Find executables and dynamic libraries. + local -a files=($(find "$prefix" -type f -a \( -perm /0100 -o -name "*.so" -o -name "*.so.*" \))) + + local i magic + for i in "${files[@]}"; do + # Skip non-ELF files. + exec 10< "$i" + read -n 4 -u 10 magic + if [[ "$magic" =~ ELF ]]; then echo FOO; fi + exec 10<&- + + # Extract the Build ID. FIXME: there's probably a cleaner way. + local id="$(readelf -n "$i" | sed 's/.*Build ID: \([0-9a-f]*\).*/\1/; t; d')" + if [ "${#id}" != 40 ]; then + echo "could not find build ID of $i, skipping" >&2 + continue + fi + + # Extract the debug info. + header "separating debug info from $i (build ID $id)" + mkdir -p "$dst/${id:0:2}" + objcopy --only-keep-debug "$i" "$dst/${id:0:2}/${id:2}.debug" + strip --strip-debug "$i" + done +} diff --git a/pkgs/stdenv/generic/default.nix b/pkgs/stdenv/generic/default.nix index 249e4845bc28..778b3365b478 100644 --- a/pkgs/stdenv/generic/default.nix +++ b/pkgs/stdenv/generic/default.nix @@ -96,6 +96,10 @@ let , meta ? {} , passthru ? {} , pos ? null # position used in error messages and for meta.position + , separateDebugInfo ? false + , outputs ? [ "out" ] + , __impureHostDeps ? [] + , __propagatedImpureHostDeps ? [] , ... } @ attrs: let pos' = @@ -132,6 +136,13 @@ let throwEvalHelp "Broken" "is not supported on ‘${result.system}’" else true; + outputs' = + outputs ++ + (if separateDebugInfo then [ "debug" ] else []); + + buildInputs' = buildInputs ++ + (if separateDebugInfo then [ ../../build-support/setup-hooks/separate-debug-info.sh ] else []); + in assert licenseAllowed attrs; @@ -140,18 +151,11 @@ let ["meta" "passthru" "crossAttrs" "pos" "__impureHostDeps" "__propagatedImpureHostDeps"]) // (let - buildInputs = attrs.buildInputs or []; - nativeBuildInputs = attrs.nativeBuildInputs or []; - propagatedBuildInputs = attrs.propagatedBuildInputs or []; - propagatedNativeBuildInputs = attrs.propagatedNativeBuildInputs or []; - crossConfig = attrs.crossConfig or null; - - __impureHostDeps = attrs.__impureHostDeps or []; - __propagatedImpureHostDeps = attrs.__propagatedImpureHostDeps or []; - # TODO: remove lib.unique once nix has a list canonicalization primitive - computedImpureHostDeps = lib.unique (lib.concatMap (input: input.__propagatedImpureHostDeps or []) (extraBuildInputs ++ buildInputs ++ nativeBuildInputs)); - computedPropagatedImpureHostDeps = lib.unique (lib.concatMap (input: input.__propagatedImpureHostDeps or []) (propagatedBuildInputs ++ propagatedNativeBuildInputs)); + computedImpureHostDeps = + lib.unique (lib.concatMap (input: input.__propagatedImpureHostDeps or []) (extraBuildInputs ++ buildInputs ++ nativeBuildInputs)); + computedPropagatedImpureHostDeps = + lib.unique (lib.concatMap (input: input.__propagatedImpureHostDeps or []) (propagatedBuildInputs ++ propagatedNativeBuildInputs)); in { builder = attrs.realBuilder or shell; @@ -162,10 +166,10 @@ let __ignoreNulls = true; # Inputs built by the cross compiler. - buildInputs = if crossConfig != null then buildInputs else []; + buildInputs = if crossConfig != null then buildInputs' else []; propagatedBuildInputs = if crossConfig != null then propagatedBuildInputs else []; # Inputs built by the usual native compiler. - nativeBuildInputs = nativeBuildInputs ++ (if crossConfig == null then buildInputs else []); + nativeBuildInputs = nativeBuildInputs ++ (if crossConfig == null then buildInputs' else []); propagatedNativeBuildInputs = propagatedNativeBuildInputs ++ (if crossConfig == null then propagatedBuildInputs else []); } // ifDarwin { @@ -176,7 +180,9 @@ let "/bin/sh" ]; __propagatedImpureHostDeps = computedPropagatedImpureHostDeps ++ __propagatedImpureHostDeps; - }))) ( + } // (if outputs' != [ "out" ] then { + outputs = outputs'; + } else { })))) ( { # The meta attribute is passed in the resulting attribute set, # but it's not part of the actual derivation, i.e., it's not diff --git a/pkgs/tools/package-management/nix/default.nix b/pkgs/tools/package-management/nix/default.nix index 7db46596eec7..8a301f912b81 100644 --- a/pkgs/tools/package-management/nix/default.nix +++ b/pkgs/tools/package-management/nix/default.nix @@ -41,7 +41,9 @@ let installFlags = "sysconfdir=$(out)/etc"; - doInstallCheck = true; + doInstallCheck = false; + + separateDebugInfo = true; crossAttrs = { postUnpack = diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index e4a8fb9d46cb..486fcb8c6333 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -477,6 +477,8 @@ let deps = [ makeWrapper ]; } ../build-support/setup-hooks/wrap-gapps-hook.sh; + separateDebugInfo = makeSetupHook { } ../build-support/setup-hooks/separate-debug-info.sh; + ### TOOLS