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