Skip to content

Commit 0cd4650

Browse files
committed
Auto merge of #50105 - mixi:crt-included, r=alexcrichton
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).
2 parents 6ecbe9b + 490d050 commit 0cd4650

File tree

11 files changed

+163
-15
lines changed

11 files changed

+163
-15
lines changed

src/bootstrap/bin/rustc.rs

+9
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,15 @@ fn main() {
268268
if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
269269
cmd.arg(format!("-Clinker={}", host_linker));
270270
}
271+
272+
if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {
273+
if s == "true" {
274+
cmd.arg("-C").arg("target-feature=+crt-static");
275+
}
276+
if s == "false" {
277+
cmd.arg("-C").arg("target-feature=-crt-static");
278+
}
279+
}
271280
}
272281

273282
if env::var_os("RUSTC_PARALLEL_QUERIES").is_some() {

src/bootstrap/bootstrap.py

+30-3
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ def bin_root(self):
489489
"""
490490
return os.path.join(self.build_dir, self.build, "stage0")
491491

492-
def get_toml(self, key):
492+
def get_toml(self, key, section=None):
493493
"""Returns the value of the given key in config.toml, otherwise returns None
494494
495495
>>> rb = RustBuild()
@@ -501,12 +501,29 @@ def get_toml(self, key):
501501
502502
>>> rb.get_toml("key3") is None
503503
True
504+
505+
Optionally also matches the section the key appears in
506+
507+
>>> rb.config_toml = '[a]\\nkey = "value1"\\n[b]\\nkey = "value2"'
508+
>>> rb.get_toml('key', 'a')
509+
'value1'
510+
>>> rb.get_toml('key', 'b')
511+
'value2'
512+
>>> rb.get_toml('key', 'c') is None
513+
True
504514
"""
515+
516+
cur_section = None
505517
for line in self.config_toml.splitlines():
518+
section_match = re.match(r'^\s*\[(.*)\]\s*$', line)
519+
if section_match is not None:
520+
cur_section = section_match.group(1)
521+
506522
match = re.match(r'^{}\s*=(.*)$'.format(key), line)
507523
if match is not None:
508524
value = match.group(1)
509-
return self.get_string(value) or value.strip()
525+
if section is None or section == cur_section:
526+
return self.get_string(value) or value.strip()
510527
return None
511528

512529
def cargo(self):
@@ -589,7 +606,17 @@ def build_bootstrap(self):
589606
env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
590607
(os.pathsep + env["LIBRARY_PATH"]) \
591608
if "LIBRARY_PATH" in env else ""
592-
env["RUSTFLAGS"] = "-Cdebuginfo=2"
609+
env["RUSTFLAGS"] = "-Cdebuginfo=2 "
610+
611+
build_section = "target.{}".format(self.build_triple())
612+
target_features = []
613+
if self.get_toml("crt-static", build_section) == "true":
614+
target_features += ["+crt-static"]
615+
elif self.get_toml("crt-static", build_section) == "false":
616+
target_features += ["-crt-static"]
617+
if target_features:
618+
env["RUSTFLAGS"] += "-C target-feature=" + (",".join(target_features)) + " "
619+
593620
env["PATH"] = os.path.join(self.bin_root(), "bin") + \
594621
os.pathsep + env["PATH"]
595622
if not os.path.isfile(self.cargo()):

src/bootstrap/builder.rs

+4
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,10 @@ impl<'a> Builder<'a> {
690690
cargo.env("RUSTC_CRT_STATIC", x.to_string());
691691
}
692692

693+
if let Some(x) = self.crt_static(compiler.host) {
694+
cargo.env("RUSTC_HOST_CRT_STATIC", x.to_string());
695+
}
696+
693697
// Enable usage of unstable features
694698
cargo.env("RUSTC_BOOTSTRAP", "1");
695699
self.add_rust_test_threads(&mut cargo);

src/librustc_target/spec/linux_musl_base.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ pub fn opts() -> TargetOptions {
1515

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

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

6265
// These targets statically link libc by default
6366
base.crt_static_default = true;

src/librustc_target/spec/mod.rs

+28-7
Original file line numberDiff line numberDiff line change
@@ -421,20 +421,26 @@ pub struct TargetOptions {
421421
/// Linker to invoke
422422
pub linker: Option<String>,
423423

424-
/// Linker arguments that are unconditionally passed *before* any
425-
/// user-defined libraries.
426-
pub pre_link_args: LinkArgs,
427-
/// Objects to link before all others, always found within the
424+
/// Linker arguments that are passed *before* any user-defined libraries.
425+
pub pre_link_args: LinkArgs, // ... unconditionally
426+
pub pre_link_args_crt: LinkArgs, // ... when linking with a bundled crt
427+
/// Objects to link before all others, all except *_sys found within the
428428
/// sysroot folder.
429-
pub pre_link_objects_exe: Vec<String>, // ... when linking an executable
429+
pub pre_link_objects_exe: Vec<String>, // ... when linking an executable, unconditionally
430+
pub pre_link_objects_exe_crt: Vec<String>, // ... when linking an executable with a bundled crt
431+
pub pre_link_objects_exe_crt_sys: Vec<String>, // ... when linking an executable with a bundled
432+
// crt, from the system library search path
430433
pub pre_link_objects_dll: Vec<String>, // ... when linking a dylib
431434
/// Linker arguments that are unconditionally passed after any
432435
/// user-defined but before post_link_objects. Standard platform
433436
/// libraries that should be always be linked to, usually go here.
434437
pub late_link_args: LinkArgs,
435-
/// Objects to link after all others, always found within the
438+
/// Objects to link after all others, all except *_sys found within the
436439
/// sysroot folder.
437-
pub post_link_objects: Vec<String>,
440+
pub post_link_objects: Vec<String>, // ... unconditionally
441+
pub post_link_objects_crt: Vec<String>, // ... when linking with a bundled crt
442+
pub post_link_objects_crt_sys: Vec<String>, // ... when linking with a bundled crt, from the
443+
// system library search path
438444
/// Linker arguments that are unconditionally passed *after* any
439445
/// user-defined libraries.
440446
pub post_link_args: LinkArgs,
@@ -634,6 +640,7 @@ impl Default for TargetOptions {
634640
is_builtin: false,
635641
linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()),
636642
pre_link_args: LinkArgs::new(),
643+
pre_link_args_crt: LinkArgs::new(),
637644
post_link_args: LinkArgs::new(),
638645
asm_args: Vec::new(),
639646
cpu: "generic".to_string(),
@@ -667,8 +674,12 @@ impl Default for TargetOptions {
667674
position_independent_executables: false,
668675
relro_level: RelroLevel::None,
669676
pre_link_objects_exe: Vec::new(),
677+
pre_link_objects_exe_crt: Vec::new(),
678+
pre_link_objects_exe_crt_sys: Vec::new(),
670679
pre_link_objects_dll: Vec::new(),
671680
post_link_objects: Vec::new(),
681+
post_link_objects_crt: Vec::new(),
682+
post_link_objects_crt_sys: Vec::new(),
672683
late_link_args: LinkArgs::new(),
673684
link_env: Vec::new(),
674685
archive_format: "gnu".to_string(),
@@ -887,10 +898,15 @@ impl Target {
887898
key!(is_builtin, bool);
888899
key!(linker, optional);
889900
key!(pre_link_args, link_args);
901+
key!(pre_link_args_crt, link_args);
890902
key!(pre_link_objects_exe, list);
903+
key!(pre_link_objects_exe_crt, list);
904+
key!(pre_link_objects_exe_crt_sys, list);
891905
key!(pre_link_objects_dll, list);
892906
key!(late_link_args, link_args);
893907
key!(post_link_objects, list);
908+
key!(post_link_objects_crt, list);
909+
key!(post_link_objects_crt_sys, list);
894910
key!(post_link_args, link_args);
895911
key!(link_env, env);
896912
key!(asm_args, list);
@@ -1092,10 +1108,15 @@ impl ToJson for Target {
10921108
target_option_val!(is_builtin);
10931109
target_option_val!(linker);
10941110
target_option_val!(link_args - pre_link_args);
1111+
target_option_val!(link_args - pre_link_args_crt);
10951112
target_option_val!(pre_link_objects_exe);
1113+
target_option_val!(pre_link_objects_exe_crt);
1114+
target_option_val!(pre_link_objects_exe_crt_sys);
10961115
target_option_val!(pre_link_objects_dll);
10971116
target_option_val!(link_args - late_link_args);
10981117
target_option_val!(post_link_objects);
1118+
target_option_val!(post_link_objects_crt);
1119+
target_option_val!(post_link_objects_crt_sys);
10991120
target_option_val!(link_args - post_link_args);
11001121
target_option_val!(env - link_env);
11011122
target_option_val!(asm_args);

src/librustc_trans/back/link.rs

+27
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,11 @@ fn link_natively(sess: &Session,
621621
if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {
622622
cmd.args(args);
623623
}
624+
if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) {
625+
if sess.crt_static() {
626+
cmd.args(args);
627+
}
628+
}
624629
if let Some(ref args) = sess.opts.debugging_opts.pre_link_args {
625630
cmd.args(args);
626631
}
@@ -635,6 +640,18 @@ fn link_natively(sess: &Session,
635640
cmd.arg(root.join(obj));
636641
}
637642

643+
if crate_type == config::CrateTypeExecutable && sess.crt_static() {
644+
for obj in &sess.target.target.options.pre_link_objects_exe_crt {
645+
cmd.arg(root.join(obj));
646+
}
647+
648+
for obj in &sess.target.target.options.pre_link_objects_exe_crt_sys {
649+
if flavor == LinkerFlavor::Gcc {
650+
cmd.arg(format!("-l:{}", obj));
651+
}
652+
}
653+
}
654+
638655
if sess.target.target.options.is_like_emscripten {
639656
cmd.arg("-s");
640657
cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort {
@@ -656,6 +673,16 @@ fn link_natively(sess: &Session,
656673
for obj in &sess.target.target.options.post_link_objects {
657674
cmd.arg(root.join(obj));
658675
}
676+
if sess.crt_static() {
677+
for obj in &sess.target.target.options.post_link_objects_crt_sys {
678+
if flavor == LinkerFlavor::Gcc {
679+
cmd.arg(format!("-l:{}", obj));
680+
}
681+
}
682+
for obj in &sess.target.target.options.post_link_objects_crt {
683+
cmd.arg(root.join(obj));
684+
}
685+
}
659686
if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) {
660687
cmd.args(args);
661688
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-include ../tools.mk
2+
3+
all: foo
4+
$(call RUN,foo)
5+
6+
foo: foo.rs $(call NATIVE_STATICLIB,foo)
7+
$(RUSTC) $< -lfoo $(EXTRACXXFLAGS)
8+
9+
$(TMPDIR)/libfoo.o: foo.cpp
10+
$(call COMPILE_OBJ_CXX,$@,$<)
11+
12+
.PHONY: all
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
#include <stdint.h>
12+
13+
struct A {
14+
A() { v = 1234; }
15+
~A() { v = 1; }
16+
uint32_t v;
17+
};
18+
19+
A a;
20+
21+
extern "C" {
22+
uint32_t get() {
23+
return a.v;
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
// Tests that linking to C++ code with global destructors works.
12+
13+
extern { fn get() -> u32; }
14+
15+
fn main() {
16+
let i = unsafe { get() };
17+
assert_eq!(i, 1234);
18+
}

src/test/run-make-fulldeps/tools.mk

+2
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ endif
5959

6060
ifdef IS_MSVC
6161
COMPILE_OBJ = $(CC) -c -Fo:`cygpath -w $(1)` $(2)
62+
COMPILE_OBJ_CXX = $(CXX) -c -Fo:`cygpath -w $(1)` $(2)
6263
NATIVE_STATICLIB_FILE = $(1).lib
6364
NATIVE_STATICLIB = $(TMPDIR)/$(call NATIVE_STATICLIB_FILE,$(1))
6465
OUT_EXE=-Fe:`cygpath -w $(TMPDIR)/$(call BIN,$(1))` \
6566
-Fo:`cygpath -w $(TMPDIR)/$(1).obj`
6667
else
6768
COMPILE_OBJ = $(CC) -c -o $(1) $(2)
69+
COMPILE_OBJ_CXX = $(CXX) -c -o $(1) $(2)
6870
NATIVE_STATICLIB_FILE = lib$(1).a
6971
NATIVE_STATICLIB = $(call STATICLIB,$(1))
7072
OUT_EXE=-o $(TMPDIR)/$(1)

src/tools/compiletest/src/runtest.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2524,7 +2524,7 @@ impl<'test> TestCx<'test> {
25242524
.env("IS_WINDOWS", "1")
25252525
.env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
25262526
.env("CC", format!("'{}' {}", self.config.cc, cflags))
2527-
.env("CXX", &self.config.cxx);
2527+
.env("CXX", format!("'{}'", &self.config.cxx));
25282528
} else {
25292529
cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
25302530
.env("CXX", format!("{} {}", self.config.cxx, self.config.cflags))

0 commit comments

Comments
 (0)