Repository navigation
Unreadable error messages on linking failure #46998
Description
Activity
- addedA-diagnosticsArea: Messages for errors, warnings, and lintsArea: Messages for errors, warnings, and lintsC-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.
on Dec 25, 2017 The problem here is that the error you're seeing is not coming from
rustc, but rather from shelling out and trying to runcc. We can improve this error in particular by indeed verifying that the linker we're trying to run is there and emitting a different error for it inlink.rsandwrite.rs, including the path information from the environment.As further improvement,
rustccould also probably scrape the output of the linker for known errors to minimize the output in those cases, and fall back to the current behavior (printing the entirety of the linkers output).- addedE-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
on Dec 25, 2017 There's two typical kinds of linking failure 1. the linker does not exist 2. the linker does exist but failed to link successfully.
In the former case we definitely should not print the linker command line because it's meaningless when we couldn't find a linker to begin with. Instead printing out some diagnostic information about where
rustctried to look for the linker would be more useful (in particular this would be very useful formsvcwhere finding the linker is actually really complicated).In the latter case the linker command line is important as it contains all the information needed to debug issues due to the linker failing to succeed. If you want to know what folders were searched for libraries by the linker you just look for the relevant
-Lor/LIBPATHflags. Oftentimes there is a lot of irrelevant junk in the command line but trying to filter it down is a really difficult challenge because sometimes the stuff that seems like junk contains some vital detail.@retep998 @estebank If this is an actual bug, I'd love to do this as my first contribution to the rust compiler. I'm not the best Rust developer, but I'm not exactly new to the language either. So my question would be:
- Can we agree that this is undesired behaviour and should be fixed?
- How would you think the output should look like (i.e. what should the error message tell)? I'd personally agree with @retep998:
- If the linker doesn't exist, the linker arguments are not printed, because it wouldn't make sense, instead the paths where the linker was searched for are printed in some way.
- If the linker exists, but failed to build, we need to somehow identify the library that could not be linked and the paths the library was searched for.
- I don't know exactly how the error handling in rustc works - are errors handled with abstractions (error_chain, failure, etc.) or does the compiler just panic / abort in place?
Does
rustcalready know the name of the library it's linking?Theoretically speaking, the strategy I'd think about: Ifrustcinvokes the linker with let's say-lcurlit has to give the string "curl" somehow to the linker right? So what the compiler could do is to track what the latest linker argument was - and if the linker throws an error, we parse the output, print the paths and exit.So for case 1:
error: could not exec the linker `cc`: linker not found (os error 2) | = note: this error originates in the crate "deflate" = note: searched for linker in the following paths: /usr/bin/ /usr/local/bin /usr/sbinand for case 2:
error: linking with `cc` failed: exit code: 1 --> deflate/build.rs:2:5 | 2 | println!("cargo:rustc-link-lib=asdfasdf"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: /usr/bin/ld: cannot find library "asdfasdf" = note: searched for library in the following paths: /usr/bin/ /usr/local/bin /usr/sbin = note: collect2: error: ld returned 1 exit statusTrying to tell which paths were searched for the library is going to be really hard because
rustcdoes not actually know all the paths where the linker searches for libraries. Environment variables and system configuration can determine additional directories where the linker will search whichrustcnever told the linker to search in, and this differs between different toolchains (msvc again being significantly different in this regard).There is one linker output that I know we can reliably parse and use to print out a much more informative message. If we're targeting
pc-windows-msvcand the linker output containslink: extra operandthen that is definitely not the linker we want but the coreutils commandlink, in which caserustcneeds to print out a very helpful message telling the user they need to install the VC++ build tools.I'd love to do this as my first contribution to the rust compiler. I'm not the best Rust developer, but I'm not exactly new to the language either.
That is great!
- Can we agree that this is undesired behaviour and should be fixed?
Yes, I believe we can improve significantly over the current output.
- How would you think the output should look like (i.e. what should the error message tell)? I'd personally agree with @retep998:
- If the linker doesn't exist, the linker arguments are not printed, because it wouldn't make sense, instead the paths where the linker was searched for are printed in some way.
I definitely believe this is the main fix for this issue.
- If the linker exists, but failed to build, we need to somehow identify the library that could not be linked and the paths the library was searched for.
We already output the linker's result, which should be enough but not great. Scraping the output in order to output the error in a consistent way as the rest of
rustcerrors would be the best bet, if not exhaustive.- I don't know exactly how the error handling in rustc works - are errors handled with abstractions (error_chain, failure, etc.) or does the compiler just panic / abort in place?
It depends, on cases like this the compiler usually bails out. We have a struct
DiagnosticBuilderwhich encodes the errors you normally see, which can have a message, an error code, a bunch of spans with labels, and a bunch of note/help with optional span information. The proposed outputerror: could not exec the linker `cc`: linker not found (os error 2) | = note: this error originates in the crate "deflate" = note: searched for linker in the following paths: /usr/bin/ /usr/local/bin /usr/sbinwould be created by an
let mut err: DiagnosticBuilderwitherr.note(&format!("this error originates in the crate \"{}\"", crate_name));anderr.note("searched for linker in the following paths:\n/usr/bin/\n/usr/local/bin\n/usr/sbin");.Does
rustcalready know the name of the library it's linking? Theoretically speaking, the strategy I'd think about: Ifrustcinvokes the linker with let's say-lcurlit has to give the string "curl" somehow to the linker right? So what the compiler could do is to track what the latest linker argument was - and if the linker throws an error, we parse the output, print the paths and exit.I believe it should know that, but in this case where
ccis not available, the crate failing to compile is not as relevant, and will be pointed out right before:Compiling deflate v0.7.17 error: could not exec the linker `cc`: linker not found (os error 2) | = note: searched for linker in the following paths: /usr/bin/ /usr/local/bin /usr/sbinand for case 2:
error: linking with `cc` failed: exit code: 1 --> deflate/build.rs:2:5 | 2 | println!("cargo:rustc-link-lib=asdfasdf"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: /usr/bin/ld: cannot find library "asdfasdf" = note: searched for library in the following paths: /usr/bin/ /usr/local/bin /usr/sbin = note: collect2: error: ld returned 1 exit statusI believe this is gonna be trickier to accomplish (would have to dig deeper in the code to see if it is even possible at the moment), I would keep that feature as a follow up PR, so that you can focus on a single smaller task to get a feel for the compiler codebase. Once you're acquainted with it, we can look to see if we can get the relevant span (I'm not sure if it's possible), how to expose the missing library name and the search paths.
How does that sound, @fschutt?
Sure, doing the easy things first. The thing I'm currently running into is this:
info: looks like you are running this command under `sudo` and so in order to preserve your $HOME this will now use vendored sources by default. Note that if this does not work you should run a normal build first before running a command like `sudo make install` Updating submodules error: failed to load source for a dependency on `cc` Caused by: Unable to update registry `https://github.com/rust-lang/crates.io-index` Caused by: failed to update replaced source registry `https://github.com/rust-lang/crates.io-index` Caused by: failed to read root of directory source: ~/rust/src/vendor Caused by: No such file or directory (os error 2)I am not sure what "you should run a normal build first" means - I just ran
sudo ./x.py build. Not sure what "normal build" means I should do. I cloned into~/rust, but the folder~/rust/src/vendordoesn't exist. I tried configure + make, but that seems to do the same. This happens even with a clean install.Why are you running with
sudo? The build should work as expected running as your user (./x.py build).Thanks, I got it to build. I'll try to do a PR.
Reacted by Esteban KuberSo I now changed it to output:
error: could not exec the linker `cc`: linker not found: No such file or directory (os error 2) error: aborting due to previous error... however, I think it's easier shortening the error message to:
error: linker `cc` not found: No such file or directory (os error 2) error: aborting due to previous errorIt's better to read imho, because if you use a terminal, you need less time to understand the error.
Could you move the OS error to a
note? That way people the error message itself is short and to the point, while the extra context still available to the user.error: linker `cc` not found | = note: No such file or directory (os error 2)It would also be amazing if depending on the environment being run we had an error message with a targeted description similar to the troubleshooting in the first edition of The Book telling you how to fix the problem without having to google it.
Nitpicks aside, feel free to post the PR at your earliest convenience! I love seeing this kind of small, incremental but dramatic usability improvements :D
14 remaining items
- addedT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
on Jun 11, 2020 I've encountered this bug due to the linking errors when linking targets with rustc in Chromium. Chromium is a large project, so the number of command line arguments passed to the linker is very many. Since rustc prints the whole command line of the linker when it encounters an error, the terminal is spammed with thousands of lines of (wrapped) output.
--error-format=shortomits the lengthly command line, but also omits the error that occured in the sub-process, as it does not print any of its stderr. It only printserror: linking with `../../third_party/llvm-build/Release+Asserts/bin/clang++` failed: exit status: 1With the default error-format, you can see an example of the length of the error message here: https://pastebin.com/raw/UKLFErKE
The actual error is short, but is lead with many lines of spam to print out the command-line:
= note: ld.lld: error: undefined symbol: base::rs_glue::NewRunLoopA() >>> referenced by mod.rs.cc:504 (gen/base/rs_glue/mod.rs.cc:504) >>> base/mod.rs.o:(base$rs_glue$cxxbridge1$NewRunLoopA) in archive obj/base/libbase.a clang++: error: linker command failed with exit code 1 (use -v to see invocation)Note that clang does not print the invocation of the linker command line unless you pass -v, and it mentions this in its own error message:
clang++: error: linker command failed with exit code 1 (use -v to see invocation)@michaelwoerister You've assisted me in the past (thank you!), could you advise if the issue of the linker invocation always being part of the output should be raised as a separate bug, and if an MCP would be needed to change the default behaviour and/or add a new option for --error-format?
note that this is the current output if the linker can't be exec'd:
; rustc src/main.rs -C linker=not-found error: linker `not-found` not found | = note: No such file or directory (os error 2) error: aborting due to 1 previous error
it would be nice to print PATH like fschutt suggested, though.
Maybe include the folders that rust searched for the libraries (helpful for debugging $PATH issues)
this is interesting. rustc will do this today if you pass
RUSTC_LOG=rustc_codegen_ssa::back::link -C link-arg=-Wl,--verbose. after my change in #119286 RUSTC_LOG will do nothing and you have to use-C link-arg=-Wl,--verbose --verboseinstead. i am reconsidering whether rustc should just always show the linker stdout by default, not just stderr.the other parts of this issue are tracked by #83436 and #109979.
Reacted by Esteban KuberMaybe include the folders that rust searched for the libraries (helpful for debugging $PATH issues)
this is interesting. rustc will do this today if you pass RUSTC_LOG=rustc_codegen_ssa::back::link -C link-arg=-Wl,--verbose.
this ends up being very verbose, though - i wonder if rustc should suggest
gcc -print-search-dirsinstead. that might not include all the-B/-Lflags, though? maybe it can include the relevant flags it would pass through to the linker in that suggestion?Reacted by Esteban Kuber@jyn514 the only thing I'd like to add as a check is confirming that the flags will work for the linker being used, but other than that I think it is reasonable to suggest the appropriate flags to add. I think it might be reasonable to make
--verbosealso imply those linker flags and suggest just--verbosein the output?A case encountered by a (not)FreeBSD user:
root@user-ghostbsd:/home/user/Downloads/memory # cargo run Compiling proc-macro2 v1.0.105 Compiling quote v1.0.43 Compiling libm v0.2.15 Compiling libc v0.2.180 Compiling serde_core v1.0.228 Compiling portable-atomic v1.13.0 Compiling serde v1.0.228 Compiling rustix v1.1.3 error: linking with cc failed: exit status: 1 | = note: "cc" "-m64" "/tmp/rustcYZEtLK/symbols.o" "<2 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/lib/rustlib/x86_64-unknown-freebsd/lib/{libstd-,libpanic_unwind-,libobject-,libmemchr-,libaddr2line-,libgimli-,libcfg_if-,librustc_demangle-,libstd_detect-,libhashbrown-,librustc_std_workspace_alloc-,libminiz_oxide-,libadler2-,libunwind-,liblibc-,librustc_std_workspace_core-,liballoc-,libcore-,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lexecinfo" "-lpthread" "-lgcc_s" "-lc" "-lm" "-lrt" "-lpthread" "-lrt" "-lutil" "-lexecinfo" "-lkvm" "-lmemstat" "-lkvm" "-lutil" "-lprocstat" "-lrt" "-ldevstat" "-L" "/tmp/rustcYZEtLK/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-o" "/home/user/Downloads/memory/target/debug/build/quote-ebcbd81c14b23f6f/build_script_build-ebcbd81c14b23f6f" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" = note: some arguments are omitted. use --verbose to show all linker arguments = note: ld: error: cannot open Scrt1.o: No such file or directory ld: error: cannot open crti.o: No such file or directory ld: error: cannot open crtbeginS.o: No such file or directory ld: error: unable to find library -lexecinfo ld: error: unable to find library -lpthread ld: error: unable to find library -lgcc_s ld: error: unable to find library -lc ld: error: unable to find library -lm ld: error: unable to find library -lrt ld: error: unable to find library -lpthread ld: error: unable to find library -lrt ld: error: unable to find library -lutil ld: error: unable to find library -lexecinfo ld: error: unable to find library -lkvm ld: error: unable to find library -lmemstat ld: error: unable to find library -lkvm ld: error: unable to find library -lutil ld: error: unable to find library -lprocstat ld: error: unable to find library -lrt ld: error: unable to find library -ldevstat ld: error: too many errors emitted, stopping now (use --error-limit=0 to see all errors) cc: error: linker command failed with exit code 1 (use -v to see invocation)The proposed solution was to install llvm, as it wasn't part of their OS' default installation:
pkg install llvm pkg install clangWe could at least add a
notementioning that llvm might not be installed?this is not specific to llvm, the problem is that they didn't have a C toolchain installed at all. I'm not sure how they managed to install
ccwithout installing-lc, something very weird is going on there.Reacted by Esteban Kuber
This is a very annoying aspect of rustc - if it fails to link a library, it doesn't tell you the path, it doesn't tell you which library it couldn't find, it just spits out a completely unreadable error message and quits. For example:
Right, now try and read what the actual error is. In this case, the linker
ccis missing, but the error message is similar if a system library is missing - ex. you tried to use curl-rs but you don't have libcurl installed. In that case, you have to search for the missing library somewhere in the last arguments.Please:
That would be my suggestion for improving cargo. It's simply annoying to figure out which library is missing (99,99% it's a system dependency).
Linked from rust-lang/cargo#4863