Skip to content

Commit b349036

Browse files
committed
Make crate hash stable and externally computable.
This replaces the link meta attributes with a pkgid attribute and uses a hash of this as the crate hash. This makes the crate hash computable by things other than the Rust compiler. It also switches the hash function ot SHA1 since that is much more likely to be available in shell, Python, etc than SipHash. Fixes #10188, #8523.
1 parent 29ca435 commit b349036

File tree

111 files changed

+1261
-604
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+1261
-604
lines changed

src/driver/driver.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
// except according to those terms.
1010

1111
#[cfg(rustpkg)]
12-
extern mod this(name = "rustpkg");
12+
extern mod this = "rustpkg";
1313

1414
#[cfg(rustdoc)]
15-
extern mod this(name = "rustdoc");
15+
extern mod this = "rustdoc";
1616

1717
#[cfg(rustc)]
18-
extern mod this(name = "rustc");
18+
extern mod this = "rustc";
1919

2020
#[cfg(rustdoc_ng)]
21-
extern mod this(name = "rustdoc_ng");
21+
extern mod this = "rustdoc_ng";
2222

2323
fn main() { this::main() }

src/etc/combine-tests.py

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def scrub(b):
4242
c = open("tmp/run_pass_stage2.rc", "w")
4343
i = 0
4444
c.write("// AUTO-GENERATED FILE: DO NOT EDIT\n")
45+
c.write("#[pkgid=\"run_pass_stage2#0.1\"];\n")
4546
c.write("#[link(name=\"run_pass_stage2\", vers=\"0.1\")];\n")
4647
c.write("#[feature(globs, macro_rules, struct_variant, managed_boxes)];\n")
4748
c.write("#[allow(attribute_usage)];\n")

src/libextra/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Rust extras are part of the standard Rust distribution.
2020
2121
*/
2222

23+
#[pkgid="extra#0.9-pre"];
24+
// NOTE: remove after the next snapshot
2325
#[link(name = "extra",
2426
package_id = "extra",
2527
vers = "0.9-pre",

src/librustc/back/link.rs

+55-182
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@ use middle::trans::common::gensym_name;
2424
use middle::ty;
2525
use util::common::time;
2626
use util::ppaux;
27+
use util::sha2::{Digest, Sha256};
2728

2829
use std::c_str::ToCStr;
2930
use std::char;
30-
use std::hash::Streaming;
31-
use std::hash;
3231
use std::os::consts::{macos, freebsd, linux, android, win32};
3332
use std::ptr;
3433
use std::run;
@@ -39,8 +38,8 @@ use syntax::abi;
3938
use syntax::ast;
4039
use syntax::ast_map::{path, path_mod, path_name, path_pretty_name};
4140
use 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)]
4645
pub 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-
5954
pub 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

448444
pub 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
614482
pub 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

780648
pub 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

820691
pub fn get_cc_prog(sess: Session) -> ~str {
@@ -848,7 +719,8 @@ pub fn get_cc_prog(sess: Session) -> ~str {
848719
pub 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

Comments
 (0)