1 // Copyright 2015 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.
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.
12 use std::ffi::OsString;
13 use std::fs::{self, File};
14 use std::io::{self, BufWriter};
15 use std::io::prelude::*;
16 use std::path::{Path, PathBuf};
17 use std::process::Command;
20 use middle::cstore::CrateStore;
21 use middle::dependency_format::Linkage;
23 use session::config::CrateTypeDylib;
26 use trans::CrateTranslation;
28 /// Linker abstraction used by back::link to build up the command to invoke a
31 /// This trait is the total list of requirements needed by `back::link` and
32 /// represents the meaning of each option being passed down. This trait is then
33 /// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an
34 /// MSVC linker (e.g. `link.exe`) is being used.
36 fn link_dylib(&mut self, lib: &str);
37 fn link_rust_dylib(&mut self, lib: &str, path: &Path);
38 fn link_framework(&mut self, framework: &str);
39 fn link_staticlib(&mut self, lib: &str);
40 fn link_rlib(&mut self, lib: &Path);
41 fn link_whole_rlib(&mut self, lib: &Path);
42 fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]);
43 fn include_path(&mut self, path: &Path);
44 fn framework_path(&mut self, path: &Path);
45 fn output_filename(&mut self, path: &Path);
46 fn add_object(&mut self, path: &Path);
47 fn gc_sections(&mut self, is_dylib: bool);
48 fn position_independent_executable(&mut self);
49 fn optimize(&mut self);
50 fn debuginfo(&mut self);
51 fn no_default_libraries(&mut self);
52 fn build_dylib(&mut self, out_filename: &Path);
53 fn args(&mut self, args: &[String]);
54 fn hint_static(&mut self);
55 fn hint_dynamic(&mut self);
56 fn whole_archives(&mut self);
57 fn no_whole_archives(&mut self);
58 fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation,
60 fn try_gold_linker(&mut self);
63 pub struct GnuLinker<'a> {
64 pub cmd: &'a mut Command,
65 pub sess: &'a Session,
68 impl<'a> GnuLinker<'a> {
69 fn takes_hints(&self) -> bool {
70 !self.sess.target.target.options.is_like_osx
74 impl<'a> Linker for GnuLinker<'a> {
75 fn link_dylib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); }
76 fn link_staticlib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); }
77 fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); }
78 fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); }
79 fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); }
80 fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); }
81 fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
82 fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); }
83 fn args(&mut self, args: &[String]) { self.cmd.args(args); }
85 fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
86 self.cmd.arg("-l").arg(lib);
89 fn link_framework(&mut self, framework: &str) {
90 self.cmd.arg("-framework").arg(framework);
93 fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) {
94 let target = &self.sess.target.target;
95 if !target.options.is_like_osx {
96 self.cmd.arg("-Wl,--whole-archive")
98 .arg("-Wl,--no-whole-archive");
100 // -force_load is the OSX equivalent of --whole-archive, but it
101 // involves passing the full path to the library to link.
102 let mut v = OsString::from("-Wl,-force_load,");
103 v.push(&archive::find_library(lib, search_path, &self.sess));
108 fn link_whole_rlib(&mut self, lib: &Path) {
109 if self.sess.target.target.options.is_like_osx {
110 let mut v = OsString::from("-Wl,-force_load,");
114 self.cmd.arg("-Wl,--whole-archive").arg(lib)
115 .arg("-Wl,--no-whole-archive");
119 fn gc_sections(&mut self, is_dylib: bool) {
120 // The dead_strip option to the linker specifies that functions and data
121 // unreachable by the entry point will be removed. This is quite useful
122 // with Rust's compilation model of compiling libraries at a time into
123 // one object file. For example, this brings hello world from 1.7MB to
126 // Note that this is done for both executables and dynamic libraries. We
127 // won't get much benefit from dylibs because LLVM will have already
128 // stripped away as much as it could. This has not been seen to impact
129 // link times negatively.
131 // -dead_strip can't be part of the pre_link_args because it's also used
132 // for partial linking when using multiple codegen units (-r). So we
134 if self.sess.target.target.options.is_like_osx {
135 self.cmd.arg("-Wl,-dead_strip");
137 // If we're building a dylib, we don't use --gc-sections because LLVM
138 // has already done the best it can do, and we also don't want to
139 // eliminate the metadata. If we're building an executable, however,
140 // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67%
142 } else if !is_dylib {
143 self.cmd.arg("-Wl,--gc-sections");
147 fn optimize(&mut self) {
148 if !self.sess.target.target.options.linker_is_gnu { return }
150 // GNU-style linkers support optimization with -O. GNU ld doesn't
151 // need a numeric argument, but other linkers do.
152 if self.sess.opts.optimize == config::Default ||
153 self.sess.opts.optimize == config::Aggressive {
154 self.cmd.arg("-Wl,-O1");
158 fn debuginfo(&mut self) {
159 // Don't do anything special here for GNU-style linkers.
162 fn no_default_libraries(&mut self) {
163 self.cmd.arg("-nodefaultlibs");
166 fn build_dylib(&mut self, out_filename: &Path) {
167 // On mac we need to tell the linker to let this library be rpathed
168 if self.sess.target.target.options.is_like_osx {
169 self.cmd.args(&["-dynamiclib", "-Wl,-dylib"]);
171 if self.sess.opts.cg.rpath {
172 let mut v = OsString::from("-Wl,-install_name,@rpath/");
173 v.push(out_filename.file_name().unwrap());
177 self.cmd.arg("-shared");
181 fn whole_archives(&mut self) {
182 if !self.takes_hints() { return }
183 self.cmd.arg("-Wl,--whole-archive");
186 fn no_whole_archives(&mut self) {
187 if !self.takes_hints() { return }
188 self.cmd.arg("-Wl,--no-whole-archive");
191 fn hint_static(&mut self) {
192 if !self.takes_hints() { return }
193 self.cmd.arg("-Wl,-Bstatic");
196 fn hint_dynamic(&mut self) {
197 if !self.takes_hints() { return }
198 self.cmd.arg("-Wl,-Bdynamic");
201 fn export_symbols(&mut self, _: &Session, _: &CrateTranslation, _: &Path) {
202 // noop, visibility in object files takes care of this
205 fn try_gold_linker(&mut self) {
206 // Only use gold under specific conditions that we know work
208 let gold_exists = match env::var_os("PATH") {
209 Some(ref env_path) => {
210 env::split_paths(env_path).any(|mut p| {
217 let host_is_linux = cfg!(target_os = "linux");
218 // Defensively prevent trying to use gold for bogus cross-targets.
219 let target_is_host_compatible = {
220 let host_os_is_target_os = self.sess.target.target.target_os == env::consts::OS;
221 let host_arch_is_target_arch = self.sess.target.target.arch == env::consts::ARCH;
222 // Support x86_64->i686 and reverse
223 let host_and_target_are_x86ish =
224 (self.sess.target.target.arch == "x86" ||
225 self.sess.target.target.arch == "x86_64") &&
226 (env::consts::ARCH == "x86" ||
227 env::consts::ARCH == "x86_64");
228 host_os_is_target_os && (host_arch_is_target_arch || host_and_target_are_x86ish)
230 // We have strong confidence that x86 works, but not much
231 // visibility into other architectures.
232 let target_works_with_gold =
233 self.sess.target.target.arch == "x86" ||
234 self.sess.target.target.arch == "x86_64";
235 let opt_out = self.sess.opts.cg.disable_gold;
240 target_is_host_compatible &&
241 target_works_with_gold &&
245 info!("linking with ld.gold");
246 self.cmd.arg("-fuse-ld=gold");
248 info!("linking with ld");
253 pub struct MsvcLinker<'a> {
254 pub cmd: &'a mut Command,
255 pub sess: &'a Session,
258 impl<'a> Linker for MsvcLinker<'a> {
259 fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); }
260 fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
261 fn args(&mut self, args: &[String]) { self.cmd.args(args); }
262 fn build_dylib(&mut self, _out_filename: &Path) { self.cmd.arg("/DLL"); }
263 fn gc_sections(&mut self, _is_dylib: bool) { self.cmd.arg("/OPT:REF,ICF"); }
265 fn link_dylib(&mut self, lib: &str) {
266 self.cmd.arg(&format!("{}.lib", lib));
269 fn link_rust_dylib(&mut self, lib: &str, path: &Path) {
270 // When producing a dll, the MSVC linker may not actually emit a
271 // `foo.lib` file if the dll doesn't actually export any symbols, so we
272 // check to see if the file is there and just omit linking to it if it's
274 let name = format!("{}.lib", lib);
275 if fs::metadata(&path.join(&name)).is_ok() {
280 fn link_staticlib(&mut self, lib: &str) {
281 self.cmd.arg(&format!("{}.lib", lib));
284 fn position_independent_executable(&mut self) {
288 fn no_default_libraries(&mut self) {
289 // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC
290 // as there's been trouble in the past of linking the C++ standard
291 // library required by LLVM. This likely needs to happen one day, but
292 // in general Windows is also a more controlled environment than
293 // Unix, so it's not necessarily as critical that this be implemented.
295 // Note that there are also some licensing worries about statically
296 // linking some libraries which require a specific agreement, so it may
297 // not ever be possible for us to pass this flag.
300 fn include_path(&mut self, path: &Path) {
301 let mut arg = OsString::from("/LIBPATH:");
306 fn output_filename(&mut self, path: &Path) {
307 let mut arg = OsString::from("/OUT:");
312 fn framework_path(&mut self, _path: &Path) {
313 panic!("frameworks are not supported on windows")
315 fn link_framework(&mut self, _framework: &str) {
316 panic!("frameworks are not supported on windows")
319 fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) {
321 self.link_staticlib(lib);
323 fn link_whole_rlib(&mut self, path: &Path) {
325 self.link_rlib(path);
327 fn optimize(&mut self) {
328 // Needs more investigation of `/OPT` arguments
331 fn debuginfo(&mut self) {
332 // This will cause the Microsoft linker to generate a PDB file
333 // from the CodeView line tables in the object files.
334 self.cmd.arg("/DEBUG");
337 fn whole_archives(&mut self) {
338 // hints not supported?
340 fn no_whole_archives(&mut self) {
341 // hints not supported?
344 // On windows static libraries are of the form `foo.lib` and dynamic
345 // libraries are not linked against directly, but rather through their
346 // import libraries also called `foo.lib`. As a result there's no
347 // possibility for a native library to appear both dynamically and
348 // statically in the same folder so we don't have to worry about hints like
349 // we do on Unix platforms.
350 fn hint_static(&mut self) {}
351 fn hint_dynamic(&mut self) {}
353 // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to
354 // export symbols from a dynamic library. When building a dynamic library,
355 // however, we're going to want some symbols exported, so this function
356 // generates a DEF file which lists all the symbols.
358 // The linker will read this `*.def` file and export all the symbols from
359 // the dynamic library. Note that this is not as simple as just exporting
360 // all the symbols in the current crate (as specified by `trans.reachable`)
361 // but rather we also need to possibly export the symbols of upstream
362 // crates. Upstream rlibs may be linked statically to this dynamic library,
363 // in which case they may continue to transitively be used and hence need
364 // their symbols exported.
365 fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation,
367 let path = tmpdir.join("lib.def");
368 let res = (|| -> io::Result<()> {
369 let mut f = BufWriter::new(try!(File::create(&path)));
371 // Start off with the standard module name header and then go
372 // straight to exports.
373 try!(writeln!(f, "LIBRARY"));
374 try!(writeln!(f, "EXPORTS"));
376 // Write out all our local symbols
377 for sym in trans.reachable.iter() {
378 try!(writeln!(f, " {}", sym));
381 // Take a look at how all upstream crates are linked into this
382 // dynamic library. For all statically linked libraries we take all
383 // their reachable symbols and emit them as well.
384 let cstore = &sess.cstore;
385 let formats = sess.dependency_formats.borrow();
386 let symbols = formats[&CrateTypeDylib].iter();
387 let symbols = symbols.enumerate().filter_map(|(i, f)| {
388 if *f == Linkage::Static {
389 Some((i + 1) as ast::CrateNum)
394 cstore.reachable_ids(cnum)
396 cstore.item_symbol(did)
398 for symbol in symbols {
399 try!(writeln!(f, " {}", symbol));
403 if let Err(e) = res {
404 sess.fatal(&format!("failed to write lib.def file: {}", e));
406 let mut arg = OsString::from("/DEF:");
411 fn try_gold_linker(&mut self) {}