Skip to content

Commit 88114f6

Browse files
committed
rustc: Group linked libraries where needed
This commit fixes a longstanding issue with the compiler with circular dependencies between libcore and libstd. The `core` crate requires at least one symbol, the ability to unwind. The `std` crate is the crate which actually defines this symbol, but the `std` crate also depends on the `core` crate. This circular dependency is in general disallowed in Rust as crates cannot have cycles amongst them. A special exception is made just for core/std, but this is also unfortunately incompatible with how GNU linkers work. GNU linkers will process undefined symbols in a left-to-right fashion, only actually linking an rlib like libstd if there are any symbols used from it. This strategy is incompatible with circular dependencies because if we otherwise don't use symbols from libstd we don't discover that we needed it until we're later processing libcore's symbols! To fix this GNU linkers support the `--start-group` and `--end-group` options which indicate "libraries between these markers may have circular dependencies amongst them. The linker invocation has been updated to automatically pass these arguments when we're invoking a GNU linker and automatically calculate where the arguments need to go (around libstd and libcore) Closes #18807 Closes #47074
1 parent ae544ee commit 88114f6

File tree

7 files changed

+154
-0
lines changed

7 files changed

+154
-0
lines changed

src/librustc_trans/back/link.rs

+52
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use rustc::hir::def_id::CrateNum;
3232
use tempdir::TempDir;
3333
use rustc_back::{PanicStrategy, RelroLevel};
3434
use rustc_back::target::TargetTriple;
35+
use rustc_data_structures::fx::FxHashSet;
3536
use context::get_reloc_model;
3637
use llvm;
3738

@@ -1188,9 +1189,56 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
11881189
// crates.
11891190
let deps = &trans.crate_info.used_crates_dynamic;
11901191

1192+
// There's a few internal crates in the standard library (aka libcore and
1193+
// libstd) which actually have a circular dependence upon one another. This
1194+
// currently arises through "weak lang items" where libcore requires things
1195+
// like `rust_begin_unwind` but libstd ends up defining it. To get this
1196+
// circular dependence to work correctly in all situations we'll need to be
1197+
// sure to correctly apply the `--start-group` and `--end-group` options to
1198+
// GNU linkers, otherwise if we don't use any other symbol from the standard
1199+
// library it'll get discarded and the whole application won't link.
1200+
//
1201+
// In this loop we're calculating the `group_end`, after which crate to
1202+
// pass `--end-group` and `group_start`, before which crate to pass
1203+
// `--start-group`. We currently do this by passing `--end-group` after
1204+
// the first crate (when iterating backwards) that requires a lang item
1205+
// defined somewhere else. Once that's set then when we've defined all the
1206+
// necessary lang items we'll pass `--start-group`.
1207+
//
1208+
// Note that this isn't amazing logic for now but it should do the trick
1209+
// for the current implementation of the standard library.
1210+
let mut group_end = None;
1211+
let mut group_start = None;
1212+
let mut end_with = FxHashSet();
1213+
let info = &trans.crate_info;
1214+
for &(cnum, _) in deps.iter().rev() {
1215+
if let Some(missing) = info.missing_lang_items.get(&cnum) {
1216+
end_with.extend(missing.iter().cloned());
1217+
if end_with.len() > 0 && group_end.is_none() {
1218+
group_end = Some(cnum);
1219+
}
1220+
}
1221+
end_with.retain(|item| info.lang_item_to_crate.get(item) != Some(&cnum));
1222+
if end_with.len() == 0 && group_end.is_some() {
1223+
group_start = Some(cnum);
1224+
break
1225+
}
1226+
}
1227+
1228+
// If we didn't end up filling in all lang items from upstream crates then
1229+
// we'll be filling it in with our crate. This probably means we're the
1230+
// standard library itself, so skip this for now.
1231+
if group_end.is_some() && group_start.is_none() {
1232+
group_end = None;
1233+
}
1234+
11911235
let mut compiler_builtins = None;
11921236

11931237
for &(cnum, _) in deps.iter() {
1238+
if group_start == Some(cnum) {
1239+
cmd.group_start();
1240+
}
1241+
11941242
// We may not pass all crates through to the linker. Some crates may
11951243
// appear statically in an existing dylib, meaning we'll pick up all the
11961244
// symbols from the dylib.
@@ -1217,6 +1265,10 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
12171265
add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0)
12181266
}
12191267
}
1268+
1269+
if group_end == Some(cnum) {
1270+
cmd.group_end();
1271+
}
12201272
}
12211273

12221274
// compiler-builtins are always placed last to ensure that they're

src/librustc_trans/back/linker.rs

