Skip to content

Commit e84931a

Browse files
Rollup merge of rust-lang#149937 - jyn514:linker-info, r=mati865
try enabling `linker-messages` by default again - Split out `linker-info` from `linker-messages` - Make `linker-messages` warn-by-default; keep `linker-info` deny-by-default Helps with rust-lang#136096.
2 parents d00ba92 + 8ee8d40 commit e84931a

20 files changed

Lines changed: 306 additions & 54 deletions

File tree

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 150 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
2222
use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize};
2323
use rustc_hir::attrs::NativeLibKind;
2424
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
25+
use rustc_lint_defs::builtin::LINKER_INFO;
2526
use rustc_macros::LintDiagnostic;
2627
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
2728
use rustc_metadata::{
@@ -60,7 +61,8 @@ use super::rpath::{self, RPathConfig};
6061
use super::{apple, versioned_llvm_target};
6162
use crate::base::needs_allocator_shim_for_linking;
6263
use crate::{
63-
CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file,
64+
CodegenLintLevels, CodegenResults, CompiledModule, CrateInfo, NativeLib, errors,
65+
looks_like_rust_object_file,
6466
};
6567

6668
pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
@@ -670,6 +672,145 @@ struct LinkerOutput {
670672
inner: String,
671673
}
672674

675+
fn is_msvc_link_exe(sess: &Session) -> bool {
676+
let (linker_path, flavor) = linker_and_flavor(sess);
677+
sess.target.is_like_msvc
678+
&& flavor == LinkerFlavor::Msvc(Lld::No)
679+
// Match exactly "link.exe"
680+
&& linker_path.to_str() == Some("link.exe")
681+
}
682+
683+
fn is_macos_ld(sess: &Session) -> bool {
684+
let (_, flavor) = linker_and_flavor(sess);
685+
sess.target.is_like_darwin && matches!(flavor, LinkerFlavor::Darwin(_, Lld::No))
686+
}
687+
688+
fn is_windows_gnu_ld(sess: &Session) -> bool {
689+
let (_, flavor) = linker_and_flavor(sess);
690+
sess.target.is_like_windows
691+
&& !sess.target.is_like_msvc
692+
&& matches!(flavor, LinkerFlavor::Gnu(_, Lld::No))
693+
}
694+
695+
fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) {
696+
let mut escaped_stderr = escape_string(&stderr);
697+
let mut escaped_stdout = escape_string(&stdout);
698+
let mut linker_info = String::new();
699+
700+
info!("linker stderr:\n{}", &escaped_stderr);
701+
info!("linker stdout:\n{}", &escaped_stdout);
702+
703+
fn for_each(bytes: &[u8], mut f: impl FnMut(&str, &mut String)) -> String {
704+
let mut output = String::new();
705+
if let Ok(str) = str::from_utf8(bytes) {
706+
info!("line: {str}");
707+
output = String::with_capacity(str.len());
708+
for line in str.lines() {
709+
f(line.trim(), &mut output);
710+
}
711+
}
712+
escape_string(output.trim().as_bytes())
713+
}
714+
715+
if is_msvc_link_exe(sess) {
716+
info!("inferred MSVC link.exe");
717+
718+
escaped_stdout = for_each(&stdout, |line, output| {
719+
// Hide some progress messages from link.exe that we don't care about.
720+
// See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
721+
if line.starts_with(" Creating library")
722+
|| line.starts_with("Generating code")
723+
|| line.starts_with("Finished generating code")
724+
{
725+
linker_info += line;
726+
linker_info += "\r\n";
727+
} else {
728+
*output += line;
729+
*output += "\r\n"
730+
}
731+
});
732+
} else if is_macos_ld(sess) {
733+
info!("inferred macOS LD");
734+
735+
// FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113
736+
let deployment_mismatch = |line: &str| {
737+
line.starts_with("ld: warning: object file (")
738+
&& line.contains("was built for newer 'macOS' version")
739+
&& line.contains("than being linked")
740+
};
741+
// FIXME: This is a real warning we would like to show, but it hits too many crates
742+
// to want to turn it on immediately.
743+
let search_path = |line: &str| {
744+
line.starts_with("ld: warning: search path '") && line.ends_with("' not found")
745+
};
746+
escaped_stderr = for_each(&stderr, |line, output| {
747+
// This duplicate library warning is just not helpful at all.
748+
if line.starts_with("ld: warning: ignoring duplicate libraries: ")
749+
|| deployment_mismatch(line)
750+
|| search_path(line)
751+
{
752+
linker_info += line;
753+
linker_info += "\n";
754+
} else {
755+
*output += line;
756+
*output += "\n"
757+
}
758+
});
759+
} else if is_windows_gnu_ld(sess) {
760+
info!("inferred Windows GNU LD");
761+
762+
let mut saw_exclude_symbol = false;
763+
// See https://github.com/rust-lang/rust/issues/112368.
764+
// FIXME: maybe check that binutils is older than 2.40 before downgrading this warning?
765+
let exclude_symbols = |line: &str| {
766+
line.starts_with("Warning: .drectve `-exclude-symbols:")
767+
&& line.ends_with("' unrecognized")
768+
};
769+
escaped_stderr = for_each(&stderr, |line, output| {
770+
if exclude_symbols(line) {
771+
saw_exclude_symbol = true;
772+
linker_info += line;
773+
linker_info += "\n";
774+
} else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" {
775+
linker_info += line;
776+
linker_info += "\n";
777+
} else {
778+
*output += line;
779+
*output += "\n"
780+
}
781+
});
782+
}
783+
784+
let lint_msg = |msg| {
785+
lint_level(sess, LINKER_MESSAGES, levels.linker_messages, None, |diag| {
786+
LinkerOutput { inner: msg }.decorate_lint(diag)
787+
})
788+
};
789+
let lint_info = |msg| {
790+
lint_level(sess, LINKER_INFO, levels.linker_info, None, |diag| {
791+
LinkerOutput { inner: msg }.decorate_lint(diag)
792+
})
793+
};
794+
795+
if !escaped_stderr.is_empty() {
796+
// We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
797+
escaped_stderr =
798+
escaped_stderr.strip_prefix("warning: ").unwrap_or(&escaped_stderr).to_owned();
799+
// Windows GNU LD prints uppercase Warning
800+
escaped_stderr = escaped_stderr
801+
.strip_prefix("Warning: ")
802+
.unwrap_or(&escaped_stderr)
803+
.replace(": warning: ", ": ");
804+
lint_msg(format!("linker stderr: {escaped_stderr}"));
805+
}
806+
if !escaped_stdout.is_empty() {
807+
lint_msg(format!("linker stdout: {}", escaped_stdout))
808+
}
809+
if !linker_info.is_empty() {
810+
lint_info(linker_info);
811+
}
812+
}
813+
673814
/// Create a dynamic library or executable.
674815
///
675816
/// This will invoke the system linker/cc to create the resulting file. This links to all upstream
@@ -856,11 +997,6 @@ fn link_natively(
856997

857998
match prog {
858999
Ok(prog) => {
859-
let is_msvc_link_exe = sess.target.is_like_msvc
860-
&& flavor == LinkerFlavor::Msvc(Lld::No)
861-
// Match exactly "link.exe"
862-
&& linker_path.to_str() == Some("link.exe");
863-
8641000
if !prog.status.success() {
8651001
let mut output = prog.stderr.clone();
8661002
output.extend_from_slice(&prog.stdout);
@@ -880,7 +1016,7 @@ fn link_natively(
8801016
if let Some(code) = prog.status.code() {
8811017
// All Microsoft `link.exe` linking ror codes are
8821018
// four digit numbers in the range 1000 to 9999 inclusive
883-
if is_msvc_link_exe && (code < 1000 || code > 9999) {
1019+
if is_msvc_link_exe(sess) && (code < 1000 || code > 9999) {
8841020
let is_vs_installed = find_msvc_tools::find_vs_version().is_ok();
8851021
let has_linker =
8861022
find_msvc_tools::find_tool(sess.target.arch.desc(), "link.exe")
@@ -912,48 +1048,13 @@ fn link_natively(
9121048
sess.dcx().abort_if_errors();
9131049
}
9141050

915-
let stderr = escape_string(&prog.stderr);
916-
let mut stdout = escape_string(&prog.stdout);
917-
info!("linker stderr:\n{}", &stderr);
918-
info!("linker stdout:\n{}", &stdout);
919-
920-
// Hide some progress messages from link.exe that we don't care about.
921-
// See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
922-
if is_msvc_link_exe {
923-
if let Ok(str) = str::from_utf8(&prog.stdout) {
924-
let mut output = String::with_capacity(str.len());
925-
for line in stdout.lines() {
926-
if line.starts_with(" Creating library")
927-
|| line.starts_with("Generating code")
928-
|| line.starts_with("Finished generating code")
929-
{
930-
continue;
931-
}
932-
output += line;
933-
output += "\r\n"
934-
}
935-
stdout = escape_string(output.trim().as_bytes())
936-
}
937-
}
938-
939-
let level = codegen_results.crate_info.lint_levels.linker_messages;
940-
let lint = |msg| {
941-
lint_level(sess, LINKER_MESSAGES, level, None, |diag| {
942-
LinkerOutput { inner: msg }.decorate_lint(diag)
943-
})
944-
};
945-
946-
if !prog.stderr.is_empty() {
947-
// We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
948-
let stderr = stderr
949-
.strip_prefix("warning: ")
950-
.unwrap_or(&stderr)
951-
.replace(": warning: ", ": ");
952-
lint(format!("linker stderr: {stderr}"));
953-
}
954-
if !stdout.is_empty() {
955-
lint(format!("linker stdout: {}", stdout))
956-
}
1051+
info!("reporting linker output: flavor={flavor:?}");
1052+
report_linker_output(
1053+
sess,
1054+
codegen_results.crate_info.lint_levels,
1055+
&prog.stdout,
1056+
&prog.stderr,
1057+
);
9571058
}
9581059
Err(e) => {
9591060
let linker_not_found = e.kind() == io::ErrorKind::NotFound;

compiler/rustc_codegen_ssa/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_data_structures::unord::UnordMap;
2424
use rustc_hir::CRATE_HIR_ID;
2525
use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind};
2626
use rustc_hir::def_id::CrateNum;
27+
use rustc_lint_defs::builtin::LINKER_INFO;
2728
use rustc_macros::{Decodable, Encodable};
2829
use rustc_metadata::EncodedMetadata;
2930
use rustc_middle::dep_graph::WorkProduct;
@@ -361,10 +362,14 @@ impl CodegenResults {
361362
#[derive(Copy, Clone, Debug, Encodable, Decodable)]
362363
pub struct CodegenLintLevels {
363364
linker_messages: LevelAndSource,
365+
linker_info: LevelAndSource,
364366
}
365367

366368
impl CodegenLintLevels {
367369
pub fn from_tcx(tcx: TyCtxt<'_>) -> Self {
368-
Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) }
370+
Self {
371+
linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID),
372+
linker_info: tcx.lint_level_at_node(LINKER_INFO, CRATE_HIR_ID),
373+
}
369374
}
370375
}

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ declare_lint_pass! {
6060
LARGE_ASSIGNMENTS,
6161
LATE_BOUND_LIFETIME_ARGUMENTS,
6262
LEGACY_DERIVE_HELPERS,
63+
LINKER_INFO,
6364
LINKER_MESSAGES,
6465
LONG_RUNNING_CONST_EVAL,
6566
LOSSY_PROVENANCE_CASTS,
@@ -4063,10 +4064,44 @@ declare_lint! {
40634064
/// and actionable warning of similar quality to our other diagnostics. See this tracking
40644065
/// issue for more details: <https://github.com/rust-lang/rust/issues/136096>.
40654066
pub LINKER_MESSAGES,
4066-
Allow,
4067+
Warn,
40674068
"warnings emitted at runtime by the target-specific linker program"
40684069
}
40694070

4071+
declare_lint! {
4072+
/// The `linker_info` lint forwards warnings from the linker that are known to be informational-only.
4073+
///
4074+
/// ### Example
4075+
///
4076+
/// ```rust,ignore (needs CLI args, platform-specific)
4077+
/// #[warn(linker_info)]
4078+
/// fn main () {}
4079+
/// ```
4080+
///
4081+
/// On MacOS, using `-C link-arg=-lc` and the default linker, this will produce
4082+
///
4083+
/// ```text
4084+
/// warning: linker stderr: ld: ignoring duplicate libraries: '-lc'
4085+
/// |
4086+
/// note: the lint level is defined here
4087+
/// --> ex.rs:1:9
4088+
/// |
4089+
/// 1 | #![warn(linker_info)]
4090+
/// | ^^^^^^^^^^^^^^^
4091+
/// ```
4092+
///
4093+
/// ### Explanation
4094+
///
4095+
/// Many linkers are very "chatty" and print lots of information that is not necessarily
4096+
/// indicative of an issue. This output has been ignored for many years and is often not
4097+
/// actionable by developers. It is silenced unless the developer specifically requests for it
4098+
/// to be printed. See this tracking issue for more details:
4099+
/// <https://github.com/rust-lang/rust/issues/136096>.
4100+
pub LINKER_INFO,
4101+
Allow,
4102+
"linker warnings known to be informational-only and not indicative of a problem"
4103+
}
4104+
40704105
declare_lint! {
40714106
/// The `named_arguments_used_positionally` lint detects cases where named arguments are only
40724107
/// used positionally in format strings. This usage is valid but potentially very confusing.

compiler/rustc_passes/src/check_attr.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
15891589
} else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect])
15901590
&& let Some(meta) = attr.meta_item_list()
15911591
&& meta.iter().any(|meta| {
1592-
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1592+
meta.meta_item().map_or(false, |item| {
1593+
item.path == sym::linker_messages || item.path == sym::linker_info
1594+
})
15931595
})
15941596
{
15951597
if hir_id != CRATE_HIR_ID {

compiler/rustc_passes/src/errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ pub(crate) enum UnusedNote {
318318
#[note("`default_method_body_is_const` has been replaced with `const` on traits")]
319319
DefaultMethodBodyConst,
320320
#[note(
321-
"the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked"
321+
"the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked"
322322
)]
323323
LinkerMessagesBinaryCrateOnly,
324324
}

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,6 +1362,7 @@ symbols! {
13621362
link_section,
13631363
linkage,
13641364
linker,
1365+
linker_info,
13651366
linker_messages,
13661367
linkonce,
13671368
linkonce_odr,

src/tools/tidy/src/ui_tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ fn check_unexpected_extension(check: &mut RunningCheck, file_path: &Path, ext: &
215215
"fixed", // expected source file after applying fixes
216216
"md", // test directory descriptions
217217
"ftl", // translation tests
218+
"ps1", // powershell scripts, used for fake linkers on Windows
218219
];
219220

220221
const EXTENSION_EXCEPTION_PATHS: &[&str] = &[
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void foo() {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#![warn(linker_info, linker_messages)]
2+
unsafe extern "C" {
3+
safe fn foo();
4+
}
5+
6+
fn main() {
7+
foo();
8+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//@ only-apple
2+
//! Tests that deployment target linker warnings are shown as `linker-info`, not `linker-messages`
3+
4+
use run_make_support::external_deps::c_cxx_compiler::cc;
5+
use run_make_support::external_deps::llvm::llvm_ar;
6+
use run_make_support::{bare_rustc, diff};
7+
8+
fn main() {
9+
let cwd = std::env::current_dir().unwrap().to_str().unwrap().to_owned();
10+
11+
cc().arg("-c").arg("-mmacosx-version-min=15.5").output("foo.o").input("foo.c").run();
12+
llvm_ar().obj_to_ar().output_input("libfoo.a", "foo.o").run();
13+
14+
let warnings = bare_rustc()
15+
.arg("-L")
16+
.arg(format!("native={cwd}"))
17+
.arg("-lstatic=foo")
18+
.link_arg("-mmacosx-version-min=11.2")
19+
.input("main.rs")
20+
.crate_type("bin")
21+
.run()
22+
.stderr_utf8();
23+
24+
diff()
25+
.expected_file("warnings.txt")
26+
.actual_text("(rustc -W linker-info)", &warnings)
27+
.normalize(r"\(.*/rmake_out/", "(TEST_DIR/")
28+
.run()
29+
}

0 commit comments

Comments
 (0)