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