+26
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ pub trait Linker {
125125
fn args(&mut self, args: &[String]);
126126
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType);
127127
fn subsystem(&mut self, subsystem: &str);
128+
fn group_start(&mut self);
129+
fn group_end(&mut self);
128130
// Should have been finalize(self), but we don't support self-by-value on trait objects (yet?).
129131
fn finalize(&mut self) -> Command;
130132
}
@@ -420,6 +422,18 @@ impl<'a> Linker for GccLinker<'a> {
420422
::std::mem::swap(&mut cmd, &mut self.cmd);
421423
cmd
422424
}
425+
426+
fn group_start(&mut self) {
427+
if !self.sess.target.target.options.is_like_osx {
428+
self.linker_arg("--start-group");
429+
}
430+
}
431+
432+
fn group_end(&mut self) {
433+
if !self.sess.target.target.options.is_like_osx {
434+
self.linker_arg("--end-group");
435+
}
436+
}
423437
}
424438

425439
pub struct MsvcLinker<'a> {
@@ -648,6 +662,10 @@ impl<'a> Linker for MsvcLinker<'a> {
648662
::std::mem::swap(&mut cmd, &mut self.cmd);
649663
cmd
650664
}
665+
666+
// MSVC doesn't need group indicators
667+
fn group_start(&mut self) {}
668+
fn group_end(&mut self) {}
651669
}
652670

653671
pub struct EmLinker<'a> {
@@ -810,6 +828,10 @@ impl<'a> Linker for EmLinker<'a> {
810828
::std::mem::swap(&mut cmd, &mut self.cmd);
811829
cmd
812830
}
831+
832+
// Appears not necessary on Emscripten
833+
fn group_start(&mut self) {}
834+
fn group_end(&mut self) {}
813835
}
814836

815837
fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec<String> {
@@ -953,4 +975,8 @@ impl Linker for WasmLd {
953975
::std::mem::swap(&mut cmd, &mut self.cmd);
954976
cmd
955977
}
978+
979+
// Not needed for now with LLD
980+
fn group_start(&mut self) {}
981+
fn group_end(&mut self) {}
956982
}

src/librustc_trans/base.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,10 @@ impl CrateInfo {
10821082
used_crate_source: FxHashMap(),
10831083
wasm_custom_sections: BTreeMap::new(),
10841084
wasm_imports: FxHashMap(),
1085+
lang_item_to_crate: FxHashMap(),
1086+
missing_lang_items: FxHashMap(),
10851087
};
1088+
let lang_items = tcx.lang_items();
10861089

10871090
let load_wasm_items = tcx.sess.crate_types.borrow()
10881091
.iter()
@@ -1128,6 +1131,13 @@ impl CrateInfo {
11281131
}
11291132
info.load_wasm_imports(tcx, cnum);
11301133
}
1134+
let missing = tcx.missing_lang_items(cnum);
1135+
for &item in missing.iter() {
1136+
if let Ok(id) = lang_items.require(item) {
1137+
info.lang_item_to_crate.insert(item, id.krate);
1138+
}
1139+
}
1140+
info.missing_lang_items.insert(cnum, missing);
11311141
}
11321142

11331143
return info

src/librustc_trans/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ use rustc::dep_graph::DepGraph;
7878
use rustc::hir::def_id::CrateNum;
7979
use rustc::middle::cstore::MetadataLoader;
8080
use rustc::middle::cstore::{NativeLibrary, CrateSource, LibSource};
81+
use rustc::middle::lang_items::LangItem;
8182
use rustc::session::{Session, CompileIncomplete};
8283
use rustc::session::config::{OutputFilenames, OutputType, PrintRequest};
8384
use rustc::ty::{self, TyCtxt};
@@ -405,6 +406,8 @@ struct CrateInfo {
405406
used_crates_dynamic: Vec<(CrateNum, LibSource)>,
406407
wasm_custom_sections: BTreeMap<String, Vec<u8>>,
407408
wasm_imports: FxHashMap<String, String>,
409+
lang_item_to_crate: FxHashMap<LangItem, CrateNum>,
410+
missing_lang_items: FxHashMap<CrateNum, Lrc<Vec<LangItem>>>,
408411
}
409412

410413
__build_diagnostic_array! { librustc_trans, DIAGNOSTICS }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-include ../tools.mk
2+
3+
ifeq ($(UNAME),Darwin)
4+
FLAGS :=
5+
else
6+
ifdef IS_WINDOWS
7+
FLAGS :=
8+
else
9+
FLAGS := -C link-args=-Wl,--no-undefined
10+
endif
11+
endif
12+
13+
all:
14+
$(RUSTC) bar.rs
15+
$(RUSTC) foo.rs $(FLAGS)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(allocator_api)]
12+
#![crate_type = "rlib"]
13+
14+
use std::heap::*;
15+
16+
pub struct A;
17+
18+
unsafe impl<'a> Alloc for &'a A {
19+
unsafe fn alloc(&mut self, _: Layout) -> Result<*mut u8, AllocErr> {
20+
loop {}
21+
}
22+
23+
unsafe fn dealloc(&mut self, _ptr: *mut u8, _: Layout) {
24+
loop {}
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(global_allocator)]
12+
#![crate_type = "cdylib"]
13+
14+
extern crate bar;
15+
16+
#[global_allocator]
17+
static A: bar::A = bar::A;
18+
19+
#[no_mangle]
20+
pub extern fn a(a: u32, b: u32) -> u32 {
21+
a / b
22+
}

0 commit comments

Comments
 (0)