]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/locator.rs
b677a63edc06458c6e50247dd22281535175dfd2
[rust.git] / src / librustc_metadata / locator.rs
1 // Copyright 2012-2015 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 //! Finds crate binaries and loads their metadata
12 //!
13 //! Might I be the first to welcome you to a world of platform differences,
14 //! version requirements, dependency graphs, conflicting desires, and fun! This
15 //! is the major guts (along with metadata::creader) of the compiler for loading
16 //! crates and resolving dependencies. Let's take a tour!
17 //!
18 //! # The problem
19 //!
20 //! Each invocation of the compiler is immediately concerned with one primary
21 //! problem, to connect a set of crates to resolved crates on the filesystem.
22 //! Concretely speaking, the compiler follows roughly these steps to get here:
23 //!
24 //! 1. Discover a set of `extern crate` statements.
25 //! 2. Transform these directives into crate names. If the directive does not
26 //!    have an explicit name, then the identifier is the name.
27 //! 3. For each of these crate names, find a corresponding crate on the
28 //!    filesystem.
29 //!
30 //! Sounds easy, right? Let's walk into some of the nuances.
31 //!
32 //! ## Transitive Dependencies
33 //!
34 //! Let's say we've got three crates: A, B, and C. A depends on B, and B depends
35 //! on C. When we're compiling A, we primarily need to find and locate B, but we
36 //! also end up needing to find and locate C as well.
37 //!
38 //! The reason for this is that any of B's types could be composed of C's types,
39 //! any function in B could return a type from C, etc. To be able to guarantee
40 //! that we can always typecheck/translate any function, we have to have
41 //! complete knowledge of the whole ecosystem, not just our immediate
42 //! dependencies.
43 //!
44 //! So now as part of the "find a corresponding crate on the filesystem" step
45 //! above, this involves also finding all crates for *all upstream
46 //! dependencies*. This includes all dependencies transitively.
47 //!
48 //! ## Rlibs and Dylibs
49 //!
50 //! The compiler has two forms of intermediate dependencies. These are dubbed
51 //! rlibs and dylibs for the static and dynamic variants, respectively. An rlib
52 //! is a rustc-defined file format (currently just an ar archive) while a dylib
53 //! is a platform-defined dynamic library. Each library has a metadata somewhere
54 //! inside of it.
55 //!
56 //! When translating a crate name to a crate on the filesystem, we all of a
57 //! sudden need to take into account both rlibs and dylibs! Linkage later on may
58 //! use either one of these files, as each has their pros/cons. The job of crate
59 //! loading is to discover what's possible by finding all candidates.
60 //!
61 //! Most parts of this loading systems keep the dylib/rlib as just separate
62 //! variables.
63 //!
64 //! ## Where to look?
65 //!
66 //! We can't exactly scan your whole hard drive when looking for dependencies,
67 //! so we need to places to look. Currently the compiler will implicitly add the
68 //! target lib search path ($prefix/lib/rustlib/$target/lib) to any compilation,
69 //! and otherwise all -L flags are added to the search paths.
70 //!
71 //! ## What criterion to select on?
72 //!
73 //! This a pretty tricky area of loading crates. Given a file, how do we know
74 //! whether it's the right crate? Currently, the rules look along these lines:
75 //!
76 //! 1. Does the filename match an rlib/dylib pattern? That is to say, does the
77 //!    filename have the right prefix/suffix?
78 //! 2. Does the filename have the right prefix for the crate name being queried?
79 //!    This is filtering for files like `libfoo*.rlib` and such.
80 //! 3. Is the file an actual rust library? This is done by loading the metadata
81 //!    from the library and making sure it's actually there.
82 //! 4. Does the name in the metadata agree with the name of the library?
83 //! 5. Does the target in the metadata agree with the current target?
84 //! 6. Does the SVH match? (more on this later)
85 //!
86 //! If the file answers `yes` to all these questions, then the file is
87 //! considered as being *candidate* for being accepted. It is illegal to have
88 //! more than two candidates as the compiler has no method by which to resolve
89 //! this conflict. Additionally, rlib/dylib candidates are considered
90 //! separately.
91 //!
92 //! After all this has happened, we have 1 or two files as candidates. These
93 //! represent the rlib/dylib file found for a library, and they're returned as
94 //! being found.
95 //!
96 //! ### What about versions?
97 //!
98 //! A lot of effort has been put forth to remove versioning from the compiler.
99 //! There have been forays in the past to have versioning baked in, but it was
100 //! largely always deemed insufficient to the point that it was recognized that
101 //! it's probably something the compiler shouldn't do anyway due to its
102 //! complicated nature and the state of the half-baked solutions.
103 //!
104 //! With a departure from versioning, the primary criterion for loading crates
105 //! is just the name of a crate. If we stopped here, it would imply that you
106 //! could never link two crates of the same name from different sources
107 //! together, which is clearly a bad state to be in.
108 //!
109 //! To resolve this problem, we come to the next section!
110 //!
111 //! # Expert Mode
112 //!
113 //! A number of flags have been added to the compiler to solve the "version
114 //! problem" in the previous section, as well as generally enabling more
115 //! powerful usage of the crate loading system of the compiler. The goal of
116 //! these flags and options are to enable third-party tools to drive the
117 //! compiler with prior knowledge about how the world should look.
118 //!
119 //! ## The `--extern` flag
120 //!
121 //! The compiler accepts a flag of this form a number of times:
122 //!
123 //! ```text
124 //! --extern crate-name=path/to/the/crate.rlib
125 //! ```
126 //!
127 //! This flag is basically the following letter to the compiler:
128 //!
129 //! > Dear rustc,
130 //! >
131 //! > When you are attempting to load the immediate dependency `crate-name`, I
132 //! > would like you to assume that the library is located at
133 //! > `path/to/the/crate.rlib`, and look nowhere else. Also, please do not
134 //! > assume that the path I specified has the name `crate-name`.
135 //!
136 //! This flag basically overrides most matching logic except for validating that
137 //! the file is indeed a rust library. The same `crate-name` can be specified
138 //! twice to specify the rlib/dylib pair.
139 //!
140 //! ## Enabling "multiple versions"
141 //!
142 //! This basically boils down to the ability to specify arbitrary packages to
143 //! the compiler. For example, if crate A wanted to use Bv1 and Bv2, then it
144 //! would look something like:
145 //!
146 //! ```ignore
147 //! extern crate b1;
148 //! extern crate b2;
149 //!
150 //! fn main() {}
151 //! ```
152 //!
153 //! and the compiler would be invoked as:
154 //!
155 //! ```text
156 //! rustc a.rs --extern b1=path/to/libb1.rlib --extern b2=path/to/libb2.rlib
157 //! ```
158 //!
159 //! In this scenario there are two crates named `b` and the compiler must be
160 //! manually driven to be informed where each crate is.
161 //!
162 //! ## Frobbing symbols
163 //!
164 //! One of the immediate problems with linking the same library together twice
165 //! in the same problem is dealing with duplicate symbols. The primary way to
166 //! deal with this in rustc is to add hashes to the end of each symbol.
167 //!
168 //! In order to force hashes to change between versions of a library, if
169 //! desired, the compiler exposes an option `-C metadata=foo`, which is used to
170 //! initially seed each symbol hash. The string `foo` is prepended to each
171 //! string-to-hash to ensure that symbols change over time.
172 //!
173 //! ## Loading transitive dependencies
174 //!
175 //! Dealing with same-named-but-distinct crates is not just a local problem, but
176 //! one that also needs to be dealt with for transitive dependencies. Note that
177 //! in the letter above `--extern` flags only apply to the *local* set of
178 //! dependencies, not the upstream transitive dependencies. Consider this
179 //! dependency graph:
180 //!
181 //! ```text
182 //! A.1   A.2
183 //! |     |
184 //! |     |
185 //! B     C
186 //!  \   /
187 //!   \ /
188 //!    D
189 //! ```
190 //!
191 //! In this scenario, when we compile `D`, we need to be able to distinctly
192 //! resolve `A.1` and `A.2`, but an `--extern` flag cannot apply to these
193 //! transitive dependencies.
194 //!
195 //! Note that the key idea here is that `B` and `C` are both *already compiled*.
196 //! That is, they have already resolved their dependencies. Due to unrelated
197 //! technical reasons, when a library is compiled, it is only compatible with
198 //! the *exact same* version of the upstream libraries it was compiled against.
199 //! We use the "Strict Version Hash" to identify the exact copy of an upstream
200 //! library.
201 //!
202 //! With this knowledge, we know that `B` and `C` will depend on `A` with
203 //! different SVH values, so we crawl the normal `-L` paths looking for
204 //! `liba*.rlib` and filter based on the contained SVH.
205 //!
206 //! In the end, this ends up not needing `--extern` to specify upstream
207 //! transitive dependencies.
208 //!
209 //! # Wrapping up
210 //!
211 //! That's the general overview of loading crates in the compiler, but it's by
212 //! no means all of the necessary details. Take a look at the rest of
213 //! metadata::locator or metadata::creader for all the juicy details!
214
215 use cstore::MetadataBlob;
216 use creader::Library;
217 use schema::{METADATA_HEADER, rustc_version};
218
219 use rustc::hir::svh::Svh;
220 use rustc::session::{config, Session};
221 use rustc::session::filesearch::{FileSearch, FileMatches, FileDoesntMatch};
222 use rustc::session::search_paths::PathKind;
223 use rustc::util::common;
224 use rustc::util::nodemap::FxHashMap;
225
226 use rustc_llvm as llvm;
227 use rustc_llvm::{False, ObjectFile, mk_section_iter};
228 use rustc_llvm::archive_ro::ArchiveRO;
229 use errors::DiagnosticBuilder;
230 use syntax_pos::Span;
231 use rustc_back::target::Target;
232
233 use std::cmp;
234 use std::fmt;
235 use std::fs;
236 use std::io;
237 use std::path::{Path, PathBuf};
238 use std::ptr;
239 use std::slice;
240 use std::time::Instant;
241
242 use flate;
243
244 pub struct CrateMismatch {
245     path: PathBuf,
246     got: String,
247 }
248
249 pub struct Context<'a> {
250     pub sess: &'a Session,
251     pub span: Span,
252     pub ident: &'a str,
253     pub crate_name: &'a str,
254     pub hash: Option<&'a Svh>,
255     // points to either self.sess.target.target or self.sess.host, must match triple
256     pub target: &'a Target,
257     pub triple: &'a str,
258     pub filesearch: FileSearch<'a>,
259     pub root: &'a Option<CratePaths>,
260     pub rejected_via_hash: Vec<CrateMismatch>,
261     pub rejected_via_triple: Vec<CrateMismatch>,
262     pub rejected_via_kind: Vec<CrateMismatch>,
263     pub rejected_via_version: Vec<CrateMismatch>,
264     pub should_match_name: bool,
265     pub is_proc_macro: Option<bool>,
266 }
267
268 pub struct ArchiveMetadata {
269     _archive: ArchiveRO,
270     // points into self._archive
271     data: *const [u8],
272 }
273
274 pub struct CratePaths {
275     pub ident: String,
276     pub dylib: Option<PathBuf>,
277     pub rlib: Option<PathBuf>,
278 }
279
280 pub const METADATA_FILENAME: &'static str = "rust.metadata.bin";
281
282 #[derive(Copy, Clone, PartialEq)]
283 enum CrateFlavor {
284     Rlib,
285     Dylib,
286 }
287
288 impl fmt::Display for CrateFlavor {
289     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290         f.write_str(match *self {
291             CrateFlavor::Rlib => "rlib",
292             CrateFlavor::Dylib => "dylib",
293         })
294     }
295 }
296
297 impl CratePaths {
298     fn paths(&self) -> Vec<PathBuf> {
299         match (&self.dylib, &self.rlib) {
300             (&None, &None) => vec![],
301             (&Some(ref p), &None) |
302             (&None, &Some(ref p)) => vec![p.clone()],
303             (&Some(ref p1), &Some(ref p2)) => vec![p1.clone(), p2.clone()],
304         }
305     }
306 }
307
308 impl<'a> Context<'a> {
309     pub fn maybe_load_library_crate(&mut self) -> Option<Library> {
310         self.find_library_crate()
311     }
312
313     pub fn load_library_crate(&mut self) -> Library {
314         self.find_library_crate().unwrap_or_else(|| self.report_errs())
315     }
316
317     pub fn report_errs(&mut self) -> ! {
318         let add = match self.root {
319             &None => String::new(),
320             &Some(ref r) => format!(" which `{}` depends on", r.ident),
321         };
322         let mut err = if !self.rejected_via_hash.is_empty() {
323             struct_span_err!(self.sess,
324                              self.span,
325                              E0460,
326                              "found possibly newer version of crate `{}`{}",
327                              self.ident,
328                              add)
329         } else if !self.rejected_via_triple.is_empty() {
330             struct_span_err!(self.sess,
331                              self.span,
332                              E0461,
333                              "couldn't find crate `{}` with expected target triple {}{}",
334                              self.ident,
335                              self.triple,
336                              add)
337         } else if !self.rejected_via_kind.is_empty() {
338             struct_span_err!(self.sess,
339                              self.span,
340                              E0462,
341                              "found staticlib `{}` instead of rlib or dylib{}",
342                              self.ident,
343                              add)
344         } else if !self.rejected_via_version.is_empty() {
345             struct_span_err!(self.sess,
346                              self.span,
347                              E0514,
348                              "found crate `{}` compiled by an incompatible version of rustc{}",
349                              self.ident,
350                              add)
351         } else {
352             let mut err = struct_span_err!(self.sess,
353                                            self.span,
354                                            E0463,
355                                            "can't find crate for `{}`{}",
356                                            self.ident,
357                                            add);
358
359             if (self.ident == "std" || self.ident == "core")
360                 && self.triple != config::host_triple() {
361                 err.note(&format!("the `{}` target may not be installed", self.triple));
362             }
363             err.span_label(self.span, &format!("can't find crate"));
364             err
365         };
366
367         if !self.rejected_via_triple.is_empty() {
368             let mismatches = self.rejected_via_triple.iter();
369             for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() {
370                 err.note(&format!("crate `{}`, path #{}, triple {}: {}",
371                                   self.ident,
372                                   i + 1,
373                                   got,
374                                   path.display()));
375             }
376         }
377         if !self.rejected_via_hash.is_empty() {
378             err.note("perhaps that crate needs to be recompiled?");
379             let mismatches = self.rejected_via_hash.iter();
380             for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() {
381                 err.note(&format!("crate `{}` path #{}: {}", self.ident, i + 1, path.display()));
382             }
383             match self.root {
384                 &None => {}
385                 &Some(ref r) => {
386                     for (i, path) in r.paths().iter().enumerate() {
387                         err.note(&format!("crate `{}` path #{}: {}",
388                                           r.ident,
389                                           i + 1,
390                                           path.display()));
391                     }
392                 }
393             }
394         }
395         if !self.rejected_via_kind.is_empty() {
396             err.help("please recompile that crate using --crate-type lib");
397             let mismatches = self.rejected_via_kind.iter();
398             for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() {
399                 err.note(&format!("crate `{}` path #{}: {}", self.ident, i + 1, path.display()));
400             }
401         }
402         if !self.rejected_via_version.is_empty() {
403             err.help(&format!("please recompile that crate using this compiler ({})",
404                               rustc_version()));
405             let mismatches = self.rejected_via_version.iter();
406             for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() {
407                 err.note(&format!("crate `{}` path #{}: {} compiled by {:?}",
408                                   self.ident,
409                                   i + 1,
410                                   path.display(),
411                                   got));
412             }
413         }
414
415         err.emit();
416         self.sess.abort_if_errors();
417         unreachable!();
418     }
419
420     fn find_library_crate(&mut self) -> Option<Library> {
421         // If an SVH is specified, then this is a transitive dependency that
422         // must be loaded via -L plus some filtering.
423         if self.hash.is_none() {
424             self.should_match_name = false;
425             if let Some(s) = self.sess.opts.externs.get(self.crate_name) {
426                 return self.find_commandline_library(s.iter());
427             }
428             self.should_match_name = true;
429         }
430
431         let dypair = self.dylibname();
432         let staticpair = self.staticlibname();
433
434         // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
435         let dylib_prefix = format!("{}{}", dypair.0, self.crate_name);
436         let rlib_prefix = format!("lib{}", self.crate_name);
437         let staticlib_prefix = format!("{}{}", staticpair.0, self.crate_name);
438
439         let mut candidates = FxHashMap();
440         let mut staticlibs = vec![];
441
442         // First, find all possible candidate rlibs and dylibs purely based on
443         // the name of the files themselves. We're trying to match against an
444         // exact crate name and a possibly an exact hash.
445         //
446         // During this step, we can filter all found libraries based on the
447         // name and id found in the crate id (we ignore the path portion for
448         // filename matching), as well as the exact hash (if specified). If we
449         // end up having many candidates, we must look at the metadata to
450         // perform exact matches against hashes/crate ids. Note that opening up
451         // the metadata is where we do an exact match against the full contents
452         // of the crate id (path/name/id).
453         //
454         // The goal of this step is to look at as little metadata as possible.
455         self.filesearch.search(|path, kind| {
456             let file = match path.file_name().and_then(|s| s.to_str()) {
457                 None => return FileDoesntMatch,
458                 Some(file) => file,
459             };
460             let (hash, rlib) = if file.starts_with(&rlib_prefix[..]) && file.ends_with(".rlib") {
461                 (&file[(rlib_prefix.len())..(file.len() - ".rlib".len())], true)
462             } else if file.starts_with(&dylib_prefix) &&
463                                          file.ends_with(&dypair.1) {
464                 (&file[(dylib_prefix.len())..(file.len() - dypair.1.len())], false)
465             } else {
466                 if file.starts_with(&staticlib_prefix[..]) && file.ends_with(&staticpair.1) {
467                     staticlibs.push(CrateMismatch {
468                         path: path.to_path_buf(),
469                         got: "static".to_string(),
470                     });
471                 }
472                 return FileDoesntMatch;
473             };
474             info!("lib candidate: {}", path.display());
475
476             let hash_str = hash.to_string();
477             let slot = candidates.entry(hash_str)
478                 .or_insert_with(|| (FxHashMap(), FxHashMap()));
479             let (ref mut rlibs, ref mut dylibs) = *slot;
480             fs::canonicalize(path)
481                 .map(|p| {
482                     if rlib {
483                         rlibs.insert(p, kind);
484                     } else {
485                         dylibs.insert(p, kind);
486                     }
487                     FileMatches
488                 })
489                 .unwrap_or(FileDoesntMatch)
490         });
491         self.rejected_via_kind.extend(staticlibs);
492
493         // We have now collected all known libraries into a set of candidates
494         // keyed of the filename hash listed. For each filename, we also have a
495         // list of rlibs/dylibs that apply. Here, we map each of these lists
496         // (per hash), to a Library candidate for returning.
497         //
498         // A Library candidate is created if the metadata for the set of
499         // libraries corresponds to the crate id and hash criteria that this
500         // search is being performed for.
501         let mut libraries = FxHashMap();
502         for (_hash, (rlibs, dylibs)) in candidates {
503             let mut slot = None;
504             let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot);
505             let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot);
506             if let Some((h, m)) = slot {
507                 libraries.insert(h,
508                                  Library {
509                                      dylib: dylib,
510                                      rlib: rlib,
511                                      metadata: m,
512                                  });
513             }
514         }
515
516         // Having now translated all relevant found hashes into libraries, see
517         // what we've got and figure out if we found multiple candidates for
518         // libraries or not.
519         match libraries.len() {
520             0 => None,
521             1 => Some(libraries.into_iter().next().unwrap().1),
522             _ => {
523                 let mut err = struct_span_err!(self.sess,
524                                                self.span,
525                                                E0464,
526                                                "multiple matching crates for `{}`",
527                                                self.crate_name);
528                 err.note("candidates:");
529                 for (_, lib) in libraries {
530                     if let Some((ref p, _)) = lib.dylib {
531                         err.note(&format!("path: {}", p.display()));
532                     }
533                     if let Some((ref p, _)) = lib.rlib {
534                         err.note(&format!("path: {}", p.display()));
535                     }
536                     note_crate_name(&mut err, &lib.metadata.get_root().name);
537                 }
538                 err.emit();
539                 None
540             }
541         }
542     }
543
544     // Attempts to extract *one* library from the set `m`. If the set has no
545     // elements, `None` is returned. If the set has more than one element, then
546     // the errors and notes are emitted about the set of libraries.
547     //
548     // With only one library in the set, this function will extract it, and then
549     // read the metadata from it if `*slot` is `None`. If the metadata couldn't
550     // be read, it is assumed that the file isn't a valid rust library (no
551     // errors are emitted).
552     fn extract_one(&mut self,
553                    m: FxHashMap<PathBuf, PathKind>,
554                    flavor: CrateFlavor,
555                    slot: &mut Option<(Svh, MetadataBlob)>)
556                    -> Option<(PathBuf, PathKind)> {
557         let mut ret: Option<(PathBuf, PathKind)> = None;
558         let mut error = 0;
559
560         if slot.is_some() {
561             // FIXME(#10786): for an optimization, we only read one of the
562             //                libraries' metadata sections. In theory we should
563             //                read both, but reading dylib metadata is quite
564             //                slow.
565             if m.is_empty() {
566                 return None;
567             } else if m.len() == 1 {
568                 return Some(m.into_iter().next().unwrap());
569             }
570         }
571
572         let mut err: Option<DiagnosticBuilder> = None;
573         for (lib, kind) in m {
574             info!("{} reading metadata from: {}", flavor, lib.display());
575             let (hash, metadata) = match get_metadata_section(self.target, flavor, &lib) {
576                 Ok(blob) => {
577                     if let Some(h) = self.crate_matches(&blob, &lib) {
578                         (h, blob)
579                     } else {
580                         info!("metadata mismatch");
581                         continue;
582                     }
583                 }
584                 Err(err) => {
585                     info!("no metadata found: {}", err);
586                     continue;
587                 }
588             };
589             // If we see multiple hashes, emit an error about duplicate candidates.
590             if slot.as_ref().map_or(false, |s| s.0 != hash) {
591                 let mut e = struct_span_err!(self.sess,
592                                              self.span,
593                                              E0465,
594                                              "multiple {} candidates for `{}` found",
595                                              flavor,
596                                              self.crate_name);
597                 e.span_note(self.span,
598                             &format!(r"candidate #1: {}",
599                                      ret.as_ref()
600                                          .unwrap()
601                                          .0
602                                          .display()));
603                 if let Some(ref mut e) = err {
604                     e.emit();
605                 }
606                 err = Some(e);
607                 error = 1;
608                 *slot = None;
609             }
610             if error > 0 {
611                 error += 1;
612                 err.as_mut().unwrap().span_note(self.span,
613                                                 &format!(r"candidate #{}: {}",
614                                                          error,
615                                                          lib.display()));
616                 continue;
617             }
618             *slot = Some((hash, metadata));
619             ret = Some((lib, kind));
620         }
621
622         if error > 0 {
623             err.unwrap().emit();
624             None
625         } else {
626             ret
627         }
628     }
629
630     fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option<Svh> {
631         let root = metadata.get_root();
632         if let Some(is_proc_macro) = self.is_proc_macro {
633             if root.macro_derive_registrar.is_some() != is_proc_macro {
634                 return None;
635             }
636         }
637
638         let rustc_version = rustc_version();
639         if root.rustc_version != rustc_version {
640             info!("Rejecting via version: expected {} got {}",
641                   rustc_version,
642                   root.rustc_version);
643             self.rejected_via_version.push(CrateMismatch {
644                 path: libpath.to_path_buf(),
645                 got: root.rustc_version,
646             });
647             return None;
648         }
649
650         if self.should_match_name {
651             if self.crate_name != root.name {
652                 info!("Rejecting via crate name");
653                 return None;
654             }
655         }
656
657         if root.triple != self.triple {
658             info!("Rejecting via crate triple: expected {} got {}",
659                   self.triple,
660                   root.triple);
661             self.rejected_via_triple.push(CrateMismatch {
662                 path: libpath.to_path_buf(),
663                 got: root.triple,
664             });
665             return None;
666         }
667
668         if let Some(myhash) = self.hash {
669             if *myhash != root.hash {
670                 info!("Rejecting via hash: expected {} got {}", *myhash, root.hash);
671                 self.rejected_via_hash.push(CrateMismatch {
672                     path: libpath.to_path_buf(),
673                     got: myhash.to_string(),
674                 });
675                 return None;
676             }
677         }
678
679         Some(root.hash)
680     }
681
682
683     // Returns the corresponding (prefix, suffix) that files need to have for
684     // dynamic libraries
685     fn dylibname(&self) -> (String, String) {
686         let t = &self.target;
687         (t.options.dll_prefix.clone(), t.options.dll_suffix.clone())
688     }
689
690     // Returns the corresponding (prefix, suffix) that files need to have for
691     // static libraries
692     fn staticlibname(&self) -> (String, String) {
693         let t = &self.target;
694         (t.options.staticlib_prefix.clone(), t.options.staticlib_suffix.clone())
695     }
696
697     fn find_commandline_library<'b, LOCS>(&mut self, locs: LOCS) -> Option<Library>
698         where LOCS: Iterator<Item = &'b String>
699     {
700         // First, filter out all libraries that look suspicious. We only accept
701         // files which actually exist that have the correct naming scheme for
702         // rlibs/dylibs.
703         let sess = self.sess;
704         let dylibname = self.dylibname();
705         let mut rlibs = FxHashMap();
706         let mut dylibs = FxHashMap();
707         {
708             let locs = locs.map(|l| PathBuf::from(l)).filter(|loc| {
709                 if !loc.exists() {
710                     sess.err(&format!("extern location for {} does not exist: {}",
711                                       self.crate_name,
712                                       loc.display()));
713                     return false;
714                 }
715                 let file = match loc.file_name().and_then(|s| s.to_str()) {
716                     Some(file) => file,
717                     None => {
718                         sess.err(&format!("extern location for {} is not a file: {}",
719                                           self.crate_name,
720                                           loc.display()));
721                         return false;
722                     }
723                 };
724                 if file.starts_with("lib") && file.ends_with(".rlib") {
725                     return true;
726                 } else {
727                     let (ref prefix, ref suffix) = dylibname;
728                     if file.starts_with(&prefix[..]) && file.ends_with(&suffix[..]) {
729                         return true;
730                     }
731                 }
732                 sess.struct_err(&format!("extern location for {} is of an unknown type: {}",
733                                          self.crate_name,
734                                          loc.display()))
735                     .help(&format!("file name should be lib*.rlib or {}*.{}",
736                                    dylibname.0,
737                                    dylibname.1))
738                     .emit();
739                 false
740             });
741
742             // Now that we have an iterator of good candidates, make sure
743             // there's at most one rlib and at most one dylib.
744             for loc in locs {
745                 if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") {
746                     rlibs.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag);
747                 } else {
748                     dylibs.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag);
749                 }
750             }
751         };
752
753         // Extract the rlib/dylib pair.
754         let mut slot = None;
755         let rlib = self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot);
756         let dylib = self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot);
757
758         if rlib.is_none() && dylib.is_none() {
759             return None;
760         }
761         match slot {
762             Some((_, metadata)) => {
763                 Some(Library {
764                     dylib: dylib,
765                     rlib: rlib,
766                     metadata: metadata,
767                 })
768             }
769             None => None,
770         }
771     }
772 }
773
774 pub fn note_crate_name(err: &mut DiagnosticBuilder, name: &str) {
775     err.note(&format!("crate name: {}", name));
776 }
777
778 impl ArchiveMetadata {
779     fn new(ar: ArchiveRO) -> Option<ArchiveMetadata> {
780         let data = {
781             let section = ar.iter()
782                 .filter_map(|s| s.ok())
783                 .find(|sect| sect.name() == Some(METADATA_FILENAME));
784             match section {
785                 Some(s) => s.data() as *const [u8],
786                 None => {
787                     debug!("didn't find '{}' in the archive", METADATA_FILENAME);
788                     return None;
789                 }
790             }
791         };
792
793         Some(ArchiveMetadata {
794             _archive: ar,
795             data: data,
796         })
797     }
798
799     pub fn as_slice<'a>(&'a self) -> &'a [u8] {
800         unsafe { &*self.data }
801     }
802 }
803
804 fn verify_decompressed_encoding_version(blob: &MetadataBlob,
805                                         filename: &Path)
806                                         -> Result<(), String> {
807     if !blob.is_compatible() {
808         Err((format!("incompatible metadata version found: '{}'",
809                      filename.display())))
810     } else {
811         Ok(())
812     }
813 }
814
815 // Just a small wrapper to time how long reading metadata takes.
816 fn get_metadata_section(target: &Target,
817                         flavor: CrateFlavor,
818                         filename: &Path)
819                         -> Result<MetadataBlob, String> {
820     let start = Instant::now();
821     let ret = get_metadata_section_imp(target, flavor, filename);
822     info!("reading {:?} => {:?}",
823           filename.file_name().unwrap(),
824           start.elapsed());
825     return ret;
826 }
827
828 fn get_metadata_section_imp(target: &Target,
829                             flavor: CrateFlavor,
830                             filename: &Path)
831                             -> Result<MetadataBlob, String> {
832     if !filename.exists() {
833         return Err(format!("no such file: '{}'", filename.display()));
834     }
835     if flavor == CrateFlavor::Rlib {
836         // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap
837         // internally to read the file. We also avoid even using a memcpy by
838         // just keeping the archive along while the metadata is in use.
839         let archive = match ArchiveRO::open(filename) {
840             Some(ar) => ar,
841             None => {
842                 debug!("llvm didn't like `{}`", filename.display());
843                 return Err(format!("failed to read rlib metadata: '{}'", filename.display()));
844             }
845         };
846         return match ArchiveMetadata::new(archive).map(|ar| MetadataBlob::Archive(ar)) {
847             None => Err(format!("failed to read rlib metadata: '{}'", filename.display())),
848             Some(blob) => {
849                 verify_decompressed_encoding_version(&blob, filename)?;
850                 Ok(blob)
851             }
852         };
853     }
854     unsafe {
855         let buf = common::path2cstr(filename);
856         let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr());
857         if mb as isize == 0 {
858             return Err(format!("error reading library: '{}'", filename.display()));
859         }
860         let of = match ObjectFile::new(mb) {
861             Some(of) => of,
862             _ => {
863                 return Err((format!("provided path not an object file: '{}'", filename.display())))
864             }
865         };
866         let si = mk_section_iter(of.llof);
867         while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
868             let mut name_buf = ptr::null();
869             let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf);
870             let name = slice::from_raw_parts(name_buf as *const u8, name_len as usize).to_vec();
871             let name = String::from_utf8(name).unwrap();
872             debug!("get_metadata_section: name {}", name);
873             if read_meta_section_name(target) == name {
874                 let cbuf = llvm::LLVMGetSectionContents(si.llsi);
875                 let csz = llvm::LLVMGetSectionSize(si.llsi) as usize;
876                 let cvbuf: *const u8 = cbuf as *const u8;
877                 let vlen = METADATA_HEADER.len();
878                 debug!("checking {} bytes of metadata-version stamp", vlen);
879                 let minsz = cmp::min(vlen, csz);
880                 let buf0 = slice::from_raw_parts(cvbuf, minsz);
881                 let version_ok = buf0 == METADATA_HEADER;
882                 if !version_ok {
883                     return Err((format!("incompatible metadata version found: '{}'",
884                                         filename.display())));
885                 }
886
887                 let cvbuf1 = cvbuf.offset(vlen as isize);
888                 debug!("inflating {} bytes of compressed metadata", csz - vlen);
889                 let bytes = slice::from_raw_parts(cvbuf1, csz - vlen);
890                 match flate::inflate_bytes(bytes) {
891                     Ok(inflated) => {
892                         let blob = MetadataBlob::Inflated(inflated);
893                         verify_decompressed_encoding_version(&blob, filename)?;
894                         return Ok(blob);
895                     }
896                     Err(_) => {}
897                 }
898             }
899             llvm::LLVMMoveToNextSection(si.llsi);
900         }
901         Err(format!("metadata not found: '{}'", filename.display()))
902     }
903 }
904
905 pub fn meta_section_name(target: &Target) -> &'static str {
906     // Historical note:
907     //
908     // When using link.exe it was seen that the section name `.note.rustc`
909     // was getting shortened to `.note.ru`, and according to the PE and COFF
910     // specification:
911     //
912     // > Executable images do not use a string table and do not support
913     // > section names longer than 8 characters
914     //
915     // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx
916     //
917     // As a result, we choose a slightly shorter name! As to why
918     // `.note.rustc` works on MinGW, that's another good question...
919
920     if target.options.is_like_osx {
921         "__DATA,.rustc"
922     } else {
923         ".rustc"
924     }
925 }
926
927 pub fn read_meta_section_name(_target: &Target) -> &'static str {
928     ".rustc"
929 }
930
931 // A diagnostic function for dumping crate metadata to an output stream
932 pub fn list_file_metadata(target: &Target, path: &Path, out: &mut io::Write) -> io::Result<()> {
933     let filename = path.file_name().unwrap().to_str().unwrap();
934     let flavor = if filename.ends_with(".rlib") {
935         CrateFlavor::Rlib
936     } else {
937         CrateFlavor::Dylib
938     };
939     match get_metadata_section(target, flavor, path) {
940         Ok(metadata) => metadata.list_crate_metadata(out),
941         Err(msg) => write!(out, "{}\n", msg),
942     }
943 }