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