Skip to content

Commit 9c499cc

Browse files
committed
Auto merge of #57018 - dcreager:redundant-linker, r=alexcrichton
Keep last redundant linker flag, not first When a library (L1) is passed to the linker multiple times, this is sometimes purposeful: there might be several other libraries in the linker command (L2 and L3) that all depend on L1. You'd end up with a (simplified) linker command that looks like: ``` -l2 -l1 -l3 -l1 ``` With the previous behavior, when rustc encountered a redundant library, it would keep the first instance, and remove the later ones, resulting in: ``` -l2 -l1 -l3 ``` This can cause a linker error, because on some platforms (e.g. Linux), the linker will only include symbols from L1 that are needed *at the point it's referenced in the command line*. So if L3 depends on additional symbols from L1, which aren't needed by L2, the linker won't know to include them, and you'll end up with "undefined symbols" errors. A better behavior is to keep the *last* instance of the library: ``` -l2 -l3 -l1 ``` This ensures that all "downstream" libraries have been included in the linker command before the "upstream" library is referenced. Fixes #47989
2 parents 0c8700b + 32d99ef commit 9c499cc

File tree

7 files changed

+75
-25
lines changed

7 files changed

+75
-25
lines changed

src/librustc_metadata/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
22

33
#![feature(box_patterns)]
4+
#![feature(drain_filter)]
45
#![feature(libc)]
56
#![feature(nll)]
67
#![feature(proc_macro_internals)]

src/librustc_metadata/native_libs.rs

+26-25
Original file line numberDiff line numberDiff line change
@@ -199,34 +199,31 @@ impl<'a, 'tcx> Collector<'a, 'tcx> {
199199
}
200200

201201
// Update kind and, optionally, the name of all native libraries
202-
// (there may be more than one) with the specified name.
202+
// (there may be more than one) with the specified name. If any
203+
// library is mentioned more than once, keep the latest mention
204+
// of it, so that any possible dependent libraries appear before
205+
// it. (This ensures that the linker is able to see symbols from
206+
// all possible dependent libraries before linking in the library
207+
// in question.)
203208
for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs {
204-
let mut found = false;
205-
for lib in self.libs.iter_mut() {
206-
let lib_name = match lib.name {
207-
Some(n) => n,
208-
None => continue,
209-
};
210-
if lib_name == name as &str {
211-
let mut changed = false;
212-
if let Some(k) = kind {
213-
lib.kind = k;
214-
changed = true;
215-
}
216-
if let &Some(ref new_name) = new_name {
217-
lib.name = Some(Symbol::intern(new_name));
218-
changed = true;
219-
}
220-
if !changed {
221-
let msg = format!("redundant linker flag specified for \
222-
library `{}`", name);
223-
self.tcx.sess.warn(&msg);
209+
// If we've already added any native libraries with the same
210+
// name, they will be pulled out into `existing`, so that we
211+
// can move them to the end of the list below.
212+
let mut existing = self.libs.drain_filter(|lib| {
213+
if let Some(lib_name) = lib.name {
214+
if lib_name == name as &str {
215+
if let Some(k) = kind {
216+
lib.kind = k;
217+
}
218+
if let &Some(ref new_name) = new_name {
219+
lib.name = Some(Symbol::intern(new_name));
220+
}
221+
return true;
224222
}
225-
226-
found = true;
227223
}
228-
}
229-
if !found {
224+
false
225+
}).collect::<Vec<_>>();
226+
if existing.is_empty() {
230227
// Add if not found
231228
let new_name = new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
232229
let lib = NativeLibrary {
@@ -237,6 +234,10 @@ impl<'a, 'tcx> Collector<'a, 'tcx> {
237234
wasm_import_module: None,
238235
};
239236
self.register_native_lib(None, lib);
237+
} else {
238+
// Move all existing libraries with the same name to the
239+
// end of the command line.
240+
self.libs.append(&mut existing);
240241
}
241242
}
242243
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-include ../tools.mk
2+
3+
ifdef IS_WINDOWS
4+
all:
5+
else
6+
7+
# rustc will remove one of the two redundant references to foo below. Depending
8+
# on which one gets removed, we'll get a linker error on SOME platforms (like
9+
# Linux). On these platforms, when a library is referenced, the linker will
10+
# only pull in the symbols needed _at that point in time_. If a later library
11+
# depends on additional symbols from the library, they will not have been pulled
12+
# in, and you'll get undefined symbols errors.
13+
#
14+
# So in this example, we need to ensure that rustc keeps the _later_ reference
15+
# to foo, and not the former one.
16+
RUSTC_FLAGS = \
17+
-l static=bar \
18+
-l foo \
19+
-l static=baz \
20+
-l foo \
21+
-Z print-link-args
22+
23+
all: $(call DYLIB,foo) $(call STATICLIB,bar) $(call STATICLIB,baz)
24+
$(RUSTC) $(RUSTC_FLAGS) main.rs
25+
$(call RUN,main)
26+
27+
endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void bar() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extern void foo1();
2+
extern void foo2();
3+
4+
void baz() {
5+
foo1();
6+
foo2();
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
void foo1() {}
2+
void foo2() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
extern "C" {
2+
fn bar();
3+
fn baz();
4+
}
5+
6+
fn main() {
7+
unsafe {
8+
bar();
9+
baz();
10+
}
11+
}

0 commit comments

Comments
 (0)