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

Move cg_llvm::rpath and parts of cg_llvm::back::link to rustc_codegen_utils #55725

Closed
wants to merge 2 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
1 change: 1 addition & 0 deletions src/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2138,6 +2138,7 @@ dependencies = [
name = "rustc_codegen_utils"
version = "0.0.0"
dependencies = [
"cc 1.0.25 (registry+https:/rust-lang/crates.io-index)",
"flate2 1.0.3 (registry+https:/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https:/rust-lang/crates.io-index)",
"rustc 0.0.0",
Expand Down
276 changes: 6 additions & 270 deletions src/librustc_codegen_llvm/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@
// except according to those terms.

use back::wasm;
use cc::windows_registry;
use super::archive::{ArchiveBuilder, ArchiveConfig};
use super::bytecode::RLIB_BYTECODE_EXTENSION;
use super::rpath::RPathConfig;
use super::rpath;
use metadata::METADATA_FILENAME;
use rustc::session::config::{self, DebugInfo, OutputFilenames, OutputType, PrintRequest};
use rustc::session::config::{RUST_CGU_EXT, Lto};
Expand All @@ -31,80 +28,24 @@ use rustc_target::spec::{PanicStrategy, RelroLevel, LinkerFlavor};
use rustc_data_structures::fx::FxHashSet;
use rustc_codegen_utils::linker::Linker;
use rustc_codegen_utils::command::Command;
use rustc_codegen_utils::rpath::{self, RPathConfig};
use context::get_reloc_model;
use llvm;

use std::ascii;
use std::char;
use std::env;
use std::fmt;
use std::fs;
use std::io;
use std::iter;
use std::path::{Path, PathBuf};
use std::process::{Output, Stdio};
use std::str;
use syntax::attr;

pub use rustc_codegen_utils::link::{find_crate_name, filename_for_input, default_output_for_target,
invalid_output_for_target, out_filename, check_file_is_writeable,
filename_for_metadata};

// The third parameter is for env vars, used on windows to set up the
// path for MSVC to find its DLLs, and gcc to find its bundled
// toolchain
pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathBuf, Command) {
let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe");

// If our linker looks like a batch script on Windows then to execute this
// we'll need to spawn `cmd` explicitly. This is primarily done to handle
// emscripten where the linker is `emcc.bat` and needs to be spawned as
// `cmd /c emcc.bat ...`.
//
// This worked historically but is needed manually since #42436 (regression
// was tagged as #42791) and some more info can be found on #44443 for
// emscripten itself.
let mut cmd = match linker.to_str() {
Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker),
_ => match flavor {
LinkerFlavor::Lld(f) => Command::lld(linker, f),
LinkerFlavor::Msvc
if sess.opts.cg.linker.is_none() && sess.target.target.options.linker.is_none() =>
{
Command::new(msvc_tool.as_ref().map(|t| t.path()).unwrap_or(linker))
},
_ => Command::new(linker),
}
};

// The compiler's sysroot often has some bundled tools, so add it to the
// PATH for the child.
let mut new_path = sess.host_filesearch(PathKind::All)
.get_tools_search_paths();
let mut msvc_changed_path = false;
if sess.target.target.options.is_like_msvc {
if let Some(ref tool) = msvc_tool {
cmd.args(tool.args());
for &(ref k, ref v) in tool.env() {
if k == "PATH" {
new_path.extend(env::split_paths(v));
msvc_changed_path = true;
} else {
cmd.env(k, v);
}
}
}
}

if !msvc_changed_path {
if let Some(path) = env::var_os("PATH") {
new_path.extend(env::split_paths(&path));
}
}
cmd.env("PATH", env::join_paths(new_path).unwrap());

(linker.to_path_buf(), cmd)
}
pub use rustc_codegen_utils::link::{
find_crate_name, filename_for_input, default_output_for_target, invalid_output_for_target,
out_filename, check_file_is_writeable, filename_for_metadata, get_linker, linker_and_flavor,
exec_linker,
};

