From: Shiz Date: Fri, 21 Apr 2017 01:04:46 +0200 Subject: [PATCH] Support fully static linking on *nix targets This patch adds support for full static linking on *nix targets. It adds `Session::fully_static()` to determine whether full static linking should be utilised. By default, this is the case if the target is not MSVC-like and the `crt-static` target feature is requested, as for *nix targets this implies a fully static result. In the future, a target feature or other compile option could perhaps be added to have the invoker decide this more flexibly at run-time. It also adds the proper linker argument for static result objects to `Linker` and implements them for `GnuLinker` and `MsvcLinker`. Additionally, when statically linking, all the objects are linked in a group (-Wl,-( and -Wl,-) on GNU-compatible linkers) to resolve dependency and order issues that may normally arise. For `MsvcLinker`, this is a no-op as it already exhibits this behavior by default. Finally, if no linking preference is given for native libraries (`NativeLibraryKind::NativeUnknown`), they are linked statically if full static linking is requested, instead of dynamically as before. --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -409,6 +409,11 @@ return crt_static; } + pub fn fully_static(&self) -> bool { + // TODO: figure out better semantics for this, possibly a target option? + return self.crt_static() && !self.target.target.options.is_like_msvc + } + pub fn must_not_eliminate_frame_pointers(&self) -> bool { self.opts.debuginfo != DebugInfoLevel::NoDebugInfo || !self.target.target.options.eliminate_frame_pointer --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -239,8 +239,8 @@ /// Checks if target supports crate_type as output pub fn invalid_output_for_target(sess: &Session, crate_type: config::CrateType) -> bool { - match (sess.target.target.options.dynamic_linking, - sess.target.target.options.executables, crate_type) { + let dynamic_linking = sess.target.target.options.dynamic_linking && !sess.fully_static(); + match (dynamic_linking, sess.target.target.options.executables, crate_type) { (false, _, config::CrateTypeCdylib) | (false, _, config::CrateTypeProcMacro) | (false, _, config::CrateTypeDylib) => true, @@ -840,6 +840,10 @@ let used_link_args = sess.cstore.used_link_args(); + if crate_type == config::CrateTypeExecutable && sess.fully_static() { + cmd.static_executable(); + } + if crate_type == config::CrateTypeExecutable && t.options.position_independent_executables { let empty_vec = Vec::new(); @@ -870,15 +870,8 @@ cmd.no_default_libraries(); } - // Take careful note of the ordering of the arguments we pass to the linker - // here. Linkers will assume that things on the left depend on things to the - // right. Things on the right cannot depend on things on the left. This is - // all formally implemented in terms of resolving symbols (libs on the right - // resolve unknown symbols of libs on the left, but not vice versa). + // We have organized the arguments we pass to the linker as such: // - // For this reason, we have organized the arguments we pass to the linker as - // such: - // // 1. The local object that LLVM just generated // 2. Local native libraries // 3. Upstream rust libraries --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -951,17 +951,12 @@ // list can't depend on items higher up in the list. For example nothing can // depend on what we just generated (e.g. that'd be a circular dependency). // Upstream rust libraries are not allowed to depend on our local native - // libraries as that would violate the structure of the DAG, in that - // scenario they are required to link to them as well in a shared fashion. - // - // Note that upstream rust libraries may contain native dependencies as - // well, but they also can't depend on what we just started to add to the - // link line. And finally upstream native libraries can't depend on anything - // in this DAG so far because they're only dylibs and dylibs can only depend - // on other dylibs (e.g. other native deps). + // libraries as that would violate the structure of the DAG. + cmd.start_group(); add_local_native_libraries(cmd, sess); add_upstream_rust_crates(cmd, sess, crate_type, tmpdir); add_upstream_native_libraries(cmd, sess, crate_type); + cmd.end_group(); // # Telling the linker what we're doing @@ -983,11 +983,14 @@ cmd.link_whole_staticlib(&l.name.as_str(), &search_path); } - cmd.hint_dynamic(); + let fully_static = sess.fully_static(); + if !fully_static { + cmd.hint_dynamic(); + } for lib in others { match lib.kind { - NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()), + NativeLibraryKind::NativeUnknown => if fully_static { cmd.link_staticlib(&lib.name.as_str()) } else { cmd.link_dylib(&lib.name.as_str()) }, NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()), NativeLibraryKind::NativeStatic => bug!(), } --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -82,6 +82,7 @@ fn add_object(&mut self, path: &Path); fn gc_sections(&mut self, keep_metadata: bool); fn position_independent_executable(&mut self); + fn static_executable(&mut self); fn optimize(&mut self); fn debuginfo(&mut self); fn no_default_libraries(&mut self); @@ -93,6 +93,8 @@ fn no_whole_archives(&mut self); fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); fn subsystem(&mut self, subsystem: &str); + fn start_group(&mut self); + fn end_group(&mut self); } pub struct GnuLinker<'a> { @@ -116,6 +117,9 @@ fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn static_executable(&mut self) { self.cmd.arg("-static"); } + fn start_group(&mut self) { self.cmd.arg("-Wl,-("); } + fn end_group(&mut self) { self.cmd.arg("-Wl,-)"); } fn args(&mut self, args: &[String]) { self.cmd.args(args); } fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { @@ -359,6 +361,10 @@ fn position_independent_executable(&mut self) { // noop + } + + fn static_executable(&mut self) { + self.cmd.arg("-MT"); } fn no_default_libraries(&mut self) { @@ -484,6 +488,14 @@ if subsystem == "windows" { self.cmd.arg("/ENTRY:mainCRTStartup"); } + } + + fn start_group(&mut self) { + // Not needed + } + + fn end_group(&mut self) { + // Not needed } } @@ -562,6 +562,10 @@ // noop } + fn static_executable(&mut self) { + // noop + } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } @@ -657,6 +661,14 @@ fn subsystem(&mut self, _subsystem: &str) { // noop + } + + fn start_group(&mut self) { + self.cmd.arg("-Wl,-("); + } + + fn end_group(&mut self) { + self.cmd.arg("-Wl,-)"); } }