Skip to content

Commit

Permalink
Auto merge of #50105 - mixi:crt-included, r=alexcrichton
Browse files Browse the repository at this point in the history
Use the correct crt*.o files when linking musl targets.

This is supposed to support optionally using the system copy of musl
libc instead of the included one if supported. This currently only
affects the start files, which is enough to allow building rustc on musl
targets.

Most of the changes are analogous to crt-static.

Excluding the start files is something musl based distributions usually patch into their copy of rustc:
  - https://github.com/alpinelinux/aports/blob/eb064c8/community/rust/musl-fix-linux_musl_base.patch
  - https://github.com/voidlinux/void-packages/blob/77400fc/srcpkgs/rust/patches/link-musl-dynamically.patch

For third-party distributions that not yet carry those patches it would be nice if it was supported without the need to patch upstream sources.

## Reasons
### What breaks?
Some start files were missed when originally writing the logic to swap in musl start files (gcc comes with its own start files, which are suppressed by -nostdlib, but not manually included later on). This caused #36710, which also affects rustc with the internal llvm copy or any other system libraries that need crtbegin/crtend.

### How is it fixed?
The system linker already has all the logic to decide which start files to include, so we can just defer to it (except of course if it doesn't target musl).

### Why is it optional?
In #40113 it was first tried to remove the start files, which broke compiling musl-targeting static binaries with a glibc-targeting compiler. This is why it eventually landed without removing the start files. Being an option side-steps the issue.

### Why are the start files still installed?
This has the nice side-effect, that the produced rust-std-* binaries can still be used by on a glibc-targeting system with a rustc built against glibc.

## Does it work?
With the following build script (using [musl-cross-make](https://github.com/richfelker/musl-cross-make)): https://shadowice.org/~mixi/rust-musl/build.sh, I was able to cross-compile a musl-host musl-targeting rustc on a glibc-based system. The resulting binaries are at https://shadowice.org/~mixi/rust-musl/binaries/. This also requires #50103 and #50104 (which are also applied to the branch the build script uses).
  • Loading branch information
bors committed May 11, 2018
2 parents 6ecbe9b + 490d050 commit 0cd4650
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 15 deletions.
9 changes: 9 additions & 0 deletions src/bootstrap/bin/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,15 @@ fn main() {
if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
cmd.arg(format!("-Clinker={}", host_linker));
}

if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
if s == "true" {
cmd.arg("-C").arg("target-feature=+crt-static");
}
if s == "false" {
cmd.arg("-C").arg("target-feature=-crt-static");
}
}
}

if env::var_os("RUSTC_PARALLEL_QUERIES").is_some() {
Expand Down
33 changes: 30 additions & 3 deletions src/bootstrap/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ def bin_root(self):
"""
return os.path.join(self.build_dir, self.build, "stage0")

def get_toml(self, key):
def get_toml(self, key, section=None):
"""Returns the value of the given key in config.toml, otherwise returns None
>>> rb = RustBuild()
Expand All @@ -501,12 +501,29 @@ def get_toml(self, key):
>>> rb.get_toml("key3") is None
True
Optionally also matches the section the key appears in
>>> rb.config_toml = '[a]\\nkey = "value1"\\n[b]\\nkey = "value2"'
>>> rb.get_toml('key', 'a')
'value1'
>>> rb.get_toml('key', 'b')
'value2'
>>> rb.get_toml('key', 'c') is None
True
"""

cur_section = None
for line in self.config_toml.splitlines():
section_match = re.match(r'^\s*\[(.*)\]\s*$', line)
if section_match is not None:
cur_section = section_match.group(1)

match = re.match(r'^{}\s*=(.*)$'.format(key), line)
if match is not None:
value = match.group(1)
return self.get_string(value) or value.strip()
if section is None or section == cur_section:
return self.get_string(value) or value.strip()
return None

def cargo(self):
Expand Down Expand Up @@ -589,7 +606,17 @@ def build_bootstrap(self):
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
(os.pathsep + env["LIBRARY_PATH"]) \
if "LIBRARY_PATH" in env else ""
env["RUSTFLAGS"] = "-Cdebuginfo=2"
env["RUSTFLAGS"] = "-Cdebuginfo=2 "

build_section = "target.{}".format(self.build_triple())
target_features = []
if self.get_toml("crt-static", build_section) == "true":
target_features += ["+crt-static"]
elif self.get_toml("crt-static", build_section) == "false":
target_features += ["-crt-static"]
if target_features:
env["RUSTFLAGS"] += "-C target-feature=" + (",".join(target_features)) + " "

env["PATH"] = os.path.join(self.bin_root(), "bin") + \
os.pathsep + env["PATH"]
if not os.path.isfile(self.cargo()):
Expand Down
4 changes: 4 additions & 0 deletions src/bootstrap/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,10 @@ impl<'a> Builder<'a> {
cargo.env("RUSTC_CRT_STATIC", x.to_string());
}

if let Some(x) = self.crt_static(compiler.host) {
cargo.env("RUSTC_HOST_CRT_STATIC", x.to_string());
}

// Enable usage of unstable features
cargo.env("RUSTC_BOOTSTRAP", "1");
self.add_rust_test_threads(&mut cargo);
Expand Down
11 changes: 7 additions & 4 deletions src/librustc_target/spec/linux_musl_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ pub fn opts() -> TargetOptions {

// Make sure that the linker/gcc really don't pull in anything, including
// default objects, libs, etc.
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-nostdlib".to_string());
base.pre_link_args_crt.insert(LinkerFlavor::Gcc, Vec::new());
base.pre_link_args_crt.get_mut(&LinkerFlavor::Gcc).unwrap().push("-nostdlib".to_string());

// At least when this was tested, the linker would not add the
// `GNU_EH_FRAME` program header to executables generated, which is required
Expand Down Expand Up @@ -55,9 +56,11 @@ pub fn opts() -> TargetOptions {
//
// Each target directory for musl has these object files included in it so
// they'll be included from there.
base.pre_link_objects_exe.push("crt1.o".to_string());
base.pre_link_objects_exe.push("crti.o".to_string());
base.post_link_objects.push("crtn.o".to_string());
base.pre_link_objects_exe_crt.push("crt1.o".to_string());
base.pre_link_objects_exe_crt.push("crti.o".to_string());
base.pre_link_objects_exe_crt_sys.push("crtbegin.o".to_string());
base.post_link_objects_crt_sys.push("crtend.o".to_string());
base.post_link_objects_crt.push("crtn.o".to_string());

// These targets statically link libc by default
base.crt_static_default = true;
Expand Down
35 changes: 28 additions & 7 deletions src/librustc_target/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,20 +421,26 @@ pub struct TargetOptions {
/// Linker to invoke
pub linker: Option<String>,

/// Linker arguments that are unconditionally passed *before* any
/// user-defined libraries.
pub pre_link_args: LinkArgs,
/// Objects to link before all others, always found within the
/// Linker arguments that are passed *before* any user-defined libraries.
pub pre_link_args: LinkArgs, // ... unconditionally
pub pre_link_args_crt: LinkArgs, // ... when linking with a bundled crt
/// Objects to link before all others, all except *_sys found within the
/// sysroot folder.
pub pre_link_objects_exe: Vec<String>, // ... when linking an executable
pub pre_link_objects_exe: Vec<String>, // ... when linking an executable, unconditionally
pub pre_link_objects_exe_crt: Vec<String>, // ... when linking an executable with a bundled crt
pub pre_link_objects_exe_crt_sys: Vec<String>, // ... when linking an executable with a bundled
// crt, from the system library search path
pub pre_link_objects_dll: Vec<String>, // ... when linking a dylib
/// Linker arguments that are unconditionally passed after any
/// user-defined but before post_link_objects. Standard platform
/// libraries that should be always be linked to, usually go here.
pub late_link_args: LinkArgs,
/// Objects to link after all others, always found within the
/// Objects to link after all others, all except *_sys found within the
/// sysroot folder.
pub post_link_objects: Vec<String>,
pub post_link_objects: Vec<String>, // ... unconditionally
pub post_link_objects_crt: Vec<String>, // ... when linking with a bundled crt
pub post_link_objects_crt_sys: Vec<String>, // ... when linking with a bundled crt, from the
// system library search path
/// Linker arguments that are unconditionally passed *after* any
/// user-defined libraries.
pub post_link_args: LinkArgs,
Expand Down Expand Up @@ -634,6 +640,7 @@ impl Default for TargetOptions {
is_builtin: false,
linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()),
pre_link_args: LinkArgs::new(),
pre_link_args_crt: LinkArgs::new(),
post_link_args: LinkArgs::new(),
asm_args: Vec::new(),
cpu: "generic".to_string(),
Expand Down Expand Up @@ -667,8 +674,12 @@ impl Default for TargetOptions {
position_independent_executables: false,
relro_level: RelroLevel::None,
pre_link_objects_exe: Vec::new(),
pre_link_objects_exe_crt: Vec::new(),
pre_link_objects_exe_crt_sys: Vec::new(),
pre_link_objects_dll: Vec::new(),
post_link_objects: Vec::new(),
post_link_objects_crt: Vec::new(),
post_link_objects_crt_sys: Vec::new(),
late_link_args: LinkArgs::new(),
link_env: Vec::new(),
archive_format: "gnu".to_string(),
Expand Down Expand Up @@ -887,10 +898,15 @@ impl Target {
key!(is_builtin, bool);
key!(linker, optional);
key!(pre_link_args, link_args);
key!(pre_link_args_crt, link_args);
key!(pre_link_objects_exe, list);
key!(pre_link_objects_exe_crt, list);
key!(pre_link_objects_exe_crt_sys, list);
key!(pre_link_objects_dll, list);
key!(late_link_args, link_args);
key!(post_link_objects, list);
key!(post_link_objects_crt, list);
key!(post_link_objects_crt_sys, list);
key!(post_link_args, link_args);
key!(link_env, env);
key!(asm_args, list);
Expand Down Expand Up @@ -1092,10 +1108,15 @@ impl ToJson for Target {
target_option_val!(is_builtin);
target_option_val!(linker);
target_option_val!(link_args - pre_link_args);
target_option_val!(link_args - pre_link_args_crt);
target_option_val!(pre_link_objects_exe);
target_option_val!(pre_link_objects_exe_crt);
target_option_val!(pre_link_objects_exe_crt_sys);
target_option_val!(pre_link_objects_dll);
target_option_val!(link_args - late_link_args);
target_option_val!(post_link_objects);
target_option_val!(post_link_objects_crt);
target_option_val!(post_link_objects_crt_sys);
target_option_val!(link_args - post_link_args);
target_option_val!(env - link_env);
target_option_val!(asm_args);
Expand Down
27 changes: 27 additions & 0 deletions src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,11 @@ fn link_natively(sess: &Session,
if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {
cmd.args(args);
}
if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) {
if sess.crt_static() {
cmd.args(args);
}
}
if let Some(ref args) = sess.opts.debugging_opts.pre_link_args {
cmd.args(args);
}
Expand All @@ -635,6 +640,18 @@ fn link_natively(sess: &Session,
cmd.arg(root.join(obj));
}

if crate_type == config::CrateTypeExecutable && sess.crt_static() {
for obj in &sess.target.target.options.pre_link_objects_exe_crt {
cmd.arg(root.join(obj));
}

for obj in &sess.target.target.options.pre_link_objects_exe_crt_sys {
if flavor == LinkerFlavor::Gcc {
cmd.arg(format!("-l:{}", obj));
}
}
}

if sess.target.target.options.is_like_emscripten {
cmd.arg("-s");
cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort {
Expand All @@ -656,6 +673,16 @@ fn link_natively(sess: &Session,
for obj in &sess.target.target.options.post_link_objects {
cmd.arg(root.join(obj));
}
if sess.crt_static() {
for obj in &sess.target.target.options.post_link_objects_crt_sys {
if flavor == LinkerFlavor::Gcc {
cmd.arg(format!("-l:{}", obj));
}
}
for obj in &sess.target.target.options.post_link_objects_crt {
cmd.arg(root.join(obj));
}
}
if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) {
cmd.args(args);
}
Expand Down
12 changes: 12 additions & 0 deletions src/test/run-make-fulldeps/issue-36710/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-include ../tools.mk

all: foo
$(call RUN,foo)

foo: foo.rs $(call NATIVE_STATICLIB,foo)
$(RUSTC) $< -lfoo $(EXTRACXXFLAGS)

$(TMPDIR)/libfoo.o: foo.cpp
$(call COMPILE_OBJ_CXX,$@,$<)

.PHONY: all
25 changes: 25 additions & 0 deletions src/test/run-make-fulldeps/issue-36710/foo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2018 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.

#include <stdint.h>

struct A {
A() { v = 1234; }
~A() { v = 1; }
uint32_t v;
};

A a;

extern "C" {
uint32_t get() {
return a.v;
}
}
18 changes: 18 additions & 0 deletions src/test/run-make-fulldeps/issue-36710/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2018 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.

// Tests that linking to C++ code with global destructors works.

extern { fn get() -> u32; }

fn main() {
let i = unsafe { get() };
assert_eq!(i, 1234);
}
2 changes: 2 additions & 0 deletions src/test/run-make-fulldeps/tools.mk
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ endif

ifdef IS_MSVC
COMPILE_OBJ = $(CC) -c -Fo:`cygpath -w $(1)` $(2)
COMPILE_OBJ_CXX = $(CXX) -c -Fo:`cygpath -w $(1)` $(2)
NATIVE_STATICLIB_FILE = $(1).lib
NATIVE_STATICLIB = $(TMPDIR)/$(call NATIVE_STATICLIB_FILE,$(1))
OUT_EXE=-Fe:`cygpath -w $(TMPDIR)/$(call BIN,$(1))` \
-Fo:`cygpath -w $(TMPDIR)/$(1).obj`
else
COMPILE_OBJ = $(CC) -c -o $(1) $(2)
COMPILE_OBJ_CXX = $(CXX) -c -o $(1) $(2)
NATIVE_STATICLIB_FILE = lib$(1).a
NATIVE_STATICLIB = $(call STATICLIB,$(1))
OUT_EXE=-o $(TMPDIR)/$(1)
Expand Down
2 changes: 1 addition & 1 deletion src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2524,7 +2524,7 @@ impl<'test> TestCx<'test> {
.env("IS_WINDOWS", "1")
.env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
.env("CC", format!("'{}' {}", self.config.cc, cflags))
.env("CXX", &self.config.cxx);
.env("CXX", format!("'{}'", &self.config.cxx));
} else {
cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
.env("CXX", format!("{} {}", self.config.cxx, self.config.cflags))
Expand Down

0 comments on commit 0cd4650

Please sign in to comment.