@@ -24,11 +24,10 @@ use middle::trans::common::gensym_name;
2424use middle:: ty;
2525use util:: common:: time;
2626use util:: ppaux;
27+ use util:: sha2:: { Digest , Sha256 } ;
2728
2829use std:: c_str:: ToCStr ;
2930use std:: char;
30- use std:: hash:: Streaming ;
31- use std:: hash;
3231use std:: os:: consts:: { macos, freebsd, linux, android, win32} ;
3332use std:: ptr;
3433use std:: run;
@@ -39,8 +38,8 @@ use syntax::abi;
3938use syntax:: ast;
4039use syntax:: ast_map:: { path, path_mod, path_name, path_pretty_name} ;
4140use syntax:: attr;
42- use syntax:: attr:: { AttrMetaMethods } ;
43- use syntax:: print :: pprust ;
41+ use syntax:: attr:: AttrMetaMethods ;
42+ use syntax:: pkgid :: PkgId ;
4443
4544#[ deriving( Clone , Eq ) ]
4645pub enum output_type {
@@ -52,10 +51,6 @@ pub enum output_type {
5251 output_type_exe,
5352}
5453
55- fn write_string < W : Writer > ( writer : & mut W , string : & str ) {
56- writer. write ( string. as_bytes ( ) ) ;
57- }
58-
5954pub fn llvm_err ( sess : Session , msg : ~str ) -> ! {
6055 unsafe {
6156 let cstr = llvm:: LLVMRustGetLastError ( ) ;
@@ -413,217 +408,90 @@ pub mod write {
413408 * - Symbols with the same name but different types need to get different
414409 * linkage-names. We do this by hashing a string-encoding of the type into
415410 * a fixed-size (currently 16-byte hex) cryptographic hash function (CHF:
416- * we use SHA1 ) to "prevent collisions". This is not airtight but 16 hex
411+ * we use SHA256 ) to "prevent collisions". This is not airtight but 16 hex
417412 * digits on uniform probability means you're going to need 2**32 same-name
418413 * symbols in the same process before you're even hitting birthday-paradox
419414 * collision probability.
420415 *
421416 * - Symbols in different crates but with same names "within" the crate need
422417 * to get different linkage-names.
423418 *
424- * So here is what we do:
419+ * - The hash shown in the filename needs to be predictable and stable for
420+ * build tooling integration. It also needs to be using a hash function
421+ * which is easy to use from Python, make, etc.
425422 *
426- * - Separate the meta tags into two sets: exported and local. Only work with
427- * the exported ones when considering linkage.
423+ * So here is what we do:
428424 *
429- * - Consider two exported tags as special (and mandatory): name and vers.
430- * Every crate gets them; if it doesn't name them explicitly we infer them
431- * as basename(crate) and "0.1", respectively. Call these CNAME, CVERS.
425+ * - Consider the package id; every crate has one (specified with pkgid
426+ * attribute). If a package id isn't provided explicitly, we infer a
427+ * versionless one from the output name. The version will end up being 0.0
428+ * in this case. CNAME and CVERS are taken from this package id. For
429+ * example, github.com/mozilla/CNAME#CVERS.
432430 *
433- * - Define CMETA as all the non-name, non-vers exported meta tags in the
434- * crate (in sorted order).
431+ * - Define CMH as SHA256(pkgid).
435432 *
436- * - Define CMH as hash(CMETA + hashes of dependent crates) .
433+ * - Define CMH8 as the first 8 characters of CMH .
437434 *
438- * - Compile our crate to lib CNAME-CMH -CVERS.so
435+ * - Compile our crate to lib CNAME-CMH8 -CVERS.so
439436 *
440- * - Define STH(sym) as hash(CNAME, CMH, type_str(sym))
437+ * - Define STH(sym) as SHA256( CMH, type_str(sym))
441438 *
442439 * - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the
443440 * name, non-name metadata, and type sense, and versioned in the way
444441 * system linkers understand.
445- *
446442 */
447443
448444pub fn build_link_meta ( sess : Session ,
449445 c : & ast:: Crate ,
450446 output : & Path ,
451- symbol_hasher : & mut hash :: State )
447+ symbol_hasher : & mut Sha256 )
452448 -> LinkMeta {
453- struct ProvidedMetas {
454- name : Option < @str > ,
455- vers : Option < @str > ,
456- pkg_id : Option < @str > ,
457- cmh_items : ~[ @ast:: MetaItem ]
458- }
459-
460- fn provided_link_metas ( sess : Session , c : & ast:: Crate ) ->
461- ProvidedMetas {
462- let mut name = None ;
463- let mut vers = None ;
464- let mut pkg_id = None ;
465- let mut cmh_items = ~[ ] ;
466- let linkage_metas = attr:: find_linkage_metas ( c. attrs ) ;
467- attr:: require_unique_names ( sess. diagnostic ( ) , linkage_metas) ;
468- for meta in linkage_metas. iter ( ) {
469- match meta. name_str_pair ( ) {
470- Some ( ( n, value) ) if "name" == n => name = Some ( value) ,
471- Some ( ( n, value) ) if "vers" == n => vers = Some ( value) ,
472- Some ( ( n, value) ) if "package_id" == n => pkg_id = Some ( value) ,
473- _ => cmh_items. push ( * meta)
474- }
475- }
476-
477- ProvidedMetas {
478- name : name,
479- vers : vers,
480- pkg_id : pkg_id,
481- cmh_items : cmh_items
482- }
483- }
484-
485449 // This calculates CMH as defined above
486- fn crate_meta_extras_hash ( symbol_hasher : & mut hash:: State ,
487- cmh_items : ~[ @ast:: MetaItem ] ,
488- dep_hashes : ~[ @str ] ,
489- pkg_id : Option < @str > ) -> @str {
490- fn len_and_str ( s : & str ) -> ~str {
491- format ! ( "{}_{}" , s. len( ) , s)
492- }
493-
494- fn len_and_str_lit ( l : ast:: lit ) -> ~str {
495- len_and_str ( pprust:: lit_to_str ( & l) )
496- }
497-
498- let cmh_items = attr:: sort_meta_items ( cmh_items) ;
499-
500- fn hash ( symbol_hasher : & mut hash:: State , m : & @ast:: MetaItem ) {
501- match m. node {
502- ast:: MetaNameValue ( key, value) => {
503- write_string ( symbol_hasher, len_and_str ( key) ) ;
504- write_string ( symbol_hasher, len_and_str_lit ( value) ) ;
505- }
506- ast:: MetaWord ( name) => {
507- write_string ( symbol_hasher, len_and_str ( name) ) ;
508- }
509- ast:: MetaList ( name, ref mis) => {
510- write_string ( symbol_hasher, len_and_str ( name) ) ;
511- for m_ in mis. iter ( ) {
512- hash ( symbol_hasher, m_) ;
513- }
514- }
515- }
516- }
517-
450+ fn crate_hash ( symbol_hasher : & mut Sha256 , pkgid : & PkgId ) -> @str {
518451 symbol_hasher. reset ( ) ;
519- for m in cmh_items. iter ( ) {
520- hash ( symbol_hasher, m) ;
521- }
522-
523- for dh in dep_hashes. iter ( ) {
524- write_string ( symbol_hasher, len_and_str ( * dh) ) ;
525- }
526-
527- for p in pkg_id. iter ( ) {
528- write_string ( symbol_hasher, len_and_str ( * p) ) ;
529- }
530-
531- return truncated_hash_result ( symbol_hasher) . to_managed ( ) ;
532- }
533-
534- fn warn_missing ( sess : Session , name : & str , default : & str ) {
535- if !* sess. building_library { return ; }
536- sess. warn ( format ! ( "missing crate link meta `{}`, using `{}` as default" ,
537- name, default ) ) ;
538- }
539-
540- fn crate_meta_name ( sess : Session , output : & Path , opt_name : Option < @str > )
541- -> @str {
542- match opt_name {
543- Some ( v) if !v. is_empty ( ) => v,
544- _ => {
545- // to_managed could go away if there was a version of
546- // filestem that returned an @str
547- // FIXME (#9639): Non-utf8 filenames will give a misleading error
548- let name = session:: expect ( sess,
549- output. filestem_str ( ) ,
550- || format ! ( "output file name `{}` doesn't\
551- appear to have a stem",
552- output. display( ) ) ) . to_managed ( ) ;
553- if name. is_empty ( ) {
554- sess. fatal ( "missing crate link meta `name`, and the \
555- inferred name is blank") ;
556- }
557- warn_missing ( sess, "name" , name) ;
558- name
559- }
560- }
452+ symbol_hasher. input_str ( pkgid. to_str ( ) ) ;
453+ truncated_hash_result ( symbol_hasher) . to_managed ( )
561454 }
562455
563- fn crate_meta_vers ( sess : Session , opt_vers : Option < @ str > ) -> @ str {
564- match opt_vers {
565- Some ( v ) if !v . is_empty ( ) => v ,
566- _ => {
567- let vers = @" 0.0 " ;
568- warn_missing ( sess , "vers" , vers ) ;
569- vers
570- }
456+ let pkgid = match attr :: find_pkgid ( c . attrs ) {
457+ None => {
458+ let stem = session :: expect (
459+ sess ,
460+ output . filestem_str ( ) ,
461+ || format ! ( "output file name '{}' doesn't appear to have a stem" ,
462+ output . display ( ) ) ) ;
463+ from_str ( stem ) . unwrap ( )
571464 }
572- }
573-
574- fn crate_meta_pkgid ( sess : Session , name : @str , opt_pkg_id : Option < @str > )
575- -> @str {
576- match opt_pkg_id {
577- Some ( v) if !v. is_empty ( ) => v,
578- _ => {
579- let pkg_id = name. clone ( ) ;
580- warn_missing ( sess, "package_id" , pkg_id) ;
581- pkg_id
582- }
583- }
584- }
465+ Some ( s) => s,
466+ } ;
585467
586- let ProvidedMetas {
587- name : opt_name,
588- vers : opt_vers,
589- pkg_id : opt_pkg_id,
590- cmh_items : cmh_items
591- } = provided_link_metas ( sess, c) ;
592- let name = crate_meta_name ( sess, output, opt_name) ;
593- let vers = crate_meta_vers ( sess, opt_vers) ;
594- let pkg_id = crate_meta_pkgid ( sess, name, opt_pkg_id) ;
595- let dep_hashes = cstore:: get_dep_hashes ( sess. cstore ) ;
596- let extras_hash =
597- crate_meta_extras_hash ( symbol_hasher, cmh_items,
598- dep_hashes, Some ( pkg_id) ) ;
468+ let hash = crate_hash ( symbol_hasher, & pkgid) ;
599469
600470 LinkMeta {
601- name : name,
602- vers : vers,
603- package_id : Some ( pkg_id) ,
604- extras_hash : extras_hash
471+ pkgid : pkgid,
472+ crate_hash : hash,
605473 }
606474}
607475
608- pub fn truncated_hash_result ( symbol_hasher : & mut hash :: State ) -> ~str {
476+ pub fn truncated_hash_result ( symbol_hasher : & mut Sha256 ) -> ~str {
609477 symbol_hasher. result_str ( )
610478}
611479
612480
613481// This calculates STH for a symbol, as defined above
614482pub fn symbol_hash ( tcx : ty:: ctxt ,
615- symbol_hasher : & mut hash :: State ,
483+ symbol_hasher : & mut Sha256 ,
616484 t : ty:: t ,
617- link_meta : LinkMeta ) -> @str {
485+ link_meta : & LinkMeta ) -> @str {
618486 // NB: do *not* use abbrevs here as we want the symbol names
619487 // to be independent of one another in the crate.
620488
621489 symbol_hasher. reset ( ) ;
622- write_string ( symbol_hasher, link_meta. name ) ;
623- write_string ( symbol_hasher, "-" ) ;
624- write_string ( symbol_hasher, link_meta. extras_hash ) ;
625- write_string ( symbol_hasher, "-" ) ;
626- write_string ( symbol_hasher, encoder:: encoded_ty ( tcx, t) ) ;
490+ symbol_hasher. input_str ( link_meta. pkgid . name ) ;
491+ symbol_hasher. input_str ( "-" ) ;
492+ symbol_hasher. input_str ( link_meta. crate_hash ) ;
493+ symbol_hasher. input_str ( "-" ) ;
494+ symbol_hasher. input_str ( encoder:: encoded_ty ( tcx, t) ) ;
627495 let mut hash = truncated_hash_result ( symbol_hasher) ;
628496 // Prefix with 'h' so that it never blends into adjacent digits
629497 hash. unshift_char ( 'h' ) ;
@@ -635,7 +503,7 @@ pub fn get_symbol_hash(ccx: &mut CrateContext, t: ty::t) -> @str {
635503 match ccx. type_hashcodes . find ( & t) {
636504 Some ( & h) => h,
637505 None => {
638- let hash = symbol_hash ( ccx. tcx , & mut ccx. symbol_hasher , t, ccx. link_meta ) ;
506+ let hash = symbol_hash ( ccx. tcx , & mut ccx. symbol_hasher , t, & ccx. link_meta ) ;
639507 ccx. type_hashcodes . insert ( t, hash) ;
640508 hash
641509 }
@@ -774,7 +642,7 @@ pub fn mangle_exported_name(ccx: &mut CrateContext,
774642 let hash = get_symbol_hash ( ccx, t) ;
775643 return exported_name ( ccx. sess , path,
776644 hash,
777- ccx. link_meta . vers ) ;
645+ ccx. link_meta . pkgid . version_or_default ( ) ) ;
778646}
779647
780648pub fn mangle_internal_name_by_type_only ( ccx : & mut CrateContext ,
@@ -813,8 +681,11 @@ pub fn mangle_internal_name_by_path(ccx: &mut CrateContext, path: path) -> ~str
813681 mangle ( ccx. sess , path, None , None )
814682}
815683
816- pub fn output_lib_filename ( lm : LinkMeta ) -> ~str {
817- format ! ( "{}-{}-{}" , lm. name, lm. extras_hash, lm. vers)
684+ pub fn output_lib_filename ( lm : & LinkMeta ) -> ~str {
685+ format ! ( "{}-{}-{}" ,
686+ lm. pkgid. name,
687+ lm. crate_hash. slice_chars( 0 , 8 ) ,
688+ lm. pkgid. version_or_default( ) )
818689}
819690
820691pub fn get_cc_prog ( sess : Session ) -> ~str {
@@ -848,7 +719,8 @@ pub fn get_cc_prog(sess: Session) -> ~str {
848719pub fn link_binary ( sess : Session ,
849720 trans : & CrateTranslation ,
850721 obj_filename : & Path ,
851- out_filename : & Path ) {
722+ out_filename : & Path ,
723+ lm : & LinkMeta ) {
852724 // If we're generating a test executable, then ignore all other output
853725 // styles at all other locations
854726 let outputs = if sess. opts . test {
@@ -858,7 +730,7 @@ pub fn link_binary(sess: Session,
858730 } ;
859731
860732 for output in outputs. move_iter ( ) {
861- link_binary_output ( sess, trans, output, obj_filename, out_filename) ;
733+ link_binary_output ( sess, trans, output, obj_filename, out_filename, lm ) ;
862734 }
863735
864736 // Remove the temporary object file and metadata if we aren't saving temps
@@ -881,8 +753,9 @@ fn link_binary_output(sess: Session,
881753 trans : & CrateTranslation ,
882754 output : session:: OutputStyle ,
883755 obj_filename : & Path ,
884- out_filename : & Path ) {
885- let libname = output_lib_filename ( trans. link ) ;
756+ out_filename : & Path ,
757+ lm : & LinkMeta ) {
758+ let libname = output_lib_filename ( lm) ;
886759 let out_filename = match output {
887760 session:: OutputRlib => {
888761 out_filename. with_filename ( format ! ( "lib{}.rlib" , libname) )
0 commit comments