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