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