]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/back/linker.rs
90ebf364367a0c66388a8423e70b74fef16e3198
[rust.git] / src / librustc_trans / back / linker.rs
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.
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 use std::env;
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;
18
19 use back::archive;
20 use middle::cstore::CrateStore;
21 use middle::dependency_format::Linkage;
22 use session::Session;
23 use session::config::CrateTypeDylib;
24 use session::config;
25 use syntax::ast;
26 use trans::CrateTranslation;
27
28 /// Linker abstraction used by back::link to build up the command to invoke a
29 /// linker.
30 ///
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.
35 pub trait Linker {
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,
59                       tmpdir: &Path);
60     fn try_gold_linker(&mut self);
61 }
62
63 pub struct GnuLinker<'a> {
64     pub cmd: &'a mut Command,
65     pub sess: &'a Session,
66 }
67
68 impl<'a> GnuLinker<'a> {
69     fn takes_hints(&self) -> bool {
70         !self.sess.target.target.options.is_like_osx
71     }
72 }
73
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); }
84
85     fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
86         self.cmd.arg("-l").arg(lib);
87     }
88
89     fn link_framework(&mut self, framework: &str) {
90         self.cmd.arg("-framework").arg(framework);
91     }
92
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")
97                     .arg("-l").arg(lib)
98                     .arg("-Wl,--no-whole-archive");
99         } else {
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));
104             self.cmd.arg(&v);
105         }
106     }
107
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,");
111             v.push(lib);
112             self.cmd.arg(&v);
113         } else {
114             self.cmd.arg("-Wl,--whole-archive").arg(lib)
115                     .arg("-Wl,--no-whole-archive");
116         }
117     }
118
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
124         // 458K.
125         //
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.
130         //
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
133         // insert it here.
134         if self.sess.target.target.options.is_like_osx {
135             self.cmd.arg("-Wl,-dead_strip");
136
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%
141         // reduction.
142         } else if !is_dylib {
143             self.cmd.arg("-Wl,--gc-sections");
144         }
145     }
146
147     fn optimize(&mut self) {
148         if !self.sess.target.target.options.linker_is_gnu { return }
149
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");
155         }
156     }
157
158     fn debuginfo(&mut self) {
159         // Don't do anything special here for GNU-style linkers.
160     }
161
162     fn no_default_libraries(&mut self) {
163         self.cmd.arg("-nodefaultlibs");
164     }
165
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"]);
170
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());
174                 self.cmd.arg(&v);
175             }
176         } else {
177             self.cmd.arg("-shared");
178         }
179     }
180
181     fn whole_archives(&mut self) {
182         if !self.takes_hints() { return }
183         self.cmd.arg("-Wl,--whole-archive");
184     }
185
186     fn no_whole_archives(&mut self) {
187         if !self.takes_hints() { return }
188         self.cmd.arg("-Wl,--no-whole-archive");
189     }
190
191     fn hint_static(&mut self) {
192         if !self.takes_hints() { return }
193         self.cmd.arg("-Wl,-Bstatic");
194     }
195
196     fn hint_dynamic(&mut self) {
197         if !self.takes_hints() { return }
198         self.cmd.arg("-Wl,-Bdynamic");
199     }
200
201     fn export_symbols(&mut self, _: &Session, _: &CrateTranslation, _: &Path) {
202         // noop, visibility in object files takes care of this
203     }
204
205     fn try_gold_linker(&mut self) {
206         // Only use gold under specific conditions that we know work
207
208         let gold_exists = match env::var_os("PATH") {
209             Some(ref env_path) => {
210                 env::split_paths(env_path).any(|mut p| {
211                     p.push("ld.gold");
212                     p.exists()
213                 })
214             }
215             None => false
216         };
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)
229         };
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;
236
237         let can_use_gold =
238             gold_exists &&
239             host_is_linux &&
240             target_is_host_compatible &&
241             target_works_with_gold &&
242             !opt_out;
243
244         if can_use_gold {
245             info!("linking with ld.gold");
246             self.cmd.arg("-fuse-ld=gold");
247         } else {
248             info!("linking with ld");
249         }
250     }
251 }
252
253 pub struct MsvcLinker<'a> {
254     pub cmd: &'a mut Command,
255     pub sess: &'a Session,
256 }
257
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"); }
264
265     fn link_dylib(&mut self, lib: &str) {
266         self.cmd.arg(&format!("{}.lib", lib));
267     }
268
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
273         // not present.
274         let name = format!("{}.lib", lib);
275         if fs::metadata(&path.join(&name)).is_ok() {
276             self.cmd.arg(name);
277         }
278     }
279
280     fn link_staticlib(&mut self, lib: &str) {
281         self.cmd.arg(&format!("{}.lib", lib));
282     }
283
284     fn position_independent_executable(&mut self) {
285         // noop
286     }
287
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.
294         //
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.
298     }
299
300     fn include_path(&mut self, path: &Path) {
301         let mut arg = OsString::from("/LIBPATH:");
302         arg.push(path);
303         self.cmd.arg(&arg);
304     }
305
306     fn output_filename(&mut self, path: &Path) {
307         let mut arg = OsString::from("/OUT:");
308         arg.push(path);
309         self.cmd.arg(&arg);
310     }
311
312     fn framework_path(&mut self, _path: &Path) {
313         panic!("frameworks are not supported on windows")
314     }
315     fn link_framework(&mut self, _framework: &str) {
316         panic!("frameworks are not supported on windows")
317     }
318
319     fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) {
320         // not supported?
321         self.link_staticlib(lib);
322     }
323     fn link_whole_rlib(&mut self, path: &Path) {
324         // not supported?
325         self.link_rlib(path);
326     }
327     fn optimize(&mut self) {
328         // Needs more investigation of `/OPT` arguments
329     }
330
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");
335     }
336
337     fn whole_archives(&mut self) {
338         // hints not supported?
339     }
340     fn no_whole_archives(&mut self) {
341         // hints not supported?
342     }
343
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) {}
352
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.
357     //
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,
366                       tmpdir: &Path) {
367         let path = tmpdir.join("lib.def");
368         let res = (|| -> io::Result<()> {
369             let mut f = BufWriter::new(try!(File::create(&path)));
370
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"));
375
376             // Write out all our local symbols
377             for sym in trans.reachable.iter() {
378                 try!(writeln!(f, "  {}", sym));
379             }
380
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)
390                 } else {
391                     None
392                 }
393             }).flat_map(|cnum| {
394                 cstore.reachable_ids(cnum)
395             }).map(|did| {
396                 cstore.item_symbol(did)
397             });
398             for symbol in symbols {
399                 try!(writeln!(f, "  {}", symbol));
400             }
401             Ok(())
402         })();
403         if let Err(e) = res {
404             sess.fatal(&format!("failed to write lib.def file: {}", e));
405         }
406         let mut arg = OsString::from("/DEF:");
407         arg.push(path);
408         self.cmd.arg(&arg);
409     }
410
411     fn try_gold_linker(&mut self) {}
412 }