pub fn remove(sess: &Session, path: &Path) {
match fs::remove_file(path) {
Expand Down Expand Up @@ -584,69 +525,6 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) {
}
}

pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
fn infer_from(
sess: &Session,
linker: Option<PathBuf>,
flavor: Option<LinkerFlavor>,
) -> Option<(PathBuf, LinkerFlavor)> {
match (linker, flavor) {
(Some(linker), Some(flavor)) => Some((linker, flavor)),
// only the linker flavor is known; use the default linker for the selected flavor
(None, Some(flavor)) => Some((PathBuf::from(match flavor {
LinkerFlavor::Em => if cfg!(windows) { "emcc.bat" } else { "emcc" },
LinkerFlavor::Gcc => "cc",
LinkerFlavor::Ld => "ld",
LinkerFlavor::Msvc => "link.exe",
LinkerFlavor::Lld(_) => "lld",
}), flavor)),
(Some(linker), None) => {
let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
sess.fatal("couldn't extract file stem from specified linker");
}).to_owned();

let flavor = if stem == "emcc" {
LinkerFlavor::Em
} else if stem == "gcc" || stem.ends_with("-gcc") {
LinkerFlavor::Gcc
} else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") {
LinkerFlavor::Ld
} else if stem == "link" || stem == "lld-link" {
LinkerFlavor::Msvc
} else if stem == "lld" || stem == "rust-lld" {
LinkerFlavor::Lld(sess.target.target.options.lld_flavor)
} else {
// fall back to the value in the target spec
sess.target.target.linker_flavor
};

Some((linker, flavor))
},
(None, None) => None,
}
}

// linker and linker flavor specified via command line have precedence over what the target
// specification specifies
if let Some(ret) = infer_from(
sess,
sess.opts.cg.linker.clone(),
sess.opts.debugging_opts.linker_flavor,
) {
return ret;
}

if let Some(ret) = infer_from(
sess,
sess.target.target.options.linker.clone().map(PathBuf::from),
Some(sess.target.target.linker_flavor),
) {
return ret;
}

bug!("Not enough information provided to determine how to invoke the linker");
}

// Create a dynamic library or executable
//
// This will invoke the system linker/cc to create the resulting file. This
Expand Down Expand Up @@ -881,148 +759,6 @@ fn link_natively(sess: &Session,
}
}

fn exec_linker(sess: &Session, cmd: &mut Command, out_filename: &Path, tmpdir: &Path)
-> io::Result<Output>
{
// When attempting to spawn the linker we run a risk of blowing out the
// size limits for spawning a new process with respect to the arguments
// we pass on the command line.
//
// Here we attempt to handle errors from the OS saying "your list of
// arguments is too big" by reinvoking the linker again with an `@`-file
// that contains all the arguments. The theory is that this is then
// accepted on all linkers and the linker will read all its options out of
// there instead of looking at the command line.
if !cmd.very_likely_to_exceed_some_spawn_limit() {
match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() {
Ok(child) => {
let output = child.wait_with_output();
flush_linked_file(&output, out_filename)?;
return output;
}
Err(ref e) if command_line_too_big(e) => {
info!("command line to linker was too big: {}", e);
}
Err(e) => return Err(e)
}
}

info!("falling back to passing arguments to linker via an @-file");
let mut cmd2 = cmd.clone();
let mut args = String::new();
for arg in cmd2.take_args() {
args.push_str(&Escape {
arg: arg.to_str().unwrap(),
is_like_msvc: sess.target.target.options.is_like_msvc,
}.to_string());
args.push_str("\n");
}
let file = tmpdir.join("linker-arguments");
let bytes = if sess.target.target.options.is_like_msvc {
let mut out = Vec::with_capacity((1 + args.len()) * 2);
// start the stream with a UTF-16 BOM
for c in iter::once(0xFEFF).chain(args.encode_utf16()) {
// encode in little endian
out.push(c as u8);
out.push((c >> 8) as u8);
}
out
} else {
args.into_bytes()
};
fs::write(&file, &bytes)?;
cmd2.arg(format!("@{}", file.display()));
info!("invoking linker {:?}", cmd2);
let output = cmd2.output();
flush_linked_file(&output, out_filename)?;
return output;

#[cfg(unix)]
fn flush_linked_file(_: &io::Result<Output>, _: &Path) -> io::Result<()> {
Ok(())
}

#[cfg(windows)]
fn flush_linked_file(command_output: &io::Result<Output>, out_filename: &Path)
-> io::Result<()>
{
// On Windows, under high I/O load, output buffers are sometimes not flushed,
// even long after process exit, causing nasty, non-reproducible output bugs.
//
// File::sync_all() calls FlushFileBuffers() down the line, which solves the problem.
//
// А full writeup of the original Chrome bug can be found at
// randomascii.wordpress.com/2018/02/25/compiler-bug-linker-bug-windows-kernel-bug/amp

if let &Ok(ref out) = command_output {
if out.status.success() {
if let Ok(of) = fs::OpenOptions::new().write(true).open(out_filename) {
of.sync_all()?;
}
}
}

Ok(())
}

#[cfg(unix)]
fn command_line_too_big(err: &io::Error) -> bool {
err.raw_os_error() == Some(::libc::E2BIG)
}

#[cfg(windows)]
fn command_line_too_big(err: &io::Error) -> bool {
const ERROR_FILENAME_EXCED_RANGE: i32 = 206;
err.raw_os_error() == Some(ERROR_FILENAME_EXCED_RANGE)
}

struct Escape<'a> {
arg: &'a str,
is_like_msvc: bool,
}

