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