separateDebugInfo: allow any reasonable length of build ID#146275
separateDebugInfo: allow any reasonable length of build ID#146275oxalica wants to merge 1 commit intoNixOS:stagingfrom
Conversation
LLVM stdenv produces 16byte build ID, which is different than GCC's 40byte one. They both work well in gdb. Currently we create a directory layer for first 2byte, so any length greater or equal than 3 should be fine.
|
@edolstra @Ericson2314 Can one of you review this change? We're carrying a large-ish workaround in firefox to work around this issue. |
vcunat
left a comment
There was a problem hiding this comment.
Is this syntax right? My bash (ran by hand) does not agree.
bash: 3: No such file or directory
With using -le this sounds OK. It's unclear to me why originally the condition was written this way (the commit ec5b66e does not indicate), but perhaps there really wasn't any reason beyond what's mentioned here.
|
I don't think the rest of our infrastructure ( |
|
BTW, 64 bits seems rather insufficient as a unique identifier... I wonder why LLVM went with such short IDs? Given the birthday paradox, this means we'll only need a few billion binaries (hardly unimaginable given the size of the binary cache) until we can expect collisions. |
|
I don't expect that it's so bad. Existence of a collision shouldn't cause issues by itself, if the code is written reasonably. Probability of a collision for a hash that you need should still be fairly low, I think. |
The hash is used as a key to look up debug info, so a collision means that we get the wrong debug info. |
|
Yes, of course. But existence of a collision in a huge repo only breaks info on (some of) the colliding files, nowhere else. There will still be very very few of the collisions. |
| # 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 | ||
| if [ "${#id}" < 3 ]; then |
There was a problem hiding this comment.
| if [ "${#id}" < 3 ]; then | |
| if [ "${#id}" -lt 16 ]; then |
if llvm produces 16 bytes then we should use that as a lower limit. Otherwise collisions will happen all the time.
There was a problem hiding this comment.
< is a lexicographical compare. You probably want -lt for numeric comparison: if [ "${#id}" -lt 3 ]; then
|
I'd like to test this, how can I get an affected llvm stdenv ? I tried compiling sl with llvmPackages.stdenv but it still has long build ids, so presumably it's still using a partly gnu toolchain. |
|
Perhaps not a solution but: you can get [nix-shell:~]$ nix-shell --expr "with (import <nixpkgs> {}); (mkShell.override { stdenv = pkgsLLVM.stdenv; }) {}" --run '$CC -Wl,--build-id=fast -xc - <<<"int main() { }" -o test; $READELF -n test | grep Build'
Build ID: 8903a1c09635bcb6
[nix-shell:~]$ nix-shell --expr "with (import <nixpkgs> {}); (mkShell.override { stdenv = pkgsLLVM.stdenv; }) {}" --run '$CC -Wl,--build-id=md5 -xc - <<<"int main() { }" -o test; $READELF -n test | grep Build'
Build ID: f10ec65cd2789197314b9025851c6b18
[nix-shell:~]$ nix-shell --expr "with (import <nixpkgs> {}); (mkShell.override { stdenv = pkgsLLVM.stdenv; }) {}" --run '$CC -Wl,--build-id=sha1 -xc - <<<"int main() { }" -o test; $READELF -n test | grep Build'
Build ID: f0793470c0198f9f167186a1a0e15b7c60cb2e68
[nix-shell:~]$ nix-shell --expr "with (import <nixpkgs> {}); (mkShell.override { stdenv = pkgsLLVM.stdenv; }) {}" --run '$CC -Wl,--build-id=uuid -xc - <<<"int main() { }" -o test; $READELF -n test | grep Build'
Build ID: 8a00108ec7d22857a47b23eba1f481ee(I haven't done any benchmarking but I believe Maybe we can change this: nixpkgs/pkgs/build-support/bintools-wrapper/ld-wrapper.sh Lines 233 to 237 in 1a27624 to use
|
|
(thanks to @rrbutani I could obtain such an executable with short build id as follows: with import ./. { };
(sl.override { stdenv = pkgsLLVM.stdenv; }).overrideAttrs (old:
{
separateDebugInfo = true;
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs/build-support/setup-hooks/separate-debug-info.sh ];
})) About tooling: nixseparatedebuginfod can handle such short buildids. About collisions: I agree with @vcunat that even if hydra may one day contain collisions, the probability that the debuginfo you need is affected is quite low. To give concrete numbers I have currently 18618 distinct build ids on my system, and if I'm doing the math right, the probability that I'm affected if I asked nixseparatedebuginfod to download the debuginfo for them all is 1-product(1-k/(2^64), k, 1, 18618) so 9.39592755809e-12. This is low enough for me. For this reason we can accept such short build ids. But it would be nice if we can have lld produce the same length of build ids as ld as @rrbutani suggests is possible. Concretely I am in favor of this change, modulo the bash syntax for comparison, and the fact that we still disable separatedebuginfo for the llvm stdenv ( , discovered by @rrbutani ) so except for the case of firefox which calls clang itself we kind of never get those buildids.We can change the kind of buildids lld produces later if deemed desirable. |
|
I personally prefer the alternative approach #218301 to make |
#146275 has more discussion on this; the abridged version is that `lld` defaults to using `--build-id=fast` while GNU `ld` defaults to `--build-id=sha1`. These differ in length and so `separate-debug-info.sh`, as of this writing, errors on `lld`'s shorter `--build-id=fast`-generated hashes. `lld` offers the following `build-id` styles: - UUID (random; fast but bad for reproducibility) - fast (xxhash; fast but shorter hashes) - user provided hexstring - SHA1 - MD5 GNU `ld` supports the latter three options, `mold` supports all of these plus SHA256. UUID is out because it's not reproducible, fast isn't supported by GNU `ld` Using a nix provided (sourced from the output base hash) hash as the `build-id` seems tempting but would require a little extra work (we have to include some characteristic of the binary being hashed so that binaries within a derivation still have unique hashes; it seems easy to get this wrong; i.e. a path based approach would make two otherwise identical binaries that reside at different paths have different `build-id` hashes) That leaves SHA1 and MD5 and GNU `ld` already defaults to the former. This commit adds `$NIX_BUILD_ID_STYLE` as an escape hatch, in case any packages have strong opinions about which hash to use. ---- Note that if/when #146275 goes through, this change can be reverted if linker speed is a priority.
This was disabled here: b86e62d#diff-282a02cc3871874f16401347d8fadc90d59d7ab11f6a99eaa5173c3867e1a160 h/t to @teh: b86e62d#commitcomment-77916294 for pointing out that the failure that @matthewbauer was seeing was caused by the `separate-debug-info.sh` `build-id` length requirement that #146275 will relax `lld` has had `--build-id` support dating back to LLVM4: https://reviews.llvm.org/D18091 This predates every `llvmPackages_` version currently in nixpkgs (and certainly every version actually still used in `useLLVM` stdenvs) so with the previous commit (asking `ld` for sufficiently long SHA1 hashes) I think we can safely enable `separateDebugInfo` when using LLVM bintools.
Motivation for this change
LLVM stdenv produces 16byte build ID, which is different than GCC's 40byte one. They both work well in gdb. Currently we create a directory layer for first 2byte, so any length greater or equal than 3 should be fine.
I'm working on keeping debug info for
firefox, and ran into this issue. This PR should unblock that attempt.Tested:
glibcbuilt and produceddebugoutput properly.Things done
sandbox = trueset innix.conf? (See Nix manual)nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage./result/bin/)