impl<'a> fmt::Display for Escape<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_like_msvc {
// This is "documented" at
// https://msdn.microsoft.com/en-us/library/4xdcbak7.aspx
//
// Unfortunately there's not a great specification of the
// syntax I could find online (at least) but some local
// testing showed that this seemed sufficient-ish to catch
// at least a few edge cases.
write!(f, "\"")?;
for c in self.arg.chars() {
match c {
'"' => write!(f, "\\{}", c)?,
c => write!(f, "{}", c)?,
}
}
write!(f, "\"")?;
} else {
// This is documented at https://linux.die.net/man/1/ld, namely:
//
// > Options in file are separated by whitespace. A whitespace
// > character may be included in an option by surrounding the
// > entire option in either single or double quotes. Any
// > character (including a backslash) may be included by
// > prefixing the character to be included with a backslash.
//
// We put an argument on each line, so all we need to do is
// ensure the line is interpreted as one whole argument.
for c in self.arg.chars() {
match c {
'\\' |
' ' => write!(f, "\\{}", c)?,
c => write!(f, "{}", c)?,
}
}
}
Ok(())
}
}
}

fn link_args(cmd: &mut dyn Linker,
flavor: LinkerFlavor,
sess: &Session,
Expand Down
1 change: 0 additions & 1 deletion src/librustc_codegen_llvm/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ mod back {
pub mod link;
pub mod lto;
pub mod write;
mod rpath;
pub mod wasm;
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc_codegen_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ crate-type = ["dylib"]
test = false

[dependencies]
cc = "1.0.1"
flate2 = "1.0"
log = "0.4"

Expand Down
6 changes: 5 additions & 1 deletion src/librustc_codegen_utils/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@
#![allow(unused_attributes)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]
#![feature(libc)]

#![recursion_limit="256"]

extern crate cc;
extern crate flate2;
#[macro_use]
extern crate log;
extern crate libc;

extern crate serialize;
#[macro_use]
Expand All @@ -47,10 +50,11 @@ use std::path::PathBuf;
use rustc::session::Session;
use rustc::ty::TyCtxt;

pub mod codegen_backend;
pub mod command;
pub mod link;
pub mod linker;
pub mod codegen_backend;
pub mod rpath;
pub mod symbol_export;
pub mod symbol_names;
pub mod symbol_names_test;
Expand Down
Loading