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.
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.
11 //! Finds crate binaries and loads their metadata
13 use back::archive::{ArchiveRO, METADATA_FILENAME};
15 use driver::session::Session;
16 use lib::llvm::{False, llvm, ObjectFile, mk_section_iter};
17 use metadata::cstore::{MetadataBlob, MetadataVec, MetadataArchive};
18 use metadata::decoder;
19 use metadata::encoder;
20 use metadata::filesearch::{FileSearch, FileMatches, FileDoesntMatch};
22 use syntax::codemap::Span;
23 use syntax::diagnostic::SpanHandler;
26 use std::c_str::ToCStr;
34 use std::collections::{HashMap, HashSet};
38 pub static MACOS_DLL_PREFIX: &'static str = "lib";
39 pub static MACOS_DLL_SUFFIX: &'static str = ".dylib";
41 pub static WIN32_DLL_PREFIX: &'static str = "";
42 pub static WIN32_DLL_SUFFIX: &'static str = ".dll";
44 pub static LINUX_DLL_PREFIX: &'static str = "lib";
45 pub static LINUX_DLL_SUFFIX: &'static str = ".so";
47 pub static FREEBSD_DLL_PREFIX: &'static str = "lib";
48 pub static FREEBSD_DLL_SUFFIX: &'static str = ".so";
50 pub static ANDROID_DLL_PREFIX: &'static str = "lib";
51 pub static ANDROID_DLL_SUFFIX: &'static str = ".so";
53 pub struct CrateMismatch {
58 pub struct Context<'a> {
59 pub sess: &'a Session,
62 pub crate_name: &'a str,
63 pub hash: Option<&'a Svh>,
66 pub filesearch: FileSearch<'a>,
67 pub root: &'a Option<CratePaths>,
68 pub rejected_via_hash: Vec<CrateMismatch>,
69 pub rejected_via_triple: Vec<CrateMismatch>,
73 pub dylib: Option<Path>,
74 pub rlib: Option<Path>,
75 pub metadata: MetadataBlob,
78 pub struct ArchiveMetadata {
80 // See comments in ArchiveMetadata::new for why this is static
84 pub struct CratePaths {
86 pub dylib: Option<Path>,
87 pub rlib: Option<Path>
91 fn paths(&self) -> Vec<Path> {
92 match (&self.dylib, &self.rlib) {
93 (&None, &None) => vec!(),
94 (&Some(ref p), &None) |
95 (&None, &Some(ref p)) => vec!(p.clone()),
96 (&Some(ref p1), &Some(ref p2)) => vec!(p1.clone(), p2.clone()),
101 impl<'a> Context<'a> {
102 pub fn maybe_load_library_crate(&mut self) -> Option<Library> {
103 self.find_library_crate()
106 pub fn load_library_crate(&mut self) -> Library {
107 match self.find_library_crate() {
110 self.report_load_errs();
116 pub fn report_load_errs(&mut self) {
117 let message = if self.rejected_via_hash.len() > 0 {
118 format!("found possibly newer version of crate `{}`",
120 } else if self.rejected_via_triple.len() > 0 {
121 format!("found incorrect triple for crate `{}`", self.ident)
123 format!("can't find crate for `{}`", self.ident)
125 let message = match self.root {
127 &Some(ref r) => format!("{} which `{}` depends on",
130 self.sess.span_err(self.span, message.as_slice());
132 let mismatches = self.rejected_via_triple.iter();
133 if self.rejected_via_triple.len() > 0 {
134 self.sess.span_note(self.span,
135 format!("expected triple of {}",
136 self.triple).as_slice());
137 for (i, &CrateMismatch{ ref path, ref got }) in mismatches.enumerate() {
138 self.sess.fileline_note(self.span,
139 format!("crate `{}` path {}{}, triple {}: {}",
140 self.ident, "#", i+1, got, path.display()).as_slice());
143 if self.rejected_via_hash.len() > 0 {
144 self.sess.span_note(self.span, "perhaps this crate needs \
146 let mismatches = self.rejected_via_hash.iter();
147 for (i, &CrateMismatch{ ref path, .. }) in mismatches.enumerate() {
148 self.sess.fileline_note(self.span,
149 format!("crate `{}` path {}{}: {}",
150 self.ident, "#", i+1, path.display()).as_slice());
155 for (i, path) in r.paths().iter().enumerate() {
156 self.sess.fileline_note(self.span,
157 format!("crate `{}` path #{}: {}",
158 r.ident, i+1, path.display()).as_slice());
163 self.sess.abort_if_errors();
166 fn find_library_crate(&mut self) -> Option<Library> {
167 let dypair = self.dylibname();
169 // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
170 let dylib_prefix = dypair.map(|(prefix, _)| {
171 format!("{}{}", prefix, self.crate_name)
173 let rlib_prefix = format!("lib{}", self.crate_name);
175 let mut candidates = HashMap::new();
177 // First, find all possible candidate rlibs and dylibs purely based on
178 // the name of the files themselves. We're trying to match against an
179 // exact crate name and a possibly an exact hash.
181 // During this step, we can filter all found libraries based on the
182 // name and id found in the crate id (we ignore the path portion for
183 // filename matching), as well as the exact hash (if specified). If we
184 // end up having many candidates, we must look at the metadata to
185 // perform exact matches against hashes/crate ids. Note that opening up
186 // the metadata is where we do an exact match against the full contents
187 // of the crate id (path/name/id).
189 // The goal of this step is to look at as little metadata as possible.
190 self.filesearch.search(|path| {
191 let file = match path.filename_str() {
192 None => return FileDoesntMatch,
195 let (hash, rlib) = if file.starts_with(rlib_prefix.as_slice()) &&
196 file.ends_with(".rlib") {
197 (file.slice(rlib_prefix.len(), file.len() - ".rlib".len()),
199 } else if dypair.map_or(false, |(_, suffix)| {
200 file.starts_with(dylib_prefix.get_ref().as_slice()) &&
201 file.ends_with(suffix)
203 let (_, suffix) = dypair.unwrap();
204 let dylib_prefix = dylib_prefix.get_ref().as_slice();
205 (file.slice(dylib_prefix.len(), file.len() - suffix.len()),
208 return FileDoesntMatch
210 info!("lib candidate: {}", path.display());
211 let slot = candidates.find_or_insert_with(hash.to_string(), |_| {
212 (HashSet::new(), HashSet::new())
214 let (ref mut rlibs, ref mut dylibs) = *slot;
216 rlibs.insert(fs::realpath(path).unwrap());
218 dylibs.insert(fs::realpath(path).unwrap());
223 // We have now collected all known libraries into a set of candidates
224 // keyed of the filename hash listed. For each filename, we also have a
225 // list of rlibs/dylibs that apply. Here, we map each of these lists
226 // (per hash), to a Library candidate for returning.
228 // A Library candidate is created if the metadata for the set of
229 // libraries corresponds to the crate id and hash criteria that this
230 // search is being performed for.
231 let mut libraries = Vec::new();
232 for (_hash, (rlibs, dylibs)) in candidates.move_iter() {
233 let mut metadata = None;
234 let rlib = self.extract_one(rlibs, "rlib", &mut metadata);
235 let dylib = self.extract_one(dylibs, "dylib", &mut metadata);
238 libraries.push(Library {
248 // Having now translated all relevant found hashes into libraries, see
249 // what we've got and figure out if we found multiple candidates for
251 match libraries.len() {
253 1 => Some(libraries.move_iter().next().unwrap()),
255 self.sess.span_err(self.span,
256 format!("multiple matching crates for `{}`",
257 self.crate_name).as_slice());
258 self.sess.note("candidates:");
259 for lib in libraries.iter() {
262 self.sess.note(format!("path: {}",
263 p.display()).as_slice());
269 self.sess.note(format!("path: {}",
270 p.display()).as_slice());
274 let data = lib.metadata.as_slice();
275 let name = decoder::get_crate_name(data);
276 note_crate_name(self.sess.diagnostic(), name.as_slice());
283 // Attempts to extract *one* library from the set `m`. If the set has no
284 // elements, `None` is returned. If the set has more than one element, then
285 // the errors and notes are emitted about the set of libraries.
287 // With only one library in the set, this function will extract it, and then
288 // read the metadata from it if `*slot` is `None`. If the metadata couldn't
289 // be read, it is assumed that the file isn't a valid rust library (no
290 // errors are emitted).
291 fn extract_one(&mut self, m: HashSet<Path>, flavor: &str,
292 slot: &mut Option<MetadataBlob>) -> Option<Path> {
293 let mut ret = None::<Path>;
297 // FIXME(#10786): for an optimization, we only read one of the
298 // library's metadata sections. In theory we should
299 // read both, but reading dylib metadata is quite
303 } else if m.len() == 1 {
304 return Some(m.move_iter().next().unwrap())
308 for lib in m.move_iter() {
309 info!("{} reading metadata from: {}", flavor, lib.display());
310 let metadata = match get_metadata_section(self.os, &lib) {
312 if self.crate_matches(blob.as_slice(), &lib) {
315 info!("metadata mismatch");
320 info!("no metadata found");
325 self.sess.span_err(self.span,
326 format!("multiple {} candidates for `{}` \
329 self.crate_name).as_slice());
330 self.sess.span_note(self.span,
331 format!(r"candidate #1: {}",
333 .display()).as_slice());
339 self.sess.span_note(self.span,
340 format!(r"candidate #{}: {}", error,
341 lib.display()).as_slice());
344 *slot = Some(metadata);
347 return if error > 0 {None} else {ret}
350 fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> bool {
351 match decoder::maybe_get_crate_name(crate_data) {
352 Some(ref name) if self.crate_name == name.as_slice() => {}
353 _ => { info!("Rejecting via crate name"); return false }
355 let hash = match decoder::maybe_get_crate_hash(crate_data) {
356 Some(hash) => hash, None => {
357 info!("Rejecting via lack of crate hash");
362 let triple = match decoder::get_crate_triple(crate_data) {
363 None => { debug!("triple not present"); return false }
366 if triple.as_slice() != self.triple {
367 info!("Rejecting via crate triple: expected {} got {}", self.triple, triple);
368 self.rejected_via_triple.push(CrateMismatch {
369 path: libpath.clone(),
370 got: triple.to_string()
379 info!("Rejecting via hash: expected {} got {}", *myhash, hash);
380 self.rejected_via_hash.push(CrateMismatch {
381 path: libpath.clone(),
382 got: myhash.as_str().to_string()
393 // Returns the corresponding (prefix, suffix) that files need to have for
395 fn dylibname(&self) -> Option<(&'static str, &'static str)> {
397 abi::OsWin32 => Some((WIN32_DLL_PREFIX, WIN32_DLL_SUFFIX)),
398 abi::OsMacos => Some((MACOS_DLL_PREFIX, MACOS_DLL_SUFFIX)),
399 abi::OsLinux => Some((LINUX_DLL_PREFIX, LINUX_DLL_SUFFIX)),
400 abi::OsAndroid => Some((ANDROID_DLL_PREFIX, ANDROID_DLL_SUFFIX)),
401 abi::OsFreebsd => Some((FREEBSD_DLL_PREFIX, FREEBSD_DLL_SUFFIX)),
408 pub fn note_crate_name(diag: &SpanHandler, name: &str) {
409 diag.handler().note(format!("crate name: {}", name).as_slice());
412 impl ArchiveMetadata {
413 fn new(ar: ArchiveRO) -> Option<ArchiveMetadata> {
414 let data: &'static [u8] = {
415 let data = match ar.read(METADATA_FILENAME) {
418 debug!("didn't find '{}' in the archive", METADATA_FILENAME);
422 // This data is actually a pointer inside of the archive itself, but
423 // we essentially want to cache it because the lookup inside the
424 // archive is a fairly expensive operation (and it's queried for
425 // *very* frequently). For this reason, we transmute it to the
426 // static lifetime to put into the struct. Note that the buffer is
427 // never actually handed out with a static lifetime, but rather the
428 // buffer is loaned with the lifetime of this containing object.
429 // Hence, we're guaranteed that the buffer will never be used after
430 // this object is dead, so this is a safe operation to transmute and
431 // store the data as a static buffer.
432 unsafe { mem::transmute(data) }
434 Some(ArchiveMetadata {
440 pub fn as_slice<'a>(&'a self) -> &'a [u8] { self.data }
443 // Just a small wrapper to time how long reading metadata takes.
444 fn get_metadata_section(os: abi::Os, filename: &Path) -> Result<MetadataBlob, String> {
445 let start = time::precise_time_ns();
446 let ret = get_metadata_section_imp(os, filename);
447 info!("reading {} => {}ms", filename.filename_display(),
448 (time::precise_time_ns() - start) / 1000000);
452 fn get_metadata_section_imp(os: abi::Os, filename: &Path) -> Result<MetadataBlob, String> {
453 if !filename.exists() {
454 return Err(format!("no such file: '{}'", filename.display()));
456 if filename.filename_str().unwrap().ends_with(".rlib") {
457 // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap
458 // internally to read the file. We also avoid even using a memcpy by
459 // just keeping the archive along while the metadata is in use.
460 let archive = match ArchiveRO::open(filename) {
463 debug!("llvm didn't like `{}`", filename.display());
464 return Err(format!("failed to read rlib metadata: '{}'",
465 filename.display()));
468 return match ArchiveMetadata::new(archive).map(|ar| MetadataArchive(ar)) {
470 return Err((format!("failed to read rlib metadata: '{}'",
471 filename.display())))
473 Some(blob) => return Ok(blob)
477 let mb = filename.with_c_str(|buf| {
478 llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
481 return Err(format!("error reading library: '{}'",
484 let of = match ObjectFile::new(mb) {
487 return Err((format!("provided path not an object file: '{}'",
488 filename.display())))
491 let si = mk_section_iter(of.llof);
492 while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
493 let mut name_buf = ptr::null();
494 let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf);
495 let name = str::raw::from_buf_len(name_buf as *const u8,
497 debug!("get_metadata_section: name {}", name);
498 if read_meta_section_name(os).as_slice() == name.as_slice() {
499 let cbuf = llvm::LLVMGetSectionContents(si.llsi);
500 let csz = llvm::LLVMGetSectionSize(si.llsi) as uint;
502 Err(format!("metadata not found: '{}'", filename.display()));
503 let cvbuf: *const u8 = mem::transmute(cbuf);
504 let vlen = encoder::metadata_encoding_version.len();
505 debug!("checking {} bytes of metadata-version stamp",
507 let minsz = cmp::min(vlen, csz);
508 let version_ok = slice::raw::buf_as_slice(cvbuf, minsz,
509 |buf0| buf0 == encoder::metadata_encoding_version);
511 return Err((format!("incompatible metadata version found: '{}'",
512 filename.display())));
515 let cvbuf1 = cvbuf.offset(vlen as int);
516 debug!("inflating {} bytes of compressed metadata",
518 slice::raw::buf_as_slice(cvbuf1, csz-vlen, |bytes| {
519 match flate::inflate_bytes(bytes) {
520 Some(inflated) => found = Ok(MetadataVec(inflated)),
523 Err(format!("failed to decompress \
533 llvm::LLVMMoveToNextSection(si.llsi);
535 return Err(format!("metadata not found: '{}'", filename.display()));
539 pub fn meta_section_name(os: abi::Os) -> Option<&'static str> {
541 abi::OsMacos => Some("__DATA,__note.rustc"),
542 abi::OsiOS => Some("__DATA,__note.rustc"),
543 abi::OsWin32 => Some(".note.rustc"),
544 abi::OsLinux => Some(".note.rustc"),
545 abi::OsAndroid => Some(".note.rustc"),
546 abi::OsFreebsd => Some(".note.rustc")
550 pub fn read_meta_section_name(os: abi::Os) -> &'static str {
552 abi::OsMacos => "__note.rustc",
553 abi::OsiOS => unreachable!(),
554 abi::OsWin32 => ".note.rustc",
555 abi::OsLinux => ".note.rustc",
556 abi::OsAndroid => ".note.rustc",
557 abi::OsFreebsd => ".note.rustc"
561 // A diagnostic function for dumping crate metadata to an output stream
562 pub fn list_file_metadata(os: abi::Os, path: &Path,
563 out: &mut io::Writer) -> io::IoResult<()> {
564 match get_metadata_section(os, path) {
565 Ok(bytes) => decoder::list_crate_metadata(bytes.as_slice(), out),
567 write!(out, "{}\n", msg)