Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] internal lld linker #36120

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@
[submodule "src/liblibc"]
path = src/liblibc
url = https://github.com/rust-lang/libc.git
[submodule "src/lld"]
path = src/lld
url = git://github.com/japaric/lld.git
branch = rust-lld-2016-07-09
1 change: 1 addition & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ opt rustbuild 0 "use the rust and cargo based build system"
opt codegen-tests 1 "run the src/test/codegen tests"
opt option-checking 1 "complain about unrecognized options in this configure script"
opt ninja 0 "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)"
opt lld 1 "Build and embed lld, the LLVM linker, into rustc"

# Optimization and debugging options. These may be overridden by the release channel, etc.
opt_nosave optimize 1 "build optimized rust code"
Expand Down
5 changes: 5 additions & 0 deletions src/bootstrap/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,11 @@ pub fn krate(build: &Build,
continue
}

// FIXME(maybe) rustc_lld doesn't work right now but it doesn't have any tests anyway
if crate_name.contains("rustc_lld") {
continue
}

cargo.arg("-p").arg(crate_name);
}

Expand Down
11 changes: 8 additions & 3 deletions src/bootstrap/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,20 @@ use toml::{Parser, Decoder, Value};
/// `src/bootstrap/config.toml.example`.
#[derive(Default)]
pub struct Config {
pub ccache: bool,
pub ninja: bool,
pub verbose: bool,
pub submodules: bool,
pub compiler_docs: bool,
pub docs: bool,
pub target_config: HashMap<String, Target>,

// llvm codegen options
// llvm options (see struct Llvm)
pub ccache: bool,
pub ninja: bool,
pub llvm_assertions: bool,
pub llvm_optimize: bool,
pub llvm_version_check: bool,
pub llvm_static_stdcpp: bool,
pub lld: bool,

// rust codegen options
pub rust_optimize: bool,
Expand Down Expand Up @@ -128,6 +129,7 @@ struct Llvm {
optimize: Option<bool>,
version_check: Option<bool>,
static_libstdcpp: Option<bool>,
lld: Option<bool>,
}

/// TOML representation of how the Rust build is configured.
Expand Down Expand Up @@ -163,6 +165,7 @@ struct TomlTarget {
impl Config {
pub fn parse(build: &str, file: Option<PathBuf>) -> Config {
let mut config = Config::default();
config.lld = true;
config.llvm_optimize = true;
config.use_jemalloc = true;
config.backtrace = true;
Expand Down Expand Up @@ -229,6 +232,7 @@ impl Config {
set(&mut config.llvm_optimize, llvm.optimize);
set(&mut config.llvm_version_check, llvm.version_check);
set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
set(&mut config.lld, llvm.lld);
}
if let Some(ref rust) = toml.rust {
set(&mut config.rust_debug_assertions, rust.debug_assertions);
Expand Down Expand Up @@ -330,6 +334,7 @@ impl Config {
("LOCAL_REBUILD", self.local_rebuild),
("NINJA", self.ninja),
("CODEGEN_TESTS", self.codegen_tests),
("LLD", self.lld),
}

match key {
Expand Down
8 changes: 8 additions & 0 deletions src/bootstrap/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,11 @@ impl Build {
continue
}

if submodule.path.components().any(|c| c == Component::Normal("lld".as_ref())) &&
!self.config.lld {
continue
}

match submodule.state {
State::MaybeDirty => {
// drop staged changes
Expand Down Expand Up @@ -730,6 +735,9 @@ impl Build {
if self.config.use_jemalloc {
features.push_str(" jemalloc");
}
if self.config.lld {
features.push_str(" lld");
}
return features
}

Expand Down
5 changes: 5 additions & 0 deletions src/bootstrap/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ pub fn llvm(build: &Build, target: &str) {
.define("LLVM_ENABLE_LIBEDIT", "OFF")
.define("LLVM_PARALLEL_COMPILE_JOBS", build.jobs().to_string());

if build.config.lld {
cfg.define("LLVM_EXTERNAL_LLD_SOURCE_DIR", build.src.join("src/lld"));
cfg.define("LLVM_EXTERNAL_PROJECTS", "lld");
}

if target.starts_with("i686") {
cfg.define("LLVM_BUILD_32_BITS", "ON");
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"the directory the MIR is dumped into"),
perf_stats: bool = (false, parse_bool, [UNTRACKED],
"print some performance-related statistics"),
use_lld: bool = (false, parse_bool, [UNTRACKED], "use lld as linker"),
}

pub fn default_lib_output() -> CrateType {
Expand Down
5 changes: 5 additions & 0 deletions src/librustc_back/target/linux_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ pub fn opts() -> TargetOptions {
// Always enable NX protection when it is available
"-Wl,-z,noexecstack".to_string(),
],
pre_lld_args: vec![
"--as-needed".to_string(),
"-z".to_string(),
"noexecstack".to_string(),
],
position_independent_executables: true,
exe_allocation_crate: super::maybe_jemalloc(),
has_elf_tls: true,
Expand Down
15 changes: 15 additions & 0 deletions src/librustc_back/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ pub struct TargetOptions {
/// user-defined libraries.
pub post_link_args: Vec<String>,

/// lld flags are slightly different from the default linker's (e.g. cc) and we can't always map
/// one to the other so we just maintain a different set of args just for lld
pub pre_lld_args: Vec<String>,
pub late_lld_args: Vec<String>,
pub post_lld_args: Vec<String>,

/// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults
/// to "generic".
pub cpu: String,
Expand Down Expand Up @@ -355,6 +361,9 @@ impl Default for TargetOptions {
ar: option_env!("CFG_DEFAULT_AR").unwrap_or("ar").to_string(),
pre_link_args: Vec::new(),
post_link_args: Vec::new(),
pre_lld_args: Vec::new(),
late_lld_args: Vec::new(),
post_lld_args: Vec::new(),
cpu: "generic".to_string(),
features: "".to_string(),
dynamic_linking: false,
Expand Down Expand Up @@ -492,11 +501,14 @@ impl Target {
key!(linker);
key!(ar);
key!(pre_link_args, list);
key!(pre_lld_args, list);
key!(pre_link_objects_exe, list);
key!(pre_link_objects_dll, list);
key!(late_link_args, list);
key!(late_lld_args, list);
key!(post_link_objects, list);
key!(post_link_args, list);
key!(post_lld_args, list);
key!(cpu);
key!(features);
key!(dynamic_linking, bool);
Expand Down Expand Up @@ -634,11 +646,14 @@ impl ToJson for Target {
target_option_val!(linker);
target_option_val!(ar);
target_option_val!(pre_link_args);
target_option_val!(pre_lld_args);
target_option_val!(pre_link_objects_exe);
target_option_val!(pre_link_objects_dll);
target_option_val!(late_link_args);
target_option_val!(late_lld_args);
target_option_val!(post_link_objects);
target_option_val!(post_link_args);
target_option_val!(post_lld_args);
target_option_val!(cpu);
target_option_val!(features);
target_option_val!(dynamic_linking);
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ syntax = { path = "../libsyntax" }
syntax_ext = { path = "../libsyntax_ext" }
syntax_pos = { path = "../libsyntax_pos" }
proc_macro = { path = "../libproc_macro" }

[features]
lld = ["rustc_llvm/lld", "rustc_trans/lld"]
17 changes: 17 additions & 0 deletions src/librustc_lld/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
authors = ["The Rust Project Developers"]
name = "rustc_lld"
version = "0.0.0"
build = "build.rs"

[lib]
name = "rustc_lld"
path = "lib.rs"
crate-type = ["dylib"]

[build-dependencies]
build_helper = { path = "../build_helper" }
gcc = "0.3.27"

[dependencies]
rustc_llvm = { path = "../librustc_llvm" }
115 changes: 115 additions & 0 deletions src/librustc_lld/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate build_helper;
extern crate gcc;

use std::env;
use std::path::PathBuf;
use std::process::Command;

use build_helper::output;

// NOTE This pretty much looks the same as librustc_llvm build.rs. Refer to that file for an
// explanation of what this function does.

fn main() {
let target = env::var("TARGET").unwrap();
let host = env::var("HOST").unwrap();
let is_crossed = target != host;

let llvm_config = env::var_os("LLVM_CONFIG")
.map(PathBuf::from)
.unwrap_or_else(|| {
if let Some(dir) = env::var_os("CARGO_TARGET_DIR")
.map(PathBuf::from) {
let to_test = dir.parent()
.unwrap()
.parent()
.unwrap()
.join(&target)
.join("llvm/bin/llvm-config");
if Command::new(&to_test).output().is_ok() {
return to_test;
}
}
PathBuf::from("llvm-config")
});

println!("cargo:rerun-if-changed={}", llvm_config.display());

let mut cmd = Command::new(&llvm_config);
cmd.arg("--cxxflags");
let cxxflags = output(&mut cmd);

let mut cfg = gcc::Config::new();
for flag in cxxflags.split_whitespace() {
// Ignore flags like `-m64` when we're doing a cross build
if is_crossed && flag.starts_with("-m") {
continue;
}
cfg.flag(flag);
}

cfg.file("../rustlld/RustWrapper.cpp")
.cpp(true)
.cpp_link_stdlib(None)
.compile("librustlld.a");

let mut cmd = Command::new(&llvm_config);
cmd.arg("--ldflags");
for lib in output(&mut cmd).split_whitespace() {
if lib.starts_with("-LIBPATH:") {
println!("cargo:rustc-link-search=native={}", &lib[9..]);
} else if is_crossed {
if lib.starts_with("-L") {
println!("cargo:rustc-link-search=native={}",
lib[2..].replace(&host, &target));
}
} else if lib.starts_with("-l") {
println!("cargo:rustc-link-lib={}", &lib[2..]);
} else if lib.starts_with("-L") {
println!("cargo:rustc-link-search=native={}", &lib[2..]);
}
}

if !target.contains("msvc") {
if let Some(s) = env::var_os("LLVM_STATIC_STDCPP") {
assert!(!cxxflags.contains("stdlib=libc++"));
let path = PathBuf::from(s);
println!("cargo:rustc-link-search=native={}",
path.parent().unwrap().display());
println!("cargo:rustc-link-lib=static=stdc++");
} else if cxxflags.contains("stdlib=libc++") {
println!("cargo:rustc-link-lib=c++");
} else {
println!("cargo:rustc-link-lib=stdc++");
}
}

// NOTE After this point comes LLD specific stuff
//
// We need to link to these libraries to be able to use lld functions via ffi
// These libraries depend on llvm libraries but we don't link this crate to those libraries
// because that can result in duplicate linking to static libraries, instead rustc_llvm will
// link to those libraries for us.
//
// To elaborate on this last point: which libraries we must link to come from the output of
// llvm-config. llvm-config maps "components" to -l flags. rustc_llvm is already using this
// method to figure out which -l flags we need to use llvm via ffi. If we do the same thing here
// we risk ending up static llvm libraries being linked up more than once in the final rustc
// binary -- and this last part can cause runtime failures of rustc like
//
// : CommandLine Error: Option 'color' registered more than once!
// LLVM ERROR: inconsistency in registered CommandLine options
//
println!("cargo:rustc-link-lib=static=lldConfig");
println!("cargo:rustc-link-lib=static=lldELF");
}
16 changes: 16 additions & 0 deletions src/librustc_lld/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::os::raw::{c_char, c_uint};

#[linked_from = "rustlld"] // not quite true but good enough
extern {
pub fn LldRustElfLink(Args: *const *const c_char, NumArgs: c_uint)-> bool;
}
17 changes: 17 additions & 0 deletions src/librustc_lld/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(linked_from)]
#![feature(rustc_private)]

// NOTE Used to "inherit" the static llvm libraries that rustc_llvm links to
extern crate rustc_llvm;

pub mod ffi;
1 change: 1 addition & 0 deletions src/librustc_llvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ path = "lib.rs"
crate-type = ["dylib"]

[features]
lld = []
static-libstdcpp = []

[dependencies]
Expand Down
Loading