diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f361ede04616..56dca6c8b902 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -22,6 +22,7 @@ use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::attrs::NativeLibKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_lint_defs::builtin::LINKER_INFO; use rustc_macros::Diagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; use rustc_metadata::{ @@ -60,10 +61,8 @@ use super::{apple, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; use crate::{ - CompiledModule, CompiledModules, CrateInfo, NativeLib, errors, looks_like_rust_object_file, -}; -use crate::{ - CodegenLintLevels, CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file + CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors, + looks_like_rust_object_file, }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -683,30 +682,105 @@ fn is_msvc_link_exe(sess: &Session) -> bool { && linker_path.to_str() == Some("link.exe") } +fn is_macos_ld(sess: &Session) -> bool { + let (_, flavor) = linker_and_flavor(sess); + sess.target.is_like_darwin && matches!(flavor, LinkerFlavor::Darwin(_, Lld::No)) +} + +fn is_windows_gnu_ld(sess: &Session) -> bool { + let (_, flavor) = linker_and_flavor(sess); + sess.target.is_like_windows + && !sess.target.is_like_msvc + && matches!(flavor, LinkerFlavor::Gnu(_, Lld::No)) +} + fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) { - let escaped_stderr = escape_string(&stderr); + let mut escaped_stderr = escape_string(&stderr); let mut escaped_stdout = escape_string(&stdout); + let mut linker_info = String::new(); + info!("linker stderr:\n{}", &escaped_stderr); info!("linker stdout:\n{}", &escaped_stdout); - // Hide some progress messages from link.exe that we don't care about. - // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 - if is_msvc_link_exe(sess) { - if let Ok(str) = str::from_utf8(&stdout) { - let mut output = String::with_capacity(str.len()); + fn for_each(bytes: &[u8], mut f: impl FnMut(&str, &mut String)) -> String { + let mut output = String::new(); + if let Ok(str) = str::from_utf8(bytes) { + info!("line: {str}"); + output = String::with_capacity(str.len()); for line in str.lines() { - if line.starts_with(" Creating library") - || line.starts_with("Generating code") - || line.starts_with("Finished generating code") - { - continue; - } else { - output += line; - output += "\r\n" - } + f(line.trim(), &mut output); } - escaped_stdout = escape_string(output.trim().as_bytes()) } + escape_string(output.trim().as_bytes()) + } + + if is_msvc_link_exe(sess) { + info!("inferred MSVC link.exe"); + + escaped_stdout = for_each(&stdout, |line, output| { + // Hide some progress messages from link.exe that we don't care about. + // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 + if line.starts_with(" Creating library") + || line.starts_with("Generating code") + || line.starts_with("Finished generating code") + { + linker_info += line; + linker_info += "\r\n"; + } else { + *output += line; + *output += "\r\n" + } + }); + } else if is_macos_ld(sess) { + info!("inferred macOS LD"); + + // FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113 + let deployment_mismatch = |line: &str| { + line.starts_with("ld: warning: object file (") + && line.contains("was built for newer 'macOS' version") + && line.contains("than being linked") + }; + // FIXME: This is a real warning we would like to show, but it hits too many crates + // to want to turn it on immediately. + let search_path = |line: &str| { + line.starts_with("ld: warning: search path '") && line.ends_with("' not found") + }; + escaped_stderr = for_each(&stderr, |line, output| { + // This duplicate library warning is just not helpful at all. + if line.starts_with("ld: warning: ignoring duplicate libraries: ") + || deployment_mismatch(line) + || search_path(line) + { + linker_info += line; + linker_info += "\n"; + } else { + *output += line; + *output += "\n" + } + }); + } else if is_windows_gnu_ld(sess) { + info!("inferred Windows GNU LD"); + + let mut saw_exclude_symbol = false; + // See https://github.com/rust-lang/rust/issues/112368. + // FIXME: maybe check that binutils is older than 2.40 before downgrading this warning? + let exclude_symbols = |line: &str| { + line.starts_with("Warning: .drectve `-exclude-symbols:") + && line.ends_with("' unrecognized") + }; + escaped_stderr = for_each(&stderr, |line, output| { + if exclude_symbols(line) { + saw_exclude_symbol = true; + linker_info += line; + linker_info += "\n"; + } else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" { + linker_info += line; + linker_info += "\n"; + } else { + *output += line; + *output += "\n" + } + }); } let lint_msg = |msg| { @@ -718,18 +792,27 @@ fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8] LinkerOutput { inner: msg }, ); }; + let lint_info = |msg| { + diag_lint_level(sess, LINKER_INFO, levels.linker_info, None, LinkerOutput { inner: msg }); + }; if !escaped_stderr.is_empty() { // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present. - let stderr = escaped_stderr - .strip_prefix("warning: ") + escaped_stderr = + escaped_stderr.strip_prefix("warning: ").unwrap_or(&escaped_stderr).to_owned(); + // Windows GNU LD prints uppercase Warning + escaped_stderr = escaped_stderr + .strip_prefix("Warning: ") .unwrap_or(&escaped_stderr) .replace(": warning: ", ": "); - lint_msg(format!("linker stderr: {stderr}")); + lint_msg(format!("linker stderr: {escaped_stderr}")); } if !escaped_stdout.is_empty() { lint_msg(format!("linker stdout: {}", escaped_stdout)) } + if !linker_info.is_empty() { + lint_info(linker_info); + } } /// Create a dynamic library or executable. @@ -971,6 +1054,7 @@ fn link_natively( sess.dcx().abort_if_errors(); } + info!("reporting linker output: flavor={flavor:?}"); report_linker_output(sess, crate_info.lint_levels, &prog.stdout, &prog.stderr); } Err(e) => { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 6a0a9e7d51be..62914b82747c 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -24,6 +24,7 @@ use rustc_hir::CRATE_HIR_ID; use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind}; use rustc_hir::def_id::CrateNum; +use rustc_lint_defs::builtin::LINKER_INFO; use rustc_macros::{Decodable, Encodable}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::WorkProduct; @@ -364,10 +365,14 @@ pub fn deserialize_rlink( #[derive(Copy, Clone, Debug, Encodable, Decodable)] pub struct CodegenLintLevels { linker_messages: LevelAndSource, + linker_info: LevelAndSource, } impl CodegenLintLevels { pub fn from_tcx(tcx: TyCtxt<'_>) -> Self { - Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) } + Self { + linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID), + linker_info: tcx.lint_level_at_node(LINKER_INFO, CRATE_HIR_ID), + } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index c1df8aac6f31..2ef1d5bd1426 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -61,6 +61,7 @@ LARGE_ASSIGNMENTS, LATE_BOUND_LIFETIME_ARGUMENTS, LEGACY_DERIVE_HELPERS, + LINKER_INFO, LINKER_MESSAGES, LONG_RUNNING_CONST_EVAL, LOSSY_PROVENANCE_CASTS, @@ -4062,6 +4063,40 @@ "warnings emitted at runtime by the target-specific linker program" } +declare_lint! { + /// The `linker_info` lint forwards warnings from the linker that are known to be informational-only. + /// + /// ### Example + /// + /// ```rust,ignore (needs CLI args, platform-specific) + /// #[warn(linker_info)] + /// fn main () {} + /// ``` + /// + /// On MacOS, using `-C link-arg=-lc` and the default linker, this will produce + /// + /// ```text + /// warning: linker stderr: ld: ignoring duplicate libraries: '-lc' + /// | + /// note: the lint level is defined here + /// --> ex.rs:1:9 + /// | + /// 1 | #![warn(linker_info)] + /// | ^^^^^^^^^^^^^^^ + /// ``` + /// + /// ### Explanation + /// + /// Many linkers are very "chatty" and print lots of information that is not necessarily + /// indicative of an issue. This output has been ignored for many years and is often not + /// actionable by developers. It is silenced unless the developer specifically requests for it + /// to be printed. See this tracking issue for more details: + /// . + pub LINKER_INFO, + Allow, + "linker warnings known to be informational-only and not indicative of a problem" +} + declare_lint! { /// The `named_arguments_used_positionally` lint detects cases where named arguments are only /// used positionally in format strings. This usage is valid but potentially very confusing. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3e2f10df20b8..9b729d238ea1 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1621,7 +1621,9 @@ fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option< sym::expect, ]) && let Some(meta) = attr.meta_item_list() && meta.iter().any(|meta| { - meta.meta_item().map_or(false, |item| item.path == sym::linker_messages) + meta.meta_item().map_or(false, |item| { + item.path == sym::linker_messages || item.path == sym::linker_info + }) }) { if hir_id != CRATE_HIR_ID { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 8073cb257b03..accade79b1d3 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -310,7 +310,7 @@ pub(crate) enum UnusedNote { #[note("`default_method_body_is_const` has been replaced with `const` on traits")] DefaultMethodBodyConst, #[note( - "the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked" + "the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked" )] LinkerMessagesBinaryCrateOnly, } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 495611a06e04..a8d0fa9d8af2 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1152,6 +1152,7 @@ link_section, linkage, linker, + linker_info, linker_messages, linkonce, linkonce_odr, diff --git a/tests/run-make/linker-warning/fake-linker.sh b/tests/run-make/linker-warning/fake-linker.sh deleted file mode 100755 index ed4d472c3bfb..000000000000 --- a/tests/run-make/linker-warning/fake-linker.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -code=0 -while ! [ $# = 0 ]; do - case "$1" in - run_make_info) echo "foo" - ;; - run_make_warn) echo "warning: bar" >&2 - ;; - run_make_error) echo "error: baz" >&2; code=1 - ;; - *) ;; # rustc passes lots of args we don't care about - esac - shift -done - -exit $code diff --git a/tests/run-make/macos-deployment-target-warning/foo.c b/tests/run-make/macos-deployment-target-warning/foo.c new file mode 100644 index 000000000000..85e6cd8c3909 --- /dev/null +++ b/tests/run-make/macos-deployment-target-warning/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/tests/run-make/macos-deployment-target-warning/main.rs b/tests/run-make/macos-deployment-target-warning/main.rs new file mode 100644 index 000000000000..2c3be92812e1 --- /dev/null +++ b/tests/run-make/macos-deployment-target-warning/main.rs @@ -0,0 +1,8 @@ +#![warn(linker_info, linker_messages)] +unsafe extern "C" { + safe fn foo(); +} + +fn main() { + foo(); +} diff --git a/tests/run-make/macos-deployment-target-warning/rmake.rs b/tests/run-make/macos-deployment-target-warning/rmake.rs new file mode 100644 index 000000000000..e109b2adcc17 --- /dev/null +++ b/tests/run-make/macos-deployment-target-warning/rmake.rs @@ -0,0 +1,29 @@ +//@ only-apple +//! Tests that deployment target linker warnings are shown as `linker-info`, not `linker-messages` + +use run_make_support::external_deps::c_cxx_compiler::cc; +use run_make_support::external_deps::llvm::llvm_ar; +use run_make_support::{bare_rustc, diff}; + +fn main() { + let cwd = std::env::current_dir().unwrap().to_str().unwrap().to_owned(); + + cc().arg("-c").arg("-mmacosx-version-min=15.5").output("foo.o").input("foo.c").run(); + llvm_ar().obj_to_ar().output_input("libfoo.a", "foo.o").run(); + + let warnings = bare_rustc() + .arg("-L") + .arg(format!("native={cwd}")) + .arg("-lstatic=foo") + .link_arg("-mmacosx-version-min=11.2") + .input("main.rs") + .crate_type("bin") + .run() + .stderr_utf8(); + + diff() + .expected_file("warnings.txt") + .actual_text("(rustc -W linker-info)", &warnings) + .normalize(r"\(.*/rmake_out/", "(TEST_DIR/") + .run() +} diff --git a/tests/run-make/macos-deployment-target-warning/warnings.txt b/tests/run-make/macos-deployment-target-warning/warnings.txt new file mode 100644 index 000000000000..ab08c6d9ae5b --- /dev/null +++ b/tests/run-make/macos-deployment-target-warning/warnings.txt @@ -0,0 +1,11 @@ +warning: ld: warning: object file (TEST_DIR/libfoo.a[2](foo.o)) was built for newer 'macOS' version (15.5) than being linked (11.2) + + | +note: the lint level is defined here + --> main.rs:1:9 + | +1 | #![warn(linker_info, linker_messages)] + | ^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/run-make/windows-gnu-corrupt-drective/fake-linker.rs b/tests/run-make/windows-gnu-corrupt-drective/fake-linker.rs new file mode 100644 index 000000000000..63bcc41904c4 --- /dev/null +++ b/tests/run-make/windows-gnu-corrupt-drective/fake-linker.rs @@ -0,0 +1,8 @@ +// ignore-tidy-linelength + +fn main() { + println!( + "Warning: .drectve `-exclude-symbols:_ZN28windows_gnu_corrupt_drective4main17h291ed884c1aada69E ' unrecognized" + ); + println!("Warning: corrupt .drectve at end of def file"); +} diff --git a/tests/run-make/windows-gnu-corrupt-drective/main.rs b/tests/run-make/windows-gnu-corrupt-drective/main.rs new file mode 100644 index 000000000000..983bddd2187a --- /dev/null +++ b/tests/run-make/windows-gnu-corrupt-drective/main.rs @@ -0,0 +1,6 @@ +//@ only-windows-gnu +//@ build-fail +//@ compile-flags: -C linker={{src-base}}/linking/auxiliary/fake-linker.ps1 +#![deny(linker_info)] +//~? ERROR Warning: .drectve +fn main() {} diff --git a/tests/run-make/windows-gnu-corrupt-drective/rmake.rs b/tests/run-make/windows-gnu-corrupt-drective/rmake.rs new file mode 100644 index 000000000000..4000941d9f82 --- /dev/null +++ b/tests/run-make/windows-gnu-corrupt-drective/rmake.rs @@ -0,0 +1,14 @@ +//@ only-windows-gnu + +use run_make_support::{bare_rustc, rustc}; + +fn main() { + // bare_rustc so that this doesn't try to cross-compile our linker + bare_rustc().input("fake-linker.rs").output("fake-linker").run(); + rustc() + .input("main.rs") + .linker("./fake-linker") + .arg("-Wlinker-messages") + .run() + .assert_stderr_contains("Warning: .drectve"); +} diff --git a/tests/ui/linking/macos-ignoring-duplicate.rs b/tests/ui/linking/macos-ignoring-duplicate.rs new file mode 100644 index 000000000000..0c9241c8ee19 --- /dev/null +++ b/tests/ui/linking/macos-ignoring-duplicate.rs @@ -0,0 +1,6 @@ +//@ only-apple +//@ compile-flags: -C link-arg=-lc -C link-arg=-lc +//@ build-fail +#![deny(linker_info)] +//~? ERROR ignoring duplicate libraries +fn main() {} diff --git a/tests/ui/linking/macos-ignoring-duplicate.stderr b/tests/ui/linking/macos-ignoring-duplicate.stderr new file mode 100644 index 000000000000..9037e8f4e391 --- /dev/null +++ b/tests/ui/linking/macos-ignoring-duplicate.stderr @@ -0,0 +1,11 @@ +error: ld: warning: ignoring duplicate libraries: '-lc' + + | +note: the lint level is defined here + --> $DIR/macos-ignoring-duplicate.rs:4:9 + | +LL | #![deny(linker_info)] + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/linking/macos-search-path.rs b/tests/ui/linking/macos-search-path.rs new file mode 100644 index 000000000000..6d3e420bcdf6 --- /dev/null +++ b/tests/ui/linking/macos-search-path.rs @@ -0,0 +1,6 @@ +//@ only-apple +//@ compile-flags: -C link-arg=-Wl,-L/no/such/file/or/directory +//@ build-fail +#![deny(linker_info)] +//~? ERROR search path +fn main() {} diff --git a/tests/ui/linking/macos-search-path.stderr b/tests/ui/linking/macos-search-path.stderr new file mode 100644 index 000000000000..598036d0d403 --- /dev/null +++ b/tests/ui/linking/macos-search-path.stderr @@ -0,0 +1,11 @@ +error: ld: warning: search path '/no/such/file/or/directory' not found + + | +note: the lint level is defined here + --> $DIR/macos-search-path.rs:4:9 + | +LL | #![deny(linker_info)] + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/linker-warning.stderr b/tests/ui/lint/linker-warning.stderr index ae5f6b3adece..54994c48ea43 100644 --- a/tests/ui/lint/linker-warning.stderr +++ b/tests/ui/lint/linker-warning.stderr @@ -20,7 +20,7 @@ warning: unused attribute LL | #![allow(linker_messages)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | - = note: the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked + = note: the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked warning: 2 warnings emitted diff --git a/tests/ui/process/nofile-limit.rs b/tests/ui/process/nofile-limit.rs index 64777b514256..f5246856b80f 100644 --- a/tests/ui/process/nofile-limit.rs +++ b/tests/ui/process/nofile-limit.rs @@ -11,6 +11,9 @@ #![feature(exit_status_error)] #![feature(rustc_private)] +// on aarch64, "Using 'getaddrinfo' in statically linked applications requires at runtime the shared +// libraries from the glibc version used for linking" +#![allow(linker_messages)] extern crate libc; use std::os::unix::process::CommandExt;