]>
Commit | Line | Data |
---|---|---|
62682a34 SL |
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; | |
e9174d1e SL |
12 | use std::fs::{self, File}; |
13 | use std::io::{self, BufWriter}; | |
14 | use std::io::prelude::*; | |
62682a34 SL |
15 | use std::path::{Path, PathBuf}; |
16 | use std::process::Command; | |
17 | ||
c1a9b12d | 18 | use back::archive; |
92a42be0 | 19 | use middle::cstore::CrateStore; |
e9174d1e | 20 | use middle::dependency_format::Linkage; |
62682a34 | 21 | use session::Session; |
e9174d1e SL |
22 | use session::config::CrateTypeDylib; |
23 | use session::config; | |
24 | use syntax::ast; | |
54a0048b | 25 | use CrateTranslation; |
62682a34 SL |
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); | |
c1a9b12d | 36 | fn link_rust_dylib(&mut self, lib: &str, path: &Path); |
62682a34 SL |
37 | fn link_framework(&mut self, framework: &str); |
38 | fn link_staticlib(&mut self, lib: &str); | |
39 | fn link_rlib(&mut self, lib: &Path); | |
c1a9b12d | 40 | fn link_whole_rlib(&mut self, lib: &Path); |
62682a34 SL |
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); | |
c1a9b12d | 49 | fn debuginfo(&mut self); |
62682a34 SL |
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); | |
e9174d1e SL |
57 | fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation, |
58 | tmpdir: &Path); | |
62682a34 SL |
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 | ||
c1a9b12d SL |
83 | fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { |
84 | self.cmd.arg("-l").arg(lib); | |
85 | } | |
86 | ||
62682a34 SL |
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,"); | |
c1a9b12d SL |
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); | |
62682a34 | 110 | self.cmd.arg(&v); |
c1a9b12d SL |
111 | } else { |
112 | self.cmd.arg("-Wl,--whole-archive").arg(lib) | |
113 | .arg("-Wl,--no-whole-archive"); | |
62682a34 SL |
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"); | |
7453a54e SL |
134 | } else if self.sess.target.target.options.is_like_solaris { |
135 | self.cmd.arg("-Wl,-z"); | |
136 | self.cmd.arg("-Wl,ignore"); | |
62682a34 SL |
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. | |
9cc50fc6 SL |
153 | if self.sess.opts.optimize == config::OptLevel::Default || |
154 | self.sess.opts.optimize == config::OptLevel::Aggressive { | |
62682a34 SL |
155 | self.cmd.arg("-Wl,-O1"); |
156 | } | |
157 | } | |
158 | ||
c1a9b12d SL |
159 | fn debuginfo(&mut self) { |
160 | // Don't do anything special here for GNU-style linkers. | |
161 | } | |
162 | ||
62682a34 | 163 | fn no_default_libraries(&mut self) { |
b039eaaf | 164 | self.cmd.arg("-nodefaultlibs"); |
62682a34 SL |
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 | } | |
e9174d1e SL |
201 | |
202 | fn export_symbols(&mut self, _: &Session, _: &CrateTranslation, _: &Path) { | |
203 | // noop, visibility in object files takes care of this | |
204 | } | |
62682a34 SL |
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); } | |
7453a54e SL |
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 | ||
62682a34 SL |
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 | } | |
c1a9b12d SL |
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. | |
7453a54e | 235 | let name = format!("{}.dll.lib", lib); |
c1a9b12d SL |
236 | if fs::metadata(&path.join(&name)).is_ok() { |
237 | self.cmd.arg(name); | |
238 | } | |
239 | } | |
240 | ||
62682a34 SL |
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) { | |
54a0048b | 274 | bug!("frameworks are not supported on windows") |
62682a34 SL |
275 | } |
276 | fn link_framework(&mut self, _framework: &str) { | |
54a0048b | 277 | bug!("frameworks are not supported on windows") |
62682a34 SL |
278 | } |
279 | ||
280 | fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { | |
281 | // not supported? | |
282 | self.link_staticlib(lib); | |
283 | } | |
c1a9b12d SL |
284 | fn link_whole_rlib(&mut self, path: &Path) { |
285 | // not supported? | |
286 | self.link_rlib(path); | |
287 | } | |
62682a34 SL |
288 | fn optimize(&mut self) { |
289 | // Needs more investigation of `/OPT` arguments | |
290 | } | |
c1a9b12d SL |
291 | |
292 | fn debuginfo(&mut self) { | |
b039eaaf SL |
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"); | |
c1a9b12d SL |
296 | } |
297 | ||
62682a34 SL |
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) {} | |
e9174d1e SL |
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<()> { | |
54a0048b | 330 | let mut f = BufWriter::new(File::create(&path)?); |
e9174d1e SL |
331 | |
332 | // Start off with the standard module name header and then go | |
333 | // straight to exports. | |
54a0048b SL |
334 | writeln!(f, "LIBRARY")?; |
335 | writeln!(f, "EXPORTS")?; | |
e9174d1e SL |
336 | |
337 | // Write out all our local symbols | |
338 | for sym in trans.reachable.iter() { | |
54a0048b | 339 | writeln!(f, " {}", sym)?; |
e9174d1e SL |
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| { | |
92a42be0 | 355 | cstore.reachable_ids(cnum) |
e9174d1e | 356 | }).map(|did| { |
92a42be0 | 357 | cstore.item_symbol(did) |
e9174d1e SL |
358 | }); |
359 | for symbol in symbols { | |
54a0048b | 360 | writeln!(f, " {}", symbol)?; |
e9174d1e | 361 | } |
54a0048b | 362 | |
e9174d1e SL |
363 | Ok(()) |
364 | })(); | |
365 | if let Err(e) = res { | |
366 | sess.fatal(&format!("failed to write lib.def file: {}", e)); | |
367 | } | |
368 | let mut arg = OsString::from("/DEF:"); | |
369 | arg.push(path); | |
370 | self.cmd.arg(&arg); | |
371 | } | |
62682a34 | 372 | } |