]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/back/linker.rs
b6b330c3734b0310f67d1202634aa743164baf10
[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::ffi::OsString;
12 use std::fs::{self, File};
13 use std::io::{self, BufWriter};
14 use std::io::prelude::*;
15 use std::path::{Path, PathBuf};
16 use std::process::Command;
17
18 use back::archive;
19 use middle::cstore::CrateStore;
20 use middle::dependency_format::Linkage;
21 use session::Session;
22 use session::config::CrateTypeDylib;
23 use session::config;
24 use syntax::ast;
25 use trans::CrateTranslation;
26
27 /// Linker abstraction used by back::link to build up the command to invoke a
28 /// linker.
29 ///
30 /// This trait is the total list of requirements needed by `back::link` and
31 /// represents the meaning of each option being passed down. This trait is then
32 /// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an
33 /// MSVC linker (e.g. `link.exe`) is being used.
34 pub trait Linker {
35     fn link_dylib(&mut self, lib: &str);
36     fn link_rust_dylib(&mut self, lib: &str, path: &Path);
37     fn link_framework(&mut self, framework: &str);
38     fn link_staticlib(&mut self, lib: &str);
39     fn link_rlib(&mut self, lib: &Path);
40     fn link_whole_rlib(&mut self, lib: &Path);
41     fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]);
42     fn include_path(&mut self, path: &Path);
43     fn framework_path(&mut self, path: &Path);
44     fn output_filename(&mut self, path: &Path);
45     fn add_object(&mut self, path: &Path);
46     fn gc_sections(&mut self, is_dylib: bool);
47     fn position_independent_executable(&mut self);
48     fn optimize(&mut self);
49     fn debuginfo(&mut self);
50     fn no_default_libraries(&mut self);
51     fn build_dylib(&mut self, out_filename: &Path);
52     fn args(&mut self, args: &[String]);
53     fn hint_static(&mut self);
54     fn hint_dynamic(&mut self);
55     fn whole_archives(&mut self);
56     fn no_whole_archives(&mut self);
57     fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation,
58                       tmpdir: &Path);
59 }
60
61 pub struct GnuLinker<'a> {
62     pub cmd: &'a mut Command,
63     pub sess: &'a Session,
64 }
65
66 impl<'a> GnuLinker<'a> {
67     fn takes_hints(&self) -> bool {
68         !self.sess.target.target.options.is_like_osx
69     }
70 }
71
72 impl<'a> Linker for GnuLinker<'a> {
73     fn link_dylib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); }
74     fn link_staticlib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); }
75     fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); }
76     fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); }
77     fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); }
78     fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); }
79     fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
80     fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); }
81     fn args(&mut self, args: &[String]) { self.cmd.args(args); }
82
83     fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
84         self.cmd.arg("-l").arg(lib);
85     }
86
87     fn link_framework(&mut self, framework: &str) {
88         self.cmd.arg("-framework").arg(framework);
89     }
90
91     fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) {
92         let target = &self.sess.target.target;
93         if !target.options.is_like_osx {
94             self.cmd.arg("-Wl,--whole-archive")
95                     .arg("-l").arg(lib)
96                     .arg("-Wl,--no-whole-archive");
97         } else {
98             // -force_load is the OSX equivalent of --whole-archive, but it
99             // involves passing the full path to the library to link.
100             let mut v = OsString::from("-Wl,-force_load,");
101             v.push(&archive::find_library(lib, search_path, &self.sess));
102             self.cmd.arg(&v);
103         }
104     }
105
106     fn link_whole_rlib(&mut self, lib: &Path) {
107         if self.sess.target.target.options.is_like_osx {
108             let mut v = OsString::from("-Wl,-force_load,");
109             v.push(lib);
110             self.cmd.arg(&v);
111         } else {
112             self.cmd.arg("-Wl,--whole-archive").arg(lib)
113                     .arg("-Wl,--no-whole-archive");
114         }
115     }
116
117     fn gc_sections(&mut self, is_dylib: bool) {
118         // The dead_strip option to the linker specifies that functions and data
119         // unreachable by the entry point will be removed. This is quite useful
120         // with Rust's compilation model of compiling libraries at a time into
121         // one object file. For example, this brings hello world from 1.7MB to
122         // 458K.
123         //
124         // Note that this is done for both executables and dynamic libraries. We
125         // won't get much benefit from dylibs because LLVM will have already
126         // stripped away as much as it could. This has not been seen to impact
127         // link times negatively.
128         //
129         // -dead_strip can't be part of the pre_link_args because it's also used
130         // for partial linking when using multiple codegen units (-r).  So we
131         // insert it here.
132         if self.sess.target.target.options.is_like_osx {
133             self.cmd.arg("-Wl,-dead_strip");
134         } else if self.sess.target.target.options.is_like_solaris {
135             self.cmd.arg("-Wl,-z");
136             self.cmd.arg("-Wl,ignore");
137
138         // If we're building a dylib, we don't use --gc-sections because LLVM
139         // has already done the best it can do, and we also don't want to
140         // eliminate the metadata. If we're building an executable, however,
141         // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67%
142         // reduction.
143         } else if !is_dylib {
144             self.cmd.arg("-Wl,--gc-sections");
145         }
146     }
147
148     fn optimize(&mut self) {
149         if !self.sess.target.target.options.linker_is_gnu { return }
150
151         // GNU-style linkers support optimization with -O. GNU ld doesn't
152         // need a numeric argument, but other linkers do.
153         if self.sess.opts.optimize == config::OptLevel::Default ||
154            self.sess.opts.optimize == config::OptLevel::Aggressive {
155             self.cmd.arg("-Wl,-O1");
156         }
157     }
158
159     fn debuginfo(&mut self) {
160         // Don't do anything special here for GNU-style linkers.
161     }
162
163     fn no_default_libraries(&mut self) {
164         self.cmd.arg("-nodefaultlibs");
165     }
166
167     fn build_dylib(&mut self, out_filename: &Path) {
168         // On mac we need to tell the linker to let this library be rpathed
169         if self.sess.target.target.options.is_like_osx {
170             self.cmd.args(&["-dynamiclib", "-Wl,-dylib"]);
171
172             if self.sess.opts.cg.rpath {
173                 let mut v = OsString::from("-Wl,-install_name,@rpath/");
174                 v.push(out_filename.file_name().unwrap());
175                 self.cmd.arg(&v);
176             }
177         } else {
178             self.cmd.arg("-shared");
179         }
180     }
181
182     fn whole_archives(&mut self) {
183         if !self.takes_hints() { return }
184         self.cmd.arg("-Wl,--whole-archive");
185     }
186
187     fn no_whole_archives(&mut self) {
188         if !self.takes_hints() { return }
189         self.cmd.arg("-Wl,--no-whole-archive");
190     }
191
192     fn hint_static(&mut self) {
193         if !self.takes_hints() { return }
194         self.cmd.arg("-Wl,-Bstatic");
195     }
196
197     fn hint_dynamic(&mut self) {
198         if !self.takes_hints() { return }
199         self.cmd.arg("-Wl,-Bdynamic");
200     }
201
202     fn export_symbols(&mut self, _: &Session, _: &CrateTranslation, _: &Path) {
203         // noop, visibility in object files takes care of this
204     }
205 }
206
207 pub struct MsvcLinker<'a> {
208     pub cmd: &'a mut Command,
209     pub sess: &'a Session,
210 }
211
212 impl<'a> Linker for MsvcLinker<'a> {
213     fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); }
214     fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
215     fn args(&mut self, args: &[String]) { self.cmd.args(args); }
216
217     fn build_dylib(&mut self, out_filename: &Path) {
218         self.cmd.arg("/DLL");
219         let mut arg: OsString = "/IMPLIB:".into();
220         arg.push(out_filename.with_extension("dll.lib"));
221         self.cmd.arg(arg);
222     }
223
224     fn gc_sections(&mut self, _is_dylib: bool) { self.cmd.arg("/OPT:REF,ICF"); }
225
226     fn link_dylib(&mut self, lib: &str) {
227         self.cmd.arg(&format!("{}.lib", lib));
228     }
229
230     fn link_rust_dylib(&mut self, lib: &str, path: &Path) {
231         // When producing a dll, the MSVC linker may not actually emit a
232         // `foo.lib` file if the dll doesn't actually export any symbols, so we
233         // check to see if the file is there and just omit linking to it if it's
234         // not present.
235         let name = format!("{}.dll.lib", lib);
236         if fs::metadata(&path.join(&name)).is_ok() {
237             self.cmd.arg(name);
238         }
239     }
240
241     fn link_staticlib(&mut self, lib: &str) {
242         self.cmd.arg(&format!("{}.lib", lib));
243     }
244
245     fn position_independent_executable(&mut self) {
246         // noop
247     }
248
249     fn no_default_libraries(&mut self) {
250         // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC
251         // as there's been trouble in the past of linking the C++ standard
252         // library required by LLVM. This likely needs to happen one day, but
253         // in general Windows is also a more controlled environment than
254         // Unix, so it's not necessarily as critical that this be implemented.
255         //
256         // Note that there are also some licensing worries about statically
257         // linking some libraries which require a specific agreement, so it may
258         // not ever be possible for us to pass this flag.
259     }
260
261     fn include_path(&mut self, path: &Path) {
262         let mut arg = OsString::from("/LIBPATH:");
263         arg.push(path);
264         self.cmd.arg(&arg);
265     }
266
267     fn output_filename(&mut self, path: &Path) {
268         let mut arg = OsString::from("/OUT:");
269         arg.push(path);
270         self.cmd.arg(&arg);
271     }
272
273     fn framework_path(&mut self, _path: &Path) {
274         panic!("frameworks are not supported on windows")
275     }
276     fn link_framework(&mut self, _framework: &str) {
277         panic!("frameworks are not supported on windows")
278     }
279
280     fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) {
281         // not supported?
282         self.link_staticlib(lib);
283     }
284     fn link_whole_rlib(&mut self, path: &Path) {
285         // not supported?
286         self.link_rlib(path);
287     }
288     fn optimize(&mut self) {
289         // Needs more investigation of `/OPT` arguments
290     }
291
292     fn debuginfo(&mut self) {
293         // This will cause the Microsoft linker to generate a PDB file
294         // from the CodeView line tables in the object files.
295         self.cmd.arg("/DEBUG");
296     }
297
298     fn whole_archives(&mut self) {
299         // hints not supported?
300     }
301     fn no_whole_archives(&mut self) {
302         // hints not supported?
303     }
304
305     // On windows static libraries are of the form `foo.lib` and dynamic
306     // libraries are not linked against directly, but rather through their
307     // import libraries also called `foo.lib`. As a result there's no
308     // possibility for a native library to appear both dynamically and
309     // statically in the same folder so we don't have to worry about hints like
310     // we do on Unix platforms.
311     fn hint_static(&mut self) {}
312     fn hint_dynamic(&mut self) {}
313
314     // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to
315     // export symbols from a dynamic library. When building a dynamic library,
316     // however, we're going to want some symbols exported, so this function
317     // generates a DEF file which lists all the symbols.
318     //
319     // The linker will read this `*.def` file and export all the symbols from
320     // the dynamic library. Note that this is not as simple as just exporting
321     // all the symbols in the current crate (as specified by `trans.reachable`)
322     // but rather we also need to possibly export the symbols of upstream
323     // crates. Upstream rlibs may be linked statically to this dynamic library,
324     // in which case they may continue to transitively be used and hence need
325     // their symbols exported.
326     fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation,
327                       tmpdir: &Path) {
328         let path = tmpdir.join("lib.def");
329         let res = (|| -> io::Result<()> {
330             let mut f = BufWriter::new(File::create(&path)?);
331
332             // Start off with the standard module name header and then go
333             // straight to exports.
334             writeln!(f, "LIBRARY")?;
335             writeln!(f, "EXPORTS")?;
336
337             // Write out all our local symbols
338             for sym in trans.reachable.iter() {
339                 writeln!(f, "  {}", sym)?;
340             }
341
342             // Take a look at how all upstream crates are linked into this
343             // dynamic library. For all statically linked libraries we take all
344             // their reachable symbols and emit them as well.
345             let cstore = &sess.cstore;
346             let formats = sess.dependency_formats.borrow();
347             let symbols = formats[&CrateTypeDylib].iter();
348             let symbols = symbols.enumerate().filter_map(|(i, f)| {
349                 if *f == Linkage::Static {
350                     Some((i + 1) as ast::CrateNum)
351                 } else {
352                     None
353                 }
354             }).flat_map(|cnum| {
355                 cstore.reachable_ids(cnum)
356             }).map(|did| {
357                 cstore.item_symbol(did)
358             });
359             for symbol in symbols {
360                 writeln!(f, "  {}", symbol)?;
361             }
362             Ok(())
363         })();
364         if let Err(e) = res {
365             sess.fatal(&format!("failed to write lib.def file: {}", e));
366         }
367         let mut arg = OsString::from("/DEF:");
368         arg.push(path);
369         self.cmd.arg(&arg);
370     }
371 }