]> git.proxmox.com Git - rustc.git/blob - src/librustc_trans/back/linker.rs
aa29c3cc120584477a47f45f1cb1c56f70d086be
[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::collections::HashMap;
12 use std::ffi::{OsStr, OsString};
13 use std::fs::{self, File};
14 use std::io::prelude::*;
15 use std::io::{self, BufWriter};
16 use std::path::{Path, PathBuf};
17
18 use back::archive;
19 use back::command::Command;
20 use back::symbol_export;
21 use rustc::hir::def_id::{LOCAL_CRATE, CrateNum};
22 use rustc::middle::dependency_format::Linkage;
23 use rustc::session::Session;
24 use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel};
25 use rustc::ty::TyCtxt;
26 use rustc_back::LinkerFlavor;
27 use serialize::{json, Encoder};
28
29 /// For all the linkers we support, and information they might
30 /// need out of the shared crate context before we get rid of it.
31 pub struct LinkerInfo {
32 exports: HashMap<CrateType, Vec<String>>,
33 }
34
35 impl LinkerInfo {
36 pub fn new(tcx: TyCtxt) -> LinkerInfo {
37 LinkerInfo {
38 exports: tcx.sess.crate_types.borrow().iter().map(|&c| {
39 (c, exported_symbols(tcx, c))
40 }).collect(),
41 }
42 }
43
44 pub fn to_linker<'a>(&'a self,
45 cmd: Command,
46 sess: &'a Session) -> Box<Linker+'a> {
47 match sess.linker_flavor() {
48 LinkerFlavor::Msvc => {
49 Box::new(MsvcLinker {
50 cmd,
51 sess,
52 info: self
53 }) as Box<Linker>
54 }
55 LinkerFlavor::Em => {
56 Box::new(EmLinker {
57 cmd,
58 sess,
59 info: self
60 }) as Box<Linker>
61 }
62 LinkerFlavor::Gcc => {
63 Box::new(GccLinker {
64 cmd,
65 sess,
66 info: self,
67 hinted_static: false,
68 is_ld: false,
69 }) as Box<Linker>
70 }
71 LinkerFlavor::Ld => {
72 Box::new(GccLinker {
73 cmd,
74 sess,
75 info: self,
76 hinted_static: false,
77 is_ld: true,
78 }) as Box<Linker>
79 }
80 LinkerFlavor::Binaryen => {
81 panic!("can't instantiate binaryen linker")
82 }
83 }
84 }
85 }
86
87 /// Linker abstraction used by back::link to build up the command to invoke a
88 /// linker.
89 ///
90 /// This trait is the total list of requirements needed by `back::link` and
91 /// represents the meaning of each option being passed down. This trait is then
92 /// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an
93 /// MSVC linker (e.g. `link.exe`) is being used.
94 pub trait Linker {
95 fn link_dylib(&mut self, lib: &str);
96 fn link_rust_dylib(&mut self, lib: &str, path: &Path);
97 fn link_framework(&mut self, framework: &str);
98 fn link_staticlib(&mut self, lib: &str);
99 fn link_rlib(&mut self, lib: &Path);
100 fn link_whole_rlib(&mut self, lib: &Path);
101 fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]);
102 fn include_path(&mut self, path: &Path);
103 fn framework_path(&mut self, path: &Path);
104 fn output_filename(&mut self, path: &Path);
105 fn add_object(&mut self, path: &Path);
106 fn gc_sections(&mut self, keep_metadata: bool);
107 fn position_independent_executable(&mut self);
108 fn partial_relro(&mut self);
109 fn full_relro(&mut self);
110 fn optimize(&mut self);
111 fn debuginfo(&mut self);
112 fn no_default_libraries(&mut self);
113 fn build_dylib(&mut self, out_filename: &Path);
114 fn build_static_executable(&mut self);
115 fn args(&mut self, args: &[String]);
116 fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType);
117 fn subsystem(&mut self, subsystem: &str);
118 // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?).
119 fn finalize(&mut self) -> Command;
120 }
121
122 pub struct GccLinker<'a> {
123 cmd: Command,
124 sess: &'a Session,
125 info: &'a LinkerInfo,
126 hinted_static: bool, // Keeps track of the current hinting mode.
127 // Link as ld
128 is_ld: bool,
129 }
130
131 impl<'a> GccLinker<'a> {
132 /// Argument that must be passed *directly* to the linker
133 ///
134 /// These arguments need to be prepended with '-Wl,' when a gcc-style linker is used
135 fn linker_arg<S>(&mut self, arg: S) -> &mut Self
136 where S: AsRef<OsStr>
137 {
138 if !self.is_ld {
139 let mut os = OsString::from("-Wl,");
140 os.push(arg.as_ref());
141 self.cmd.arg(os);
142 } else {
143 self.cmd.arg(arg);
144 }
145 self
146 }
147
148 fn takes_hints(&self) -> bool {
149 !self.sess.target.target.options.is_like_osx
150 }
151
152 // Some platforms take hints about whether a library is static or dynamic.
153 // For those that support this, we ensure we pass the option if the library
154 // was flagged "static" (most defaults are dynamic) to ensure that if
155 // libfoo.a and libfoo.so both exist that the right one is chosen.
156 fn hint_static(&mut self) {
157 if !self.takes_hints() { return }
158 if !self.hinted_static {
159 self.linker_arg("-Bstatic");
160 self.hinted_static = true;
161 }
162 }
163
164 fn hint_dynamic(&mut self) {
165 if !self.takes_hints() { return }
166 if self.hinted_static {
167 self.linker_arg("-Bdynamic");
168 self.hinted_static = false;
169 }
170 }
171 }
172
173 impl<'a> Linker for GccLinker<'a> {
174 fn link_dylib(&mut self, lib: &str) { self.hint_dynamic(); self.cmd.arg("-l").arg(lib); }
175 fn link_staticlib(&mut self, lib: &str) { self.hint_static(); self.cmd.arg("-l").arg(lib); }
176 fn link_rlib(&mut self, lib: &Path) { self.hint_static(); self.cmd.arg(lib); }
177 fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); }
178 fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); }
179 fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); }
180 fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
181 fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); }
182 fn partial_relro(&mut self) { self.linker_arg("-z,relro"); }
183 fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); }
184 fn build_static_executable(&mut self) { self.cmd.arg("-static"); }
185 fn args(&mut self, args: &[String]) { self.cmd.args(args); }
186
187 fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
188 self.hint_dynamic();
189 self.cmd.arg("-l").arg(lib);
190 }
191
192 fn link_framework(&mut self, framework: &str) {
193 self.hint_dynamic();
194 self.cmd.arg("-framework").arg(framework);
195 }
196
197 // Here we explicitly ask that the entire archive is included into the
198 // result artifact. For more details see #15460, but the gist is that
199 // the linker will strip away any unused objects in the archive if we
200 // don't otherwise explicitly reference them. This can occur for
201 // libraries which are just providing bindings, libraries with generic
202 // functions, etc.
203 fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) {
204 self.hint_static();
205 let target = &self.sess.target.target;
206 if !target.options.is_like_osx {
207 self.linker_arg("--whole-archive").cmd.arg("-l").arg(lib);
208 self.linker_arg("--no-whole-archive");
209 } else {
210 // -force_load is the macOS equivalent of --whole-archive, but it
211 // involves passing the full path to the library to link.
212 let mut v = OsString::from("-force_load,");
213 v.push(&archive::find_library(lib, search_path, &self.sess));
214 self.linker_arg(&v);
215 }
216 }
217
218 fn link_whole_rlib(&mut self, lib: &Path) {
219 self.hint_static();
220 if self.sess.target.target.options.is_like_osx {
221 let mut v = OsString::from("-force_load,");
222 v.push(lib);
223 self.linker_arg(&v);
224 } else {
225 self.linker_arg("--whole-archive").cmd.arg(lib);
226 self.linker_arg("--no-whole-archive");
227 }
228 }
229
230 fn gc_sections(&mut self, keep_metadata: bool) {
231 // The dead_strip option to the linker specifies that functions and data
232 // unreachable by the entry point will be removed. This is quite useful
233 // with Rust's compilation model of compiling libraries at a time into
234 // one object file. For example, this brings hello world from 1.7MB to
235 // 458K.
236 //
237 // Note that this is done for both executables and dynamic libraries. We
238 // won't get much benefit from dylibs because LLVM will have already
239 // stripped away as much as it could. This has not been seen to impact
240 // link times negatively.
241 //
242 // -dead_strip can't be part of the pre_link_args because it's also used
243 // for partial linking when using multiple codegen units (-r). So we
244 // insert it here.
245 if self.sess.target.target.options.is_like_osx {
246 self.linker_arg("-dead_strip");
247 } else if self.sess.target.target.options.is_like_solaris {
248 self.linker_arg("-z");
249 self.linker_arg("ignore");
250
251 // If we're building a dylib, we don't use --gc-sections because LLVM
252 // has already done the best it can do, and we also don't want to
253 // eliminate the metadata. If we're building an executable, however,
254 // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67%
255 // reduction.
256 } else if !keep_metadata {
257 self.linker_arg("--gc-sections");
258 }
259 }
260
261 fn optimize(&mut self) {
262 if !self.sess.target.target.options.linker_is_gnu { return }
263
264 // GNU-style linkers support optimization with -O. GNU ld doesn't
265 // need a numeric argument, but other linkers do.
266 if self.sess.opts.optimize == config::OptLevel::Default ||
267 self.sess.opts.optimize == config::OptLevel::Aggressive {
268 self.linker_arg("-O1");
269 }
270 }
271
272 fn debuginfo(&mut self) {
273 // Don't do anything special here for GNU-style linkers.
274 }
275
276 fn no_default_libraries(&mut self) {
277 if !self.is_ld {
278 self.cmd.arg("-nodefaultlibs");
279 }
280 }
281
282 fn build_dylib(&mut self, out_filename: &Path) {
283 // On mac we need to tell the linker to let this library be rpathed
284 if self.sess.target.target.options.is_like_osx {
285 self.cmd.arg("-dynamiclib");
286 self.linker_arg("-dylib");
287
288 // Note that the `osx_rpath_install_name` option here is a hack
289 // purely to support rustbuild right now, we should get a more
290 // principled solution at some point to force the compiler to pass
291 // the right `-Wl,-install_name` with an `@rpath` in it.
292 if self.sess.opts.cg.rpath ||
293 self.sess.opts.debugging_opts.osx_rpath_install_name {
294 let mut v = OsString::from("-install_name,@rpath/");
295 v.push(out_filename.file_name().unwrap());
296 self.linker_arg(&v);
297 }
298 } else {
299 self.cmd.arg("-shared");
300 }
301 }
302
303 fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) {
304 // If we're compiling a dylib, then we let symbol visibility in object
305 // files to take care of whether they're exported or not.
306 //
307 // If we're compiling a cdylib, however, we manually create a list of
308 // exported symbols to ensure we don't expose any more. The object files
309 // have far more public symbols than we actually want to export, so we
310 // hide them all here.
311 if crate_type == CrateType::CrateTypeDylib ||
312 crate_type == CrateType::CrateTypeProcMacro {
313 return
314 }
315
316 let mut arg = OsString::new();
317 let path = tmpdir.join("list");
318
319 debug!("EXPORTED SYMBOLS:");
320
321 if self.sess.target.target.options.is_like_osx {
322 // Write a plain, newline-separated list of symbols
323 let res = (|| -> io::Result<()> {
324 let mut f = BufWriter::new(File::create(&path)?);
325 for sym in self.info.exports[&crate_type].iter() {
326 debug!(" _{}", sym);
327 writeln!(f, "_{}", sym)?;
328 }
329 Ok(())
330 })();
331 if let Err(e) = res {
332 self.sess.fatal(&format!("failed to write lib.def file: {}", e));
333 }
334 } else {
335 // Write an LD version script
336 let res = (|| -> io::Result<()> {
337 let mut f = BufWriter::new(File::create(&path)?);
338 writeln!(f, "{{\n global:")?;
339 for sym in self.info.exports[&crate_type].iter() {
340 debug!(" {};", sym);
341 writeln!(f, " {};", sym)?;
342 }
343 writeln!(f, "\n local:\n *;\n}};")?;
344 Ok(())
345 })();
346 if let Err(e) = res {
347 self.sess.fatal(&format!("failed to write version script: {}", e));
348 }
349 }
350
351 if self.sess.target.target.options.is_like_osx {
352 if !self.is_ld {
353 arg.push("-Wl,")
354 }
355 arg.push("-exported_symbols_list,");
356 } else if self.sess.target.target.options.is_like_solaris {
357 if !self.is_ld {
358 arg.push("-Wl,")
359 }
360 arg.push("-M,");
361 } else {
362 if !self.is_ld {
363 arg.push("-Wl,")
364 }
365 arg.push("--version-script=");
366 }
367
368 arg.push(&path);
369 self.cmd.arg(arg);
370 }
371
372 fn subsystem(&mut self, subsystem: &str) {
373 self.linker_arg(&format!("--subsystem,{}", subsystem));
374 }
375
376 fn finalize(&mut self) -> Command {
377 self.hint_dynamic(); // Reset to default before returning the composed command line.
378 let mut cmd = Command::new("");
379 ::std::mem::swap(&mut cmd, &mut self.cmd);
380 cmd
381 }
382 }
383
384 pub struct MsvcLinker<'a> {
385 cmd: Command,
386 sess: &'a Session,
387 info: &'a LinkerInfo
388 }
389
390 impl<'a> Linker for MsvcLinker<'a> {
391 fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); }
392 fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
393 fn args(&mut self, args: &[String]) { self.cmd.args(args); }
394
395 fn build_dylib(&mut self, out_filename: &Path) {
396 self.cmd.arg("/DLL");
397 let mut arg: OsString = "/IMPLIB:".into();
398 arg.push(out_filename.with_extension("dll.lib"));
399 self.cmd.arg(arg);
400 }
401
402 fn build_static_executable(&mut self) {
403 // noop
404 }
405
406 fn gc_sections(&mut self, _keep_metadata: bool) {
407 // MSVC's ICF (Identical COMDAT Folding) link optimization is
408 // slow for Rust and thus we disable it by default when not in
409 // optimization build.
410 if self.sess.opts.optimize != config::OptLevel::No {
411 self.cmd.arg("/OPT:REF,ICF");
412 } else {
413 // It is necessary to specify NOICF here, because /OPT:REF
414 // implies ICF by default.
415 self.cmd.arg("/OPT:REF,NOICF");
416 }
417 }
418
419 fn link_dylib(&mut self, lib: &str) {
420 self.cmd.arg(&format!("{}.lib", lib));
421 }
422
423 fn link_rust_dylib(&mut self, lib: &str, path: &Path) {
424 // When producing a dll, the MSVC linker may not actually emit a
425 // `foo.lib` file if the dll doesn't actually export any symbols, so we
426 // check to see if the file is there and just omit linking to it if it's
427 // not present.
428 let name = format!("{}.dll.lib", lib);
429 if fs::metadata(&path.join(&name)).is_ok() {
430 self.cmd.arg(name);
431 }
432 }
433
434 fn link_staticlib(&mut self, lib: &str) {
435 self.cmd.arg(&format!("{}.lib", lib));
436 }
437
438 fn position_independent_executable(&mut self) {
439 // noop
440 }
441
442 fn partial_relro(&mut self) {
443 // noop
444 }
445
446 fn full_relro(&mut self) {
447 // noop
448 }
449
450 fn no_default_libraries(&mut self) {
451 // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC
452 // as there's been trouble in the past of linking the C++ standard
453 // library required by LLVM. This likely needs to happen one day, but
454 // in general Windows is also a more controlled environment than
455 // Unix, so it's not necessarily as critical that this be implemented.
456 //
457 // Note that there are also some licensing worries about statically
458 // linking some libraries which require a specific agreement, so it may
459 // not ever be possible for us to pass this flag.
460 }
461
462 fn include_path(&mut self, path: &Path) {
463 let mut arg = OsString::from("/LIBPATH:");
464 arg.push(path);
465 self.cmd.arg(&arg);
466 }
467
468 fn output_filename(&mut self, path: &Path) {
469 let mut arg = OsString::from("/OUT:");
470 arg.push(path);
471 self.cmd.arg(&arg);
472 }
473
474 fn framework_path(&mut self, _path: &Path) {
475 bug!("frameworks are not supported on windows")
476 }
477 fn link_framework(&mut self, _framework: &str) {
478 bug!("frameworks are not supported on windows")
479 }
480
481 fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) {
482 // not supported?
483 self.link_staticlib(lib);
484 }
485 fn link_whole_rlib(&mut self, path: &Path) {
486 // not supported?
487 self.link_rlib(path);
488 }
489 fn optimize(&mut self) {
490 // Needs more investigation of `/OPT` arguments
491 }
492
493 fn debuginfo(&mut self) {
494 // This will cause the Microsoft linker to generate a PDB file
495 // from the CodeView line tables in the object files.
496 self.cmd.arg("/DEBUG");
497
498 // This will cause the Microsoft linker to embed .natvis info into the the PDB file
499 let sysroot = self.sess.sysroot();
500 let natvis_dir_path = sysroot.join("lib\\rustlib\\etc");
501 if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) {
502 // LLVM 5.0.0's lld-link frontend doesn't yet recognize, and chokes
503 // on, the /NATVIS:... flags. LLVM 6 (or earlier) should at worst ignore
504 // them, eventually mooting this workaround, per this landed patch:
505 // https://github.com/llvm-mirror/lld/commit/27b9c4285364d8d76bb43839daa100
506 if let Some(ref linker_path) = self.sess.opts.cg.linker {
507 if let Some(linker_name) = Path::new(&linker_path).file_stem() {
508 if linker_name.to_str().unwrap().to_lowercase() == "lld-link" {
509 self.sess.warn("not embedding natvis: lld-link may not support the flag");
510 return;
511 }
512 }
513 }
514 for entry in natvis_dir {
515 match entry {
516 Ok(entry) => {
517 let path = entry.path();
518 if path.extension() == Some("natvis".as_ref()) {
519 let mut arg = OsString::from("/NATVIS:");
520 arg.push(path);
521 self.cmd.arg(arg);
522 }
523 },
524 Err(err) => {
525 self.sess.warn(&format!("error enumerating natvis directory: {}", err));
526 },
527 }
528 }
529 }
530 }
531
532 // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to
533 // export symbols from a dynamic library. When building a dynamic library,
534 // however, we're going to want some symbols exported, so this function
535 // generates a DEF file which lists all the symbols.
536 //
537 // The linker will read this `*.def` file and export all the symbols from
538 // the dynamic library. Note that this is not as simple as just exporting
539 // all the symbols in the current crate (as specified by `trans.reachable`)
540 // but rather we also need to possibly export the symbols of upstream
541 // crates. Upstream rlibs may be linked statically to this dynamic library,
542 // in which case they may continue to transitively be used and hence need
543 // their symbols exported.
544 fn export_symbols(&mut self,
545 tmpdir: &Path,
546 crate_type: CrateType) {
547 let path = tmpdir.join("lib.def");
548 let res = (|| -> io::Result<()> {
549 let mut f = BufWriter::new(File::create(&path)?);
550
551 // Start off with the standard module name header and then go
552 // straight to exports.
553 writeln!(f, "LIBRARY")?;
554 writeln!(f, "EXPORTS")?;
555 for symbol in self.info.exports[&crate_type].iter() {
556 debug!(" _{}", symbol);
557 writeln!(f, " {}", symbol)?;
558 }
559 Ok(())
560 })();
561 if let Err(e) = res {
562 self.sess.fatal(&format!("failed to write lib.def file: {}", e));
563 }
564 let mut arg = OsString::from("/DEF:");
565 arg.push(path);
566 self.cmd.arg(&arg);
567 }
568
569 fn subsystem(&mut self, subsystem: &str) {
570 // Note that previous passes of the compiler validated this subsystem,
571 // so we just blindly pass it to the linker.
572 self.cmd.arg(&format!("/SUBSYSTEM:{}", subsystem));
573
574 // Windows has two subsystems we're interested in right now, the console
575 // and windows subsystems. These both implicitly have different entry
576 // points (starting symbols). The console entry point starts with
577 // `mainCRTStartup` and the windows entry point starts with
578 // `WinMainCRTStartup`. These entry points, defined in system libraries,
579 // will then later probe for either `main` or `WinMain`, respectively to
580 // start the application.
581 //
582 // In Rust we just always generate a `main` function so we want control
583 // to always start there, so we force the entry point on the windows
584 // subsystem to be `mainCRTStartup` to get everything booted up
585 // correctly.
586 //
587 // For more information see RFC #1665
588 if subsystem == "windows" {
589 self.cmd.arg("/ENTRY:mainCRTStartup");
590 }
591 }
592
593 fn finalize(&mut self) -> Command {
594 let mut cmd = Command::new("");
595 ::std::mem::swap(&mut cmd, &mut self.cmd);
596 cmd
597 }
598 }
599
600 pub struct EmLinker<'a> {
601 cmd: Command,
602 sess: &'a Session,
603 info: &'a LinkerInfo
604 }
605
606 impl<'a> Linker for EmLinker<'a> {
607 fn include_path(&mut self, path: &Path) {
608 self.cmd.arg("-L").arg(path);
609 }
610
611 fn link_staticlib(&mut self, lib: &str) {
612 self.cmd.arg("-l").arg(lib);
613 }
614
615 fn output_filename(&mut self, path: &Path) {
616 self.cmd.arg("-o").arg(path);
617 }
618
619 fn add_object(&mut self, path: &Path) {
620 self.cmd.arg(path);
621 }
622
623 fn link_dylib(&mut self, lib: &str) {
624 // Emscripten always links statically
625 self.link_staticlib(lib);
626 }
627
628 fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) {
629 // not supported?
630 self.link_staticlib(lib);
631 }
632
633 fn link_whole_rlib(&mut self, lib: &Path) {
634 // not supported?
635 self.link_rlib(lib);
636 }
637
638 fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
639 self.link_dylib(lib);
640 }
641
642 fn link_rlib(&mut self, lib: &Path) {
643 self.add_object(lib);
644 }
645
646 fn position_independent_executable(&mut self) {
647 // noop
648 }
649
650 fn partial_relro(&mut self) {
651 // noop
652 }
653
654 fn full_relro(&mut self) {
655 // noop
656 }
657
658 fn args(&mut self, args: &[String]) {
659 self.cmd.args(args);
660 }
661
662 fn framework_path(&mut self, _path: &Path) {
663 bug!("frameworks are not supported on Emscripten")
664 }
665
666 fn link_framework(&mut self, _framework: &str) {
667 bug!("frameworks are not supported on Emscripten")
668 }
669
670 fn gc_sections(&mut self, _keep_metadata: bool) {
671 // noop
672 }
673
674 fn optimize(&mut self) {
675 // Emscripten performs own optimizations
676 self.cmd.arg(match self.sess.opts.optimize {
677 OptLevel::No => "-O0",
678 OptLevel::Less => "-O1",
679 OptLevel::Default => "-O2",
680 OptLevel::Aggressive => "-O3",
681 OptLevel::Size => "-Os",
682 OptLevel::SizeMin => "-Oz"
683 });
684 // Unusable until https://github.com/rust-lang/rust/issues/38454 is resolved
685 self.cmd.args(&["--memory-init-file", "0"]);
686 }
687
688 fn debuginfo(&mut self) {
689 // Preserve names or generate source maps depending on debug info
690 self.cmd.arg(match self.sess.opts.debuginfo {
691 DebugInfoLevel::NoDebugInfo => "-g0",
692 DebugInfoLevel::LimitedDebugInfo => "-g3",
693 DebugInfoLevel::FullDebugInfo => "-g4"
694 });
695 }
696
697 fn no_default_libraries(&mut self) {
698 self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]);
699 }
700
701 fn build_dylib(&mut self, _out_filename: &Path) {
702 bug!("building dynamic library is unsupported on Emscripten")
703 }
704
705 fn build_static_executable(&mut self) {
706 // noop
707 }
708
709 fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) {
710 let symbols = &self.info.exports[&crate_type];
711
712 debug!("EXPORTED SYMBOLS:");
713
714 self.cmd.arg("-s");
715
716 let mut arg = OsString::from("EXPORTED_FUNCTIONS=");
717 let mut encoded = String::new();
718
719 {
720 let mut encoder = json::Encoder::new(&mut encoded);
721 let res = encoder.emit_seq(symbols.len(), |encoder| {
722 for (i, sym) in symbols.iter().enumerate() {
723 encoder.emit_seq_elt(i, |encoder| {
724 encoder.emit_str(&("_".to_string() + sym))
725 })?;
726 }
727 Ok(())
728 });
729 if let Err(e) = res {
730 self.sess.fatal(&format!("failed to encode exported symbols: {}", e));
731 }
732 }
733 debug!("{}", encoded);
734 arg.push(encoded);
735
736 self.cmd.arg(arg);
737 }
738
739 fn subsystem(&mut self, _subsystem: &str) {
740 // noop
741 }
742
743 fn finalize(&mut self) -> Command {
744 let mut cmd = Command::new("");
745 ::std::mem::swap(&mut cmd, &mut self.cmd);
746 cmd
747 }
748 }
749
750 fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec<String> {
751 let mut symbols = Vec::new();
752
753 let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
754 for &(ref name, _, level) in tcx.exported_symbols(LOCAL_CRATE).iter() {
755 if level.is_below_threshold(export_threshold) {
756 symbols.push(name.clone());
757 }
758 }
759
760 let formats = tcx.sess.dependency_formats.borrow();
761 let deps = formats[&crate_type].iter();
762
763 for (index, dep_format) in deps.enumerate() {
764 let cnum = CrateNum::new(index + 1);
765 // For each dependency that we are linking to statically ...
766 if *dep_format == Linkage::Static {
767 // ... we add its symbol list to our export list.
768 for &(ref name, _, level) in tcx.exported_symbols(cnum).iter() {
769 if level.is_below_threshold(export_threshold) {
770 symbols.push(name.clone());
771 }
772 }
773 }
774 }
775
776 symbols
777 }