]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/back/linker.rs
trans: Use LLVM's writeArchive to modify archives
[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::path::{Path, PathBuf};
13 use std::process::Command;
14 use std::fs;
15
16 use back::archive;
17 use session::Session;
18 use session::config;
19
20 /// Linker abstraction used by back::link to build up the command to invoke a
21 /// linker.
22 ///
23 /// This trait is the total list of requirements needed by `back::link` and
24 /// represents the meaning of each option being passed down. This trait is then
25 /// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an
26 /// MSVC linker (e.g. `link.exe`) is being used.
27 pub trait Linker {
28     fn link_dylib(&mut self, lib: &str);
29     fn link_rust_dylib(&mut self, lib: &str, path: &Path);
30     fn link_framework(&mut self, framework: &str);
31     fn link_staticlib(&mut self, lib: &str);
32     fn link_rlib(&mut self, lib: &Path);
33     fn link_whole_rlib(&mut self, lib: &Path);
34     fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]);
35     fn include_path(&mut self, path: &Path);
36     fn framework_path(&mut self, path: &Path);
37     fn output_filename(&mut self, path: &Path);
38     fn add_object(&mut self, path: &Path);
39     fn gc_sections(&mut self, is_dylib: bool);
40     fn position_independent_executable(&mut self);
41     fn optimize(&mut self);
42     fn no_default_libraries(&mut self);
43     fn build_dylib(&mut self, out_filename: &Path);
44     fn args(&mut self, args: &[String]);
45     fn hint_static(&mut self);
46     fn hint_dynamic(&mut self);
47     fn whole_archives(&mut self);
48     fn no_whole_archives(&mut self);
49 }
50
51 pub struct GnuLinker<'a> {
52     pub cmd: &'a mut Command,
53     pub sess: &'a Session,
54 }
55
56 impl<'a> GnuLinker<'a> {
57     fn takes_hints(&self) -> bool {
58         !self.sess.target.target.options.is_like_osx
59     }
60 }
61
62 impl<'a> Linker for GnuLinker<'a> {
63     fn link_dylib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); }
64     fn link_staticlib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); }
65     fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); }
66     fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); }
67     fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); }
68     fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); }
69     fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
70     fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); }
71     fn args(&mut self, args: &[String]) { self.cmd.args(args); }
72
73     fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
74         self.cmd.arg("-l").arg(lib);
75     }
76
77     fn link_framework(&mut self, framework: &str) {
78         self.cmd.arg("-framework").arg(framework);
79     }
80
81     fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) {
82         let target = &self.sess.target.target;
83         if !target.options.is_like_osx {
84             self.cmd.arg("-Wl,--whole-archive")
85                     .arg("-l").arg(lib)
86                     .arg("-Wl,--no-whole-archive");
87         } else {
88             // -force_load is the OSX equivalent of --whole-archive, but it
89             // involves passing the full path to the library to link.
90             let mut v = OsString::from("-Wl,-force_load,");
91             v.push(&archive::find_library(lib, search_path, &self.sess));
92             self.cmd.arg(&v);
93         }
94     }
95
96     fn link_whole_rlib(&mut self, lib: &Path) {
97         if self.sess.target.target.options.is_like_osx {
98             let mut v = OsString::from("-Wl,-force_load,");
99             v.push(lib);
100             self.cmd.arg(&v);
101         } else {
102             self.cmd.arg("-Wl,--whole-archive").arg(lib)
103                     .arg("-Wl,--no-whole-archive");
104         }
105     }
106
107     fn gc_sections(&mut self, is_dylib: bool) {
108         // The dead_strip option to the linker specifies that functions and data
109         // unreachable by the entry point will be removed. This is quite useful
110         // with Rust's compilation model of compiling libraries at a time into
111         // one object file. For example, this brings hello world from 1.7MB to
112         // 458K.
113         //
114         // Note that this is done for both executables and dynamic libraries. We
115         // won't get much benefit from dylibs because LLVM will have already
116         // stripped away as much as it could. This has not been seen to impact
117         // link times negatively.
118         //
119         // -dead_strip can't be part of the pre_link_args because it's also used
120         // for partial linking when using multiple codegen units (-r).  So we
121         // insert it here.
122         if self.sess.target.target.options.is_like_osx {
123             self.cmd.arg("-Wl,-dead_strip");
124
125         // If we're building a dylib, we don't use --gc-sections because LLVM
126         // has already done the best it can do, and we also don't want to
127         // eliminate the metadata. If we're building an executable, however,
128         // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67%
129         // reduction.
130         } else if !is_dylib {
131             self.cmd.arg("-Wl,--gc-sections");
132         }
133     }
134
135     fn optimize(&mut self) {
136         if !self.sess.target.target.options.linker_is_gnu { return }
137
138         // GNU-style linkers support optimization with -O. GNU ld doesn't
139         // need a numeric argument, but other linkers do.
140         if self.sess.opts.optimize == config::Default ||
141            self.sess.opts.optimize == config::Aggressive {
142             self.cmd.arg("-Wl,-O1");
143         }
144     }
145
146     fn no_default_libraries(&mut self) {
147         // Unfortunately right now passing -nodefaultlibs to gcc on windows
148         // doesn't work so hot (in terms of native dependencies). This if
149         // statement should hopefully be removed one day though!
150         if !self.sess.target.target.options.is_like_windows {
151             self.cmd.arg("-nodefaultlibs");
152         }
153     }
154
155     fn build_dylib(&mut self, out_filename: &Path) {
156         // On mac we need to tell the linker to let this library be rpathed
157         if self.sess.target.target.options.is_like_osx {
158             self.cmd.args(&["-dynamiclib", "-Wl,-dylib"]);
159
160             if self.sess.opts.cg.rpath {
161                 let mut v = OsString::from("-Wl,-install_name,@rpath/");
162                 v.push(out_filename.file_name().unwrap());
163                 self.cmd.arg(&v);
164             }
165         } else {
166             self.cmd.arg("-shared");
167         }
168     }
169
170     fn whole_archives(&mut self) {
171         if !self.takes_hints() { return }
172         self.cmd.arg("-Wl,--whole-archive");
173     }
174
175     fn no_whole_archives(&mut self) {
176         if !self.takes_hints() { return }
177         self.cmd.arg("-Wl,--no-whole-archive");
178     }
179
180     fn hint_static(&mut self) {
181         if !self.takes_hints() { return }
182         self.cmd.arg("-Wl,-Bstatic");
183     }
184
185     fn hint_dynamic(&mut self) {
186         if !self.takes_hints() { return }
187         self.cmd.arg("-Wl,-Bdynamic");
188     }
189 }
190
191 pub struct MsvcLinker<'a> {
192     pub cmd: &'a mut Command,
193     pub sess: &'a Session,
194 }
195
196 impl<'a> Linker for MsvcLinker<'a> {
197     fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); }
198     fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
199     fn args(&mut self, args: &[String]) { self.cmd.args(args); }
200     fn build_dylib(&mut self, _out_filename: &Path) { self.cmd.arg("/DLL"); }
201     fn gc_sections(&mut self, _is_dylib: bool) { self.cmd.arg("/OPT:REF,ICF"); }
202
203     fn link_dylib(&mut self, lib: &str) {
204         self.cmd.arg(&format!("{}.lib", lib));
205     }
206
207     fn link_rust_dylib(&mut self, lib: &str, path: &Path) {
208         // When producing a dll, the MSVC linker may not actually emit a
209         // `foo.lib` file if the dll doesn't actually export any symbols, so we
210         // check to see if the file is there and just omit linking to it if it's
211         // not present.
212         let name = format!("{}.lib", lib);
213         if fs::metadata(&path.join(&name)).is_ok() {
214             self.cmd.arg(name);
215         }
216     }
217
218     fn link_staticlib(&mut self, lib: &str) {
219         self.cmd.arg(&format!("{}.lib", lib));
220     }
221
222     fn position_independent_executable(&mut self) {
223         // noop
224     }
225
226     fn no_default_libraries(&mut self) {
227         // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC
228         // as there's been trouble in the past of linking the C++ standard
229         // library required by LLVM. This likely needs to happen one day, but
230         // in general Windows is also a more controlled environment than
231         // Unix, so it's not necessarily as critical that this be implemented.
232         //
233         // Note that there are also some licensing worries about statically
234         // linking some libraries which require a specific agreement, so it may
235         // not ever be possible for us to pass this flag.
236     }
237
238     fn include_path(&mut self, path: &Path) {
239         let mut arg = OsString::from("/LIBPATH:");
240         arg.push(path);
241         self.cmd.arg(&arg);
242     }
243
244     fn output_filename(&mut self, path: &Path) {
245         let mut arg = OsString::from("/OUT:");
246         arg.push(path);
247         self.cmd.arg(&arg);
248     }
249
250     fn framework_path(&mut self, _path: &Path) {
251         panic!("frameworks are not supported on windows")
252     }
253     fn link_framework(&mut self, _framework: &str) {
254         panic!("frameworks are not supported on windows")
255     }
256
257     fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) {
258         // not supported?
259         self.link_staticlib(lib);
260     }
261     fn link_whole_rlib(&mut self, path: &Path) {
262         // not supported?
263         self.link_rlib(path);
264     }
265     fn optimize(&mut self) {
266         // Needs more investigation of `/OPT` arguments
267     }
268     fn whole_archives(&mut self) {
269         // hints not supported?
270     }
271     fn no_whole_archives(&mut self) {
272         // hints not supported?
273     }
274
275     // On windows static libraries are of the form `foo.lib` and dynamic
276     // libraries are not linked against directly, but rather through their
277     // import libraries also called `foo.lib`. As a result there's no
278     // possibility for a native library to appear both dynamically and
279     // statically in the same folder so we don't have to worry about hints like
280     // we do on Unix platforms.
281     fn hint_static(&mut self) {}
282     fn hint_dynamic(&mut self) {}
283 }