]> git.lizzy.rs Git - rust.git/blob - src/librustc/back/link.rs
9166696694986c1e18719fec5e17ef1c617cb097
[rust.git] / src / librustc / back / link.rs
1 // Copyright 2012-2013 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
12 use back::rpath;
13 use driver::session::Session;
14 use driver::session;
15 use lib::llvm::llvm;
16 use lib::llvm::ModuleRef;
17 use lib;
18 use metadata::common::LinkMeta;
19 use metadata::{encoder, csearch, cstore, filesearch};
20 use middle::trans::context::CrateContext;
21 use middle::trans::common::gensym_name;
22 use middle::ty;
23 use util::ppaux;
24
25 use std::c_str::ToCStr;
26 use std::char;
27 use std::hash::Streaming;
28 use std::hash;
29 use std::io;
30 use std::os::consts::{macos, freebsd, linux, android, win32};
31 use std::os;
32 use std::ptr;
33 use std::rt::io::Writer;
34 use std::run;
35 use std::str;
36 use std::vec;
37 use syntax::ast;
38 use syntax::ast_map::{path, path_mod, path_name};
39 use syntax::attr;
40 use syntax::attr::{AttrMetaMethods};
41 use syntax::print::pprust;
42 use syntax::parse::token;
43
44 #[deriving(Clone, Eq)]
45 pub enum output_type {
46     output_type_none,
47     output_type_bitcode,
48     output_type_assembly,
49     output_type_llvm_assembly,
50     output_type_object,
51     output_type_exe,
52 }
53
54 fn write_string<W:Writer>(writer: &mut W, string: &str) {
55     writer.write(string.as_bytes());
56 }
57
58 pub fn llvm_err(sess: Session, msg: ~str) -> ! {
59     unsafe {
60         let cstr = llvm::LLVMRustGetLastError();
61         if cstr == ptr::null() {
62             sess.fatal(msg);
63         } else {
64             sess.fatal(msg + ": " + str::raw::from_c_str(cstr));
65         }
66     }
67 }
68
69 pub fn WriteOutputFile(
70         sess: Session,
71         Target: lib::llvm::TargetMachineRef,
72         PM: lib::llvm::PassManagerRef,
73         M: ModuleRef,
74         Output: &str,
75         FileType: lib::llvm::FileType) {
76     unsafe {
77         do Output.with_c_str |Output| {
78             let result = llvm::LLVMRustWriteOutputFile(
79                     Target, PM, M, Output, FileType);
80             if !result {
81                 llvm_err(sess, ~"Could not write output");
82             }
83         }
84     }
85 }
86
87 pub mod jit {
88
89     use back::link::llvm_err;
90     use driver::session::Session;
91     use lib::llvm::llvm;
92     use lib::llvm::{ModuleRef, ContextRef, ExecutionEngineRef};
93     use metadata::cstore;
94
95     use std::c_str::ToCStr;
96     use std::cast;
97     use std::local_data;
98     use std::unstable::intrinsics;
99
100     struct LLVMJITData {
101         ee: ExecutionEngineRef,
102         llcx: ContextRef
103     }
104
105     pub trait Engine {}
106     impl Engine for LLVMJITData {}
107
108     impl Drop for LLVMJITData {
109         fn drop(&self) {
110             unsafe {
111                 llvm::LLVMDisposeExecutionEngine(self.ee);
112                 llvm::LLVMContextDispose(self.llcx);
113             }
114         }
115     }
116
117     pub fn exec(sess: Session,
118                 c: ContextRef,
119                 m: ModuleRef,
120                 stacks: bool) {
121         unsafe {
122             let manager = llvm::LLVMRustPrepareJIT(intrinsics::morestack_addr());
123
124             // We need to tell JIT where to resolve all linked
125             // symbols from. The equivalent of -lstd, -lcore, etc.
126             // By default the JIT will resolve symbols from the extra and
127             // core linked into rustc. We don't want that,
128             // incase the user wants to use an older extra library.
129
130             let cstore = sess.cstore;
131             let r = cstore::get_used_crate_files(cstore);
132             for cratepath in r.iter() {
133                 let path = cratepath.to_str();
134
135                 debug!("linking: %s", path);
136
137                 do path.with_c_str |buf_t| {
138                     if !llvm::LLVMRustLoadCrate(manager, buf_t) {
139                         llvm_err(sess, ~"Could not link");
140                     }
141                     debug!("linked: %s", path);
142                 }
143             }
144
145             // We custom-build a JIT execution engine via some rust wrappers
146             // first. This wrappers takes ownership of the module passed in.
147             let ee = llvm::LLVMRustBuildJIT(manager, m, stacks);
148             if ee.is_null() {
149                 llvm::LLVMContextDispose(c);
150                 llvm_err(sess, ~"Could not create the JIT");
151             }
152
153             // Next, we need to get a handle on the _rust_main function by
154             // looking up it's corresponding ValueRef and then requesting that
155             // the execution engine compiles the function.
156             let fun = do "_rust_main".with_c_str |entry| {
157                 llvm::LLVMGetNamedFunction(m, entry)
158             };
159             if fun.is_null() {
160                 llvm::LLVMDisposeExecutionEngine(ee);
161                 llvm::LLVMContextDispose(c);
162                 llvm_err(sess, ~"Could not find _rust_main in the JIT");
163             }
164
165             // Finally, once we have the pointer to the code, we can do some
166             // closure magic here to turn it straight into a callable rust
167             // closure
168             let code = llvm::LLVMGetPointerToGlobal(ee, fun);
169             assert!(!code.is_null());
170             let func: extern "Rust" fn() = cast::transmute(code);
171             func();
172
173             // Currently there is no method of re-using the executing engine
174             // from LLVM in another call to the JIT. While this kinda defeats
175             // the purpose of having a JIT in the first place, there isn't
176             // actually much code currently which would re-use data between
177             // different invocations of this. Additionally, the compilation
178             // model currently isn't designed to support this scenario.
179             //
180             // We can't destroy the engine/context immediately here, however,
181             // because of annihilation. The JIT code contains drop glue for any
182             // types defined in the crate we just ran, and if any of those boxes
183             // are going to be dropped during annihilation, the drop glue must
184             // be run. Hence, we need to transfer ownership of this jit engine
185             // to the caller of this function. To be convenient for now, we
186             // shove it into TLS and have someone else remove it later on.
187             let data = ~LLVMJITData { ee: ee, llcx: c };
188             set_engine(data as ~Engine);
189         }
190     }
191
192     // The stage1 compiler won't work, but that doesn't really matter. TLS
193     // changed only very recently to allow storage of owned values.
194     static engine_key: local_data::Key<~Engine> = &local_data::Key;
195
196     fn set_engine(engine: ~Engine) {
197         local_data::set(engine_key, engine)
198     }
199
200     pub fn consume_engine() -> Option<~Engine> {
201         local_data::pop(engine_key)
202     }
203 }
204
205 pub mod write {
206
207     use back::link::jit;
208     use back::link::{WriteOutputFile, output_type};
209     use back::link::{output_type_assembly, output_type_bitcode};
210     use back::link::{output_type_exe, output_type_llvm_assembly};
211     use back::link::{output_type_object};
212     use driver::session::Session;
213     use driver::session;
214     use lib::llvm::llvm;
215     use lib::llvm::{ModuleRef, ContextRef};
216     use lib;
217
218     use std::c_str::ToCStr;
219     use std::libc::{c_uint, c_int};
220     use std::path::Path;
221     use std::run;
222     use std::str;
223
224     pub fn run_passes(sess: Session,
225                       llcx: ContextRef,
226                       llmod: ModuleRef,
227                       output_type: output_type,
228                       output: &Path) {
229         unsafe {
230             llvm::LLVMInitializePasses();
231
232             // Only initialize the platforms supported by Rust here, because
233             // using --llvm-root will have multiple platforms that rustllvm
234             // doesn't actually link to and it's pointless to put target info
235             // into the registry that Rust can not generate machine code for.
236             llvm::LLVMInitializeX86TargetInfo();
237             llvm::LLVMInitializeX86Target();
238             llvm::LLVMInitializeX86TargetMC();
239             llvm::LLVMInitializeX86AsmPrinter();
240             llvm::LLVMInitializeX86AsmParser();
241
242             llvm::LLVMInitializeARMTargetInfo();
243             llvm::LLVMInitializeARMTarget();
244             llvm::LLVMInitializeARMTargetMC();
245             llvm::LLVMInitializeARMAsmPrinter();
246             llvm::LLVMInitializeARMAsmParser();
247
248             llvm::LLVMInitializeMipsTargetInfo();
249             llvm::LLVMInitializeMipsTarget();
250             llvm::LLVMInitializeMipsTargetMC();
251             llvm::LLVMInitializeMipsAsmPrinter();
252             llvm::LLVMInitializeMipsAsmParser();
253
254             if sess.opts.save_temps {
255                 do output.with_filetype("no-opt.bc").with_c_str |buf| {
256                     llvm::LLVMWriteBitcodeToFile(llmod, buf);
257                 }
258             }
259
260             configure_llvm(sess);
261
262             let OptLevel = match sess.opts.optimize {
263               session::No => lib::llvm::CodeGenLevelNone,
264               session::Less => lib::llvm::CodeGenLevelLess,
265               session::Default => lib::llvm::CodeGenLevelDefault,
266               session::Aggressive => lib::llvm::CodeGenLevelAggressive,
267             };
268
269             let tm = do sess.targ_cfg.target_strs.target_triple.with_c_str |T| {
270                 do sess.opts.target_cpu.with_c_str |CPU| {
271                     do sess.opts.target_feature.with_c_str |Features| {
272                         llvm::LLVMRustCreateTargetMachine(
273                             T, CPU, Features,
274                             lib::llvm::CodeModelDefault,
275                             lib::llvm::RelocPIC,
276                             OptLevel,
277                             true
278                         )
279                     }
280                 }
281             };
282
283             // Create the two optimizing pass managers. These mirror what clang
284             // does, and are by populated by LLVM's default PassManagerBuilder.
285             // Each manager has a different set of passes, but they also share
286             // some common passes.
287             let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod);
288             let mpm = llvm::LLVMCreatePassManager();
289
290             // If we're verifying or linting, add them to the function pass
291             // manager.
292             let addpass = |pass: &str| {
293                 do pass.with_c_str |s| { llvm::LLVMRustAddPass(fpm, s) }
294             };
295             if !sess.no_verify() { assert!(addpass("verify")); }
296             if sess.lint_llvm()  { assert!(addpass("lint"));   }
297
298             if !sess.no_prepopulate_passes() {
299                 llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
300                 llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
301                 populate_llvm_passess(fpm, mpm, llmod, OptLevel);
302             }
303
304             for pass in sess.opts.custom_passes.iter() {
305                 do pass.with_c_str |s| {
306                     if !llvm::LLVMRustAddPass(mpm, s) {
307                         sess.warn(fmt!("Unknown pass %s, ignoring", *pass));
308                     }
309                 }
310             }
311
312             // Finally, run the actual optimization passes
313             llvm::LLVMRustRunFunctionPassManager(fpm, llmod);
314             llvm::LLVMRunPassManager(mpm, llmod);
315
316             // Deallocate managers that we're now done with
317             llvm::LLVMDisposePassManager(fpm);
318             llvm::LLVMDisposePassManager(mpm);
319
320             if sess.opts.save_temps {
321                 do output.with_filetype("bc").with_c_str |buf| {
322                     llvm::LLVMWriteBitcodeToFile(llmod, buf);
323                 }
324             }
325
326             if sess.opts.jit {
327                 // If we are using JIT, go ahead and create and execute the
328                 // engine now. JIT execution takes ownership of the module and
329                 // context, so don't dispose
330                 jit::exec(sess, llcx, llmod, true);
331             } else {
332                 // Create a codegen-specific pass manager to emit the actual
333                 // assembly or object files. This may not end up getting used,
334                 // but we make it anyway for good measure.
335                 let cpm = llvm::LLVMCreatePassManager();
336                 llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod);
337                 llvm::LLVMRustAddLibraryInfo(cpm, llmod);
338
339                 match output_type {
340                     output_type_none => {}
341                     output_type_bitcode => {
342                         do output.with_c_str |buf| {
343                             llvm::LLVMWriteBitcodeToFile(llmod, buf);
344                         }
345                     }
346                     output_type_llvm_assembly => {
347                         do output.with_c_str |output| {
348                             llvm::LLVMRustPrintModule(cpm, llmod, output)
349                         }
350                     }
351                     output_type_assembly => {
352                         WriteOutputFile(sess, tm, cpm, llmod, output.to_str(),
353                                         lib::llvm::AssemblyFile);
354                     }
355                     output_type_exe | output_type_object => {
356                         WriteOutputFile(sess, tm, cpm, llmod, output.to_str(),
357                                         lib::llvm::ObjectFile);
358                     }
359                 }
360
361                 llvm::LLVMDisposePassManager(cpm);
362             }
363
364             llvm::LLVMRustDisposeTargetMachine(tm);
365             // the jit takes ownership of these two items
366             if !sess.opts.jit {
367                 llvm::LLVMDisposeModule(llmod);
368                 llvm::LLVMContextDispose(llcx);
369             }
370             if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); }
371         }
372     }
373
374     pub fn run_assembler(sess: Session, assembly: &Path, object: &Path) {
375         let cc_prog = super::get_cc_prog(sess);
376
377         let cc_args = ~[
378             ~"-c",
379             ~"-o", object.to_str(),
380             assembly.to_str()];
381
382         let prog = run::process_output(cc_prog, cc_args);
383
384         if prog.status != 0 {
385             sess.err(fmt!("building with `%s` failed with code %d",
386                         cc_prog, prog.status));
387             sess.note(fmt!("%s arguments: %s",
388                         cc_prog, cc_args.connect(" ")));
389             sess.note(str::from_bytes(prog.error + prog.output));
390             sess.abort_if_errors();
391         }
392     }
393
394     unsafe fn configure_llvm(sess: Session) {
395         // Copy what clan does by turning on loop vectorization at O2 and
396         // slp vectorization at O3
397         let vectorize_loop = !sess.no_vectorize_loops() &&
398                              (sess.opts.optimize == session::Default ||
399                               sess.opts.optimize == session::Aggressive);
400         let vectorize_slp = !sess.no_vectorize_slp() &&
401                             sess.opts.optimize == session::Aggressive;
402
403         let mut llvm_c_strs = ~[];
404         let mut llvm_args = ~[];
405         let add = |arg: &str| {
406             let s = arg.to_c_str();
407             llvm_args.push(s.with_ref(|p| p));
408             llvm_c_strs.push(s);
409         };
410         add("rustc"); // fake program name
411         add("-arm-enable-ehabi");
412         add("-arm-enable-ehabi-descriptors");
413         if vectorize_loop { add("-vectorize-loops"); }
414         if vectorize_slp  { add("-vectorize-slp");   }
415         if sess.time_llvm_passes() { add("-time-passes"); }
416         if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
417
418         for arg in sess.opts.llvm_args.iter() {
419             add(*arg);
420         }
421
422         do llvm_args.as_imm_buf |p, len| {
423             llvm::LLVMRustSetLLVMOptions(len as c_int, p);
424         }
425     }
426
427     unsafe fn populate_llvm_passess(fpm: lib::llvm::PassManagerRef,
428                                     mpm: lib::llvm::PassManagerRef,
429                                     llmod: ModuleRef,
430                                     opt: lib::llvm::CodeGenOptLevel) {
431         // Create the PassManagerBuilder for LLVM. We configure it with
432         // reasonable defaults and prepare it to actually populate the pass
433         // manager.
434         let builder = llvm::LLVMPassManagerBuilderCreate();
435         match opt {
436             lib::llvm::CodeGenLevelNone => {
437                 // Don't add lifetime intrinsics add O0
438                 llvm::LLVMRustAddAlwaysInlinePass(builder, false);
439             }
440             lib::llvm::CodeGenLevelLess => {
441                 llvm::LLVMRustAddAlwaysInlinePass(builder, true);
442             }
443             // numeric values copied from clang
444             lib::llvm::CodeGenLevelDefault => {
445                 llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
446                                                                     225);
447             }
448             lib::llvm::CodeGenLevelAggressive => {
449                 llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder,
450                                                                     275);
451             }
452         }
453         llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt as c_uint);
454         llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod);
455
456         // Use the builder to populate the function/module pass managers.
457         llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(builder, fpm);
458         llvm::LLVMPassManagerBuilderPopulateModulePassManager(builder, mpm);
459         llvm::LLVMPassManagerBuilderDispose(builder);
460     }
461 }
462
463
464 /*
465  * Name mangling and its relationship to metadata. This is complex. Read
466  * carefully.
467  *
468  * The semantic model of Rust linkage is, broadly, that "there's no global
469  * namespace" between crates. Our aim is to preserve the illusion of this
470  * model despite the fact that it's not *quite* possible to implement on
471  * modern linkers. We initially didn't use system linkers at all, but have
472  * been convinced of their utility.
473  *
474  * There are a few issues to handle:
475  *
476  *  - Linkers operate on a flat namespace, so we have to flatten names.
477  *    We do this using the C++ namespace-mangling technique. Foo::bar
478  *    symbols and such.
479  *
480  *  - Symbols with the same name but different types need to get different
481  *    linkage-names. We do this by hashing a string-encoding of the type into
482  *    a fixed-size (currently 16-byte hex) cryptographic hash function (CHF:
483  *    we use SHA1) to "prevent collisions". This is not airtight but 16 hex
484  *    digits on uniform probability means you're going to need 2**32 same-name
485  *    symbols in the same process before you're even hitting birthday-paradox
486  *    collision probability.
487  *
488  *  - Symbols in different crates but with same names "within" the crate need
489  *    to get different linkage-names.
490  *
491  * So here is what we do:
492  *
493  *  - Separate the meta tags into two sets: exported and local. Only work with
494  *    the exported ones when considering linkage.
495  *
496  *  - Consider two exported tags as special (and mandatory): name and vers.
497  *    Every crate gets them; if it doesn't name them explicitly we infer them
498  *    as basename(crate) and "0.1", respectively. Call these CNAME, CVERS.
499  *
500  *  - Define CMETA as all the non-name, non-vers exported meta tags in the
501  *    crate (in sorted order).
502  *
503  *  - Define CMH as hash(CMETA + hashes of dependent crates).
504  *
505  *  - Compile our crate to lib CNAME-CMH-CVERS.so
506  *
507  *  - Define STH(sym) as hash(CNAME, CMH, type_str(sym))
508  *
509  *  - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the
510  *    name, non-name metadata, and type sense, and versioned in the way
511  *    system linkers understand.
512  *
513  */
514
515 pub fn build_link_meta(sess: Session,
516                        c: &ast::Crate,
517                        output: &Path,
518                        symbol_hasher: &mut hash::State)
519                        -> LinkMeta {
520     struct ProvidedMetas {
521         name: Option<@str>,
522         vers: Option<@str>,
523         pkg_id: Option<@str>,
524         cmh_items: ~[@ast::MetaItem]
525     }
526
527     fn provided_link_metas(sess: Session, c: &ast::Crate) ->
528        ProvidedMetas {
529         let mut name = None;
530         let mut vers = None;
531         let mut pkg_id = None;
532         let mut cmh_items = ~[];
533         let linkage_metas = attr::find_linkage_metas(c.attrs);
534         attr::require_unique_names(sess.diagnostic(), linkage_metas);
535         for meta in linkage_metas.iter() {
536             match meta.name_str_pair() {
537                 Some((n, value)) if "name" == n => name = Some(value),
538                 Some((n, value)) if "vers" == n => vers = Some(value),
539                 Some((n, value)) if "package_id" == n => pkg_id = Some(value),
540                 _ => cmh_items.push(*meta)
541             }
542         }
543
544         ProvidedMetas {
545             name: name,
546             vers: vers,
547             pkg_id: pkg_id,
548             cmh_items: cmh_items
549         }
550     }
551
552     // This calculates CMH as defined above
553     fn crate_meta_extras_hash(symbol_hasher: &mut hash::State,
554                               cmh_items: ~[@ast::MetaItem],
555                               dep_hashes: ~[@str],
556                               pkg_id: Option<@str>) -> @str {
557         fn len_and_str(s: &str) -> ~str {
558             fmt!("%u_%s", s.len(), s)
559         }
560
561         fn len_and_str_lit(l: ast::lit) -> ~str {
562             len_and_str(pprust::lit_to_str(@l))
563         }
564
565         let cmh_items = attr::sort_meta_items(cmh_items);
566
567         fn hash(symbol_hasher: &mut hash::State, m: &@ast::MetaItem) {
568             match m.node {
569               ast::MetaNameValue(key, value) => {
570                 write_string(symbol_hasher, len_and_str(key));
571                 write_string(symbol_hasher, len_and_str_lit(value));
572               }
573               ast::MetaWord(name) => {
574                 write_string(symbol_hasher, len_and_str(name));
575               }
576               ast::MetaList(name, ref mis) => {
577                 write_string(symbol_hasher, len_and_str(name));
578                 for m_ in mis.iter() {
579                     hash(symbol_hasher, m_);
580                 }
581               }
582             }
583         }
584
585         symbol_hasher.reset();
586         for m in cmh_items.iter() {
587             hash(symbol_hasher, m);
588         }
589
590         for dh in dep_hashes.iter() {
591             write_string(symbol_hasher, len_and_str(*dh));
592         }
593
594         for p in pkg_id.iter() {
595             write_string(symbol_hasher, len_and_str(*p));
596         }
597
598         return truncated_hash_result(symbol_hasher).to_managed();
599     }
600
601     fn warn_missing(sess: Session, name: &str, default: &str) {
602         if !*sess.building_library { return; }
603         sess.warn(fmt!("missing crate link meta `%s`, using `%s` as default",
604                        name, default));
605     }
606
607     fn crate_meta_name(sess: Session, output: &Path, opt_name: Option<@str>)
608         -> @str {
609         match opt_name {
610             Some(v) => v,
611             None => {
612                 // to_managed could go away if there was a version of
613                 // filestem that returned an @str
614                 let name = session::expect(sess,
615                                            output.filestem(),
616                                            || fmt!("output file name `%s` doesn't\
617                                                     appear to have a stem",
618                                                    output.to_str())).to_managed();
619                 warn_missing(sess, "name", name);
620                 name
621             }
622         }
623     }
624
625     fn crate_meta_vers(sess: Session, opt_vers: Option<@str>) -> @str {
626         match opt_vers {
627             Some(v) => v,
628             None => {
629                 let vers = @"0.0";
630                 warn_missing(sess, "vers", vers);
631                 vers
632             }
633         }
634     }
635
636     let ProvidedMetas {
637         name: opt_name,
638         vers: opt_vers,
639         pkg_id: opt_pkg_id,
640         cmh_items: cmh_items
641     } = provided_link_metas(sess, c);
642     let name = crate_meta_name(sess, output, opt_name);
643     let vers = crate_meta_vers(sess, opt_vers);
644     let dep_hashes = cstore::get_dep_hashes(sess.cstore);
645     let extras_hash =
646         crate_meta_extras_hash(symbol_hasher, cmh_items,
647                                dep_hashes, opt_pkg_id);
648
649     LinkMeta {
650         name: name,
651         vers: vers,
652         package_id: opt_pkg_id,
653         extras_hash: extras_hash
654     }
655 }
656
657 pub fn truncated_hash_result(symbol_hasher: &mut hash::State) -> ~str {
658     symbol_hasher.result_str()
659 }
660
661
662 // This calculates STH for a symbol, as defined above
663 pub fn symbol_hash(tcx: ty::ctxt,
664                    symbol_hasher: &mut hash::State,
665                    t: ty::t,
666                    link_meta: LinkMeta)
667                    -> @str {
668     // NB: do *not* use abbrevs here as we want the symbol names
669     // to be independent of one another in the crate.
670
671     symbol_hasher.reset();
672     write_string(symbol_hasher, link_meta.name);
673     write_string(symbol_hasher, "-");
674     write_string(symbol_hasher, link_meta.extras_hash);
675     write_string(symbol_hasher, "-");
676     write_string(symbol_hasher, encoder::encoded_ty(tcx, t));
677     let mut hash = truncated_hash_result(symbol_hasher);
678     // Prefix with _ so that it never blends into adjacent digits
679     hash.unshift_char('_');
680     // tjc: allocation is unfortunate; need to change std::hash
681     hash.to_managed()
682 }
683
684 pub fn get_symbol_hash(ccx: &mut CrateContext, t: ty::t) -> @str {
685     match ccx.type_hashcodes.find(&t) {
686       Some(&h) => h,
687       None => {
688         let hash = symbol_hash(ccx.tcx, &mut ccx.symbol_hasher, t, ccx.link_meta);
689         ccx.type_hashcodes.insert(t, hash);
690         hash
691       }
692     }
693 }
694
695
696 // Name sanitation. LLVM will happily accept identifiers with weird names, but
697 // gas doesn't!
698 // gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $
699 pub fn sanitize(s: &str) -> ~str {
700     let mut result = ~"";
701     for c in s.iter() {
702         match c {
703             // Escape these with $ sequences
704             '@' => result.push_str("$SP$"),
705             '~' => result.push_str("$UP$"),
706             '*' => result.push_str("$RP$"),
707             '&' => result.push_str("$BP$"),
708             '<' => result.push_str("$LT$"),
709             '>' => result.push_str("$GT$"),
710             '(' => result.push_str("$LP$"),
711             ')' => result.push_str("$RP$"),
712             ',' => result.push_str("$C$"),
713
714             // '.' doesn't occur in types and functions, so reuse it
715             // for ':'
716             ':' => result.push_char('.'),
717
718             // These are legal symbols
719             'a' .. 'z'
720             | 'A' .. 'Z'
721             | '0' .. '9'
722             | '_' => result.push_char(c),
723
724             _ => {
725                 let mut tstr = ~"";
726                 do char::escape_unicode(c) |c| { tstr.push_char(c); }
727                 result.push_char('$');
728                 result.push_str(tstr.slice_from(1));
729             }
730         }
731     }
732
733     // Underscore-qualify anything that didn't start as an ident.
734     if result.len() > 0u &&
735         result[0] != '_' as u8 &&
736         ! char::is_XID_start(result[0] as char) {
737         return ~"_" + result;
738     }
739
740     return result;
741 }
742
743 pub fn mangle(sess: Session, ss: path) -> ~str {
744     // Follow C++ namespace-mangling style
745
746     let mut n = ~"_ZN"; // Begin name-sequence.
747
748     for s in ss.iter() {
749         match *s {
750             path_name(s) | path_mod(s) => {
751                 let sani = sanitize(sess.str_of(s));
752                 n.push_str(fmt!("%u%s", sani.len(), sani));
753             }
754         }
755     }
756     n.push_char('E'); // End name-sequence.
757     n
758 }
759
760 pub fn exported_name(sess: Session,
761                      path: path,
762                      hash: &str,
763                      vers: &str) -> ~str {
764     mangle(sess,
765            vec::append_one(
766                vec::append_one(path, path_name(sess.ident_of(hash))),
767                path_name(sess.ident_of(vers))))
768 }
769
770 pub fn mangle_exported_name(ccx: &mut CrateContext,
771                             path: path,
772                             t: ty::t) -> ~str {
773     let hash = get_symbol_hash(ccx, t);
774     return exported_name(ccx.sess, path,
775                          hash,
776                          ccx.link_meta.vers);
777 }
778
779 pub fn mangle_internal_name_by_type_only(ccx: &mut CrateContext,
780                                          t: ty::t,
781                                          name: &str) -> ~str {
782     let s = ppaux::ty_to_short_str(ccx.tcx, t);
783     let hash = get_symbol_hash(ccx, t);
784     return mangle(ccx.sess,
785         ~[path_name(ccx.sess.ident_of(name)),
786           path_name(ccx.sess.ident_of(s)),
787           path_name(ccx.sess.ident_of(hash))]);
788 }
789
790 pub fn mangle_internal_name_by_type_and_seq(ccx: &mut CrateContext,
791                                          t: ty::t,
792                                          name: &str) -> ~str {
793     let s = ppaux::ty_to_str(ccx.tcx, t);
794     let hash = get_symbol_hash(ccx, t);
795     return mangle(ccx.sess,
796         ~[path_name(ccx.sess.ident_of(s)),
797           path_name(ccx.sess.ident_of(hash)),
798           path_name(gensym_name(name))]);
799 }
800
801 pub fn mangle_internal_name_by_path_and_seq(ccx: &mut CrateContext,
802                                             mut path: path,
803                                             flav: &str) -> ~str {
804     path.push(path_name(gensym_name(flav)));
805     mangle(ccx.sess, path)
806 }
807
808 pub fn mangle_internal_name_by_path(ccx: &mut CrateContext, path: path) -> ~str {
809     mangle(ccx.sess, path)
810 }
811
812 pub fn mangle_internal_name_by_seq(_ccx: &mut CrateContext, flav: &str) -> ~str {
813     return fmt!("%s_%u", flav, token::gensym(flav));
814 }
815
816
817 pub fn output_dll_filename(os: session::Os, lm: LinkMeta) -> ~str {
818     let (dll_prefix, dll_suffix) = match os {
819         session::OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX),
820         session::OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX),
821         session::OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX),
822         session::OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX),
823         session::OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
824     };
825     fmt!("%s%s-%s-%s%s", dll_prefix, lm.name, lm.extras_hash, lm.vers, dll_suffix)
826 }
827
828 pub fn get_cc_prog(sess: Session) -> ~str {
829     // In the future, FreeBSD will use clang as default compiler.
830     // It would be flexible to use cc (system's default C compiler)
831     // instead of hard-coded gcc.
832     // For win32, there is no cc command, so we add a condition to make it use g++.
833     // We use g++ rather than gcc because it automatically adds linker options required
834     // for generation of dll modules that correctly register stack unwind tables.
835     match sess.opts.linker {
836         Some(ref linker) => linker.to_str(),
837         None => match sess.targ_cfg.os {
838             session::OsAndroid =>
839                 match &sess.opts.android_cross_path {
840                     &Some(ref path) => {
841                         fmt!("%s/bin/arm-linux-androideabi-gcc", *path)
842                     }
843                     &None => {
844                         sess.fatal("need Android NDK path for linking \
845                                     (--android-cross-path)")
846                     }
847                 },
848             session::OsWin32 => ~"g++",
849             _ => ~"cc"
850         }
851     }
852 }
853
854 // If the user wants an exe generated we need to invoke
855 // cc to link the object file with some libs
856 pub fn link_binary(sess: Session,
857                    obj_filename: &Path,
858                    out_filename: &Path,
859                    lm: LinkMeta) {
860
861     let cc_prog = get_cc_prog(sess);
862     // The invocations of cc share some flags across platforms
863
864     let output = if *sess.building_library {
865         let long_libname = output_dll_filename(sess.targ_cfg.os, lm);
866         debug!("link_meta.name:  %s", lm.name);
867         debug!("long_libname: %s", long_libname);
868         debug!("out_filename: %s", out_filename.to_str());
869         debug!("dirname(out_filename): %s", out_filename.dir_path().to_str());
870
871         out_filename.dir_path().push(long_libname)
872     } else {
873         out_filename.clone()
874     };
875
876     debug!("output: %s", output.to_str());
877     let cc_args = link_args(sess, obj_filename, out_filename, lm);
878     debug!("%s link args: %s", cc_prog, cc_args.connect(" "));
879     if (sess.opts.debugging_opts & session::print_link_args) != 0 {
880         io::println(fmt!("%s link args: %s", cc_prog, cc_args.connect(" ")));
881     }
882
883     // We run 'cc' here
884     let prog = run::process_output(cc_prog, cc_args);
885     if 0 != prog.status {
886         sess.err(fmt!("linking with `%s` failed with code %d",
887                       cc_prog, prog.status));
888         sess.note(fmt!("%s arguments: %s",
889                        cc_prog, cc_args.connect(" ")));
890         sess.note(str::from_bytes(prog.error + prog.output));
891         sess.abort_if_errors();
892     }
893
894     // Clean up on Darwin
895     if sess.targ_cfg.os == session::OsMacos {
896         run::process_status("dsymutil", [output.to_str()]);
897     }
898
899     // Remove the temporary object file if we aren't saving temps
900     if !sess.opts.save_temps {
901         if ! os::remove_file(obj_filename) {
902             sess.warn(fmt!("failed to delete object file `%s`",
903                            obj_filename.to_str()));
904         }
905     }
906 }
907
908 pub fn link_args(sess: Session,
909                  obj_filename: &Path,
910                  out_filename: &Path,
911                  lm:LinkMeta) -> ~[~str] {
912
913     // Converts a library file-stem into a cc -l argument
914     fn unlib(config: @session::config, stem: ~str) -> ~str {
915         if stem.starts_with("lib") &&
916             config.os != session::OsWin32 {
917             stem.slice(3, stem.len()).to_owned()
918         } else {
919             stem
920         }
921     }
922
923
924     let output = if *sess.building_library {
925         let long_libname = output_dll_filename(sess.targ_cfg.os, lm);
926         out_filename.dir_path().push(long_libname)
927     } else {
928         out_filename.clone()
929     };
930
931     // The default library location, we need this to find the runtime.
932     // The location of crates will be determined as needed.
933     let stage: ~str = ~"-L" + sess.filesearch.get_target_lib_path().to_str();
934
935     let mut args = vec::append(~[stage], sess.targ_cfg.target_strs.cc_args);
936
937     args.push_all([
938         ~"-o", output.to_str(),
939         obj_filename.to_str()]);
940
941     let lib_cmd = match sess.targ_cfg.os {
942         session::OsMacos => ~"-dynamiclib",
943         _ => ~"-shared"
944     };
945
946     // # Crate linking
947
948     let cstore = sess.cstore;
949     let r = cstore::get_used_crate_files(cstore);
950     for cratepath in r.iter() {
951         if cratepath.filetype() == Some(~".rlib") {
952             args.push(cratepath.to_str());
953             loop;
954         }
955         let dir = cratepath.dirname();
956         if dir != ~"" { args.push(~"-L" + dir); }
957         let libarg = unlib(sess.targ_cfg, cratepath.filestem().unwrap());
958         args.push(~"-l" + libarg);
959     }
960
961     let ula = cstore::get_used_link_args(cstore);
962     for arg in ula.iter() { args.push(arg.to_owned()); }
963
964     // Add all the link args for external crates.
965     do cstore::iter_crate_data(cstore) |crate_num, _| {
966         let link_args = csearch::get_link_args_for_crate(cstore, crate_num);
967         for link_arg in link_args.move_iter() {
968             args.push(link_arg);
969         }
970     }
971
972     // # Extern library linking
973
974     // User-supplied library search paths (-L on the cammand line) These are
975     // the same paths used to find Rust crates, so some of them may have been
976     // added already by the previous crate linking code. This only allows them
977     // to be found at compile time so it is still entirely up to outside
978     // forces to make sure that library can be found at runtime.
979
980     for path in sess.opts.addl_lib_search_paths.iter() {
981         args.push(~"-L" + path.to_str());
982     }
983
984     let rustpath = filesearch::rust_path();
985     for path in rustpath.iter() {
986         args.push(~"-L" + path.to_str());
987     }
988
989     // The names of the extern libraries
990     let used_libs = cstore::get_used_libraries(cstore);
991     for l in used_libs.iter() { args.push(~"-l" + *l); }
992
993     if *sess.building_library {
994         args.push(lib_cmd);
995
996         // On mac we need to tell the linker to let this library
997         // be rpathed
998         if sess.targ_cfg.os == session::OsMacos {
999             args.push(~"-Wl,-install_name,@rpath/"
1000                       + output.filename().unwrap());
1001         }
1002     }
1003
1004     // On linux librt and libdl are an indirect dependencies via rustrt,
1005     // and binutils 2.22+ won't add them automatically
1006     if sess.targ_cfg.os == session::OsLinux {
1007         args.push_all([~"-lrt", ~"-ldl"]);
1008
1009         // LLVM implements the `frem` instruction as a call to `fmod`,
1010         // which lives in libm. Similar to above, on some linuxes we
1011         // have to be explicit about linking to it. See #2510
1012         args.push(~"-lm");
1013     }
1014     else if sess.targ_cfg.os == session::OsAndroid {
1015         args.push_all([~"-ldl", ~"-llog",  ~"-lsupc++", ~"-lgnustl_shared"]);
1016         args.push(~"-lm");
1017     }
1018
1019     if sess.targ_cfg.os == session::OsFreebsd {
1020         args.push_all([~"-pthread", ~"-lrt",
1021                        ~"-L/usr/local/lib", ~"-lexecinfo",
1022                        ~"-L/usr/local/lib/gcc46",
1023                        ~"-L/usr/local/lib/gcc44", ~"-lstdc++",
1024                        ~"-Wl,-z,origin",
1025                        ~"-Wl,-rpath,/usr/local/lib/gcc46",
1026                        ~"-Wl,-rpath,/usr/local/lib/gcc44"]);
1027     }
1028
1029     // OS X 10.6 introduced 'compact unwind info', which is produced by the
1030     // linker from the dwarf unwind info. Unfortunately, it does not seem to
1031     // understand how to unwind our __morestack frame, so we have to turn it
1032     // off. This has impacted some other projects like GHC.
1033     if sess.targ_cfg.os == session::OsMacos {
1034         args.push(~"-Wl,-no_compact_unwind");
1035     }
1036
1037     // Stack growth requires statically linking a __morestack function
1038     args.push(~"-lmorestack");
1039
1040     // Always want the runtime linked in
1041     args.push(~"-lrustrt");
1042
1043     // FIXME (#2397): At some point we want to rpath our guesses as to where
1044     // extern libraries might live, based on the addl_lib_search_paths
1045     args.push_all(rpath::get_rpath_flags(sess, &output));
1046
1047     // Finally add all the linker arguments provided on the command line
1048     args.push_all(sess.opts.linker_args);
1049
1050     return args;
1051 }