Auto merge of #44094 - alexcrichton:long-linkers, r=michaelwoerister
rustc: Attempt to handle super long linker invocations This commit adds logic to the compiler to attempt to handle super long linker invocations by falling back to the `@`-file syntax if the invoked command is too large. Each OS has a limit on how many arguments and how large the arguments can be when spawning a new process, and linkers tend to be one of those programs that can hit the limit! The logic implemented here is to unconditionally attempt to spawn a linker and then if it fails to spawn with an error from the OS that indicates the command line is too big we attempt a fallback. The fallback is roughly the same for all linkers where an argument pointing to a file, prepended with `@`, is passed. This file then contains all the various arguments that we want to pass to the linker. Closes #41190
This commit is contained in:
commit
a6a9d4c5fd
7 changed files with 316 additions and 6 deletions
5
src/test/run-make/long-linker-command-lines/Makefile
Normal file
5
src/test/run-make/long-linker-command-lines/Makefile
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
-include ../tools.mk
|
||||
|
||||
all:
|
||||
$(RUSTC) foo.rs -g
|
||||
RUSTC="$(RUSTC_ORIGINAL)" $(call RUN,foo)
|
||||
88
src/test/run-make/long-linker-command-lines/foo.rs
Normal file
88
src/test/run-make/long-linker-command-lines/foo.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// This is a test which attempts to blow out the system limit with how many
|
||||
// arguments can be passed to a process. This'll successively call rustc with
|
||||
// larger and larger argument lists in an attempt to find one that's way too
|
||||
// big for the system at hand. This file itself is then used as a "linker" to
|
||||
// detect when the process creation succeeds.
|
||||
//
|
||||
// Eventually we should see an argument that looks like `@` as we switch from
|
||||
// passing literal arguments to passing everything in the file.
|
||||
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{BufWriter, Write, Read};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let tmpdir = PathBuf::from(env::var_os("TMPDIR").unwrap());
|
||||
let ok = tmpdir.join("ok");
|
||||
if env::var("YOU_ARE_A_LINKER").is_ok() {
|
||||
if let Some(file) = env::args().find(|a| a.contains("@")) {
|
||||
fs::copy(&file[1..], &ok).unwrap();
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let rustc = env::var_os("RUSTC").unwrap_or("rustc".into());
|
||||
let me_as_linker = format!("linker={}", env::current_exe().unwrap().display());
|
||||
for i in (1..).map(|i| i * 100) {
|
||||
println!("attempt: {}", i);
|
||||
let file = tmpdir.join("bar.rs");
|
||||
let mut f = BufWriter::new(File::create(&file).unwrap());
|
||||
let mut lib_name = String::new();
|
||||
for _ in 0..i {
|
||||
lib_name.push_str("foo");
|
||||
}
|
||||
for j in 0..i {
|
||||
writeln!(f, "#[link(name = \"{}{}\")]", lib_name, j).unwrap();
|
||||
}
|
||||
writeln!(f, "extern {{}}\nfn main() {{}}").unwrap();
|
||||
f.into_inner().unwrap();
|
||||
|
||||
drop(fs::remove_file(&ok));
|
||||
let output = Command::new(&rustc)
|
||||
.arg(&file)
|
||||
.arg("-C").arg(&me_as_linker)
|
||||
.arg("--out-dir").arg(&tmpdir)
|
||||
.env("YOU_ARE_A_LINKER", "1")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
panic!("status: {}\nstdout:\n{}\nstderr:\n{}",
|
||||
output.status,
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
stderr.lines().map(|l| {
|
||||
if l.len() > 200 {
|
||||
format!("{}...\n", &l[..200])
|
||||
} else {
|
||||
format!("{}\n", l)
|
||||
}
|
||||
}).collect::<String>());
|
||||
}
|
||||
|
||||
if !ok.exists() {
|
||||
continue
|
||||
}
|
||||
|
||||
let mut contents = String::new();
|
||||
File::open(&ok).unwrap().read_to_string(&mut contents).unwrap();
|
||||
|
||||
for j in 0..i {
|
||||
assert!(contents.contains(&format!("{}{}", lib_name, j)));
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ HOST_RPATH_ENV = \
|
|||
TARGET_RPATH_ENV = \
|
||||
$(LD_LIB_PATH_ENVVAR)="$(TMPDIR):$(TARGET_RPATH_DIR):$($(LD_LIB_PATH_ENVVAR))"
|
||||
|
||||
RUSTC_ORIGINAL := $(RUSTC)
|
||||
BARE_RUSTC := $(HOST_RPATH_ENV) '$(RUSTC)'
|
||||
RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS)
|
||||
#CC := $(CC) -L $(TMPDIR)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue