about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-09-25 03:48:22 +0200
committerGitHub <noreply@github.com>2019-09-25 03:48:22 +0200
commit40fae88fa82db350f1233b9b93df0be30e6579bc (patch)
tree988ea8d7ef3a56d1cc4b191d26639ad91ad03b5f
parentdcd473d7b554a82013913244da8aba1e22a002a9 (diff)
parentf00c6346b42d2a3091752166cd86aa2d69112dcc (diff)
downloadrust-40fae88fa82db350f1233b9b93df0be30e6579bc.tar.gz
rust-40fae88fa82db350f1233b9b93df0be30e6579bc.zip
Rollup merge of #64324 - alexcrichton:share-generics-again, r=michaelwoerister
rustc: Fix mixing crates with different `share_generics`

This commit addresses #64319 by removing the `dylib` crate type from the
list of crate type that exports generic symbols. The bug in #64319
arises because a `dylib` crate type was trying to export a symbol in an
uptream crate but it miscalculated the symbol name of the uptream
symbol. This isn't really necessary, though, since `dylib` crates aren't
that heavily used, so we can just conservatively say that the `dylib`
crate type never exports generic symbols, forcibly removing them from
the exported symbol lists if were to otherwise find them.

The fix here happens in two places:

* First is in the `local_crate_exports_generics` method, indicating that
  it's now `false` for the `Dylib` crate type. Only rlibs actually
  export generics at this point.

* Next is when we load exported symbols from upstream crate. If, for our
  compilation session, the crate may be included from a dynamic library,
  then its generic symbols are removed. When the crate was linked into a
  dynamic library its symbols weren't exported, so we can't consider
  them a candidate to link against.

Overally this should avoid situations where we incorrectly calculate the
upstream symbol names in the face of differnet `share_generics` options,
ultimately...

Closes #64319
-rw-r--r--src/librustc/middle/dependency_format.rs378
-rw-r--r--src/librustc/query/mod.rs6
-rw-r--r--src/librustc/session/config.rs2
-rw-r--r--src/librustc/session/mod.rs3
-rw-r--r--src/librustc/ty/context.rs4
-rw-r--r--src/librustc_codegen_ssa/back/link.rs61
-rw-r--r--src/librustc_codegen_ssa/back/linker.rs34
-rw-r--r--src/librustc_codegen_ssa/back/symbol_export.rs2
-rw-r--r--src/librustc_codegen_ssa/back/write.rs2
-rw-r--r--src/librustc_codegen_ssa/base.rs3
-rw-r--r--src/librustc_codegen_ssa/lib.rs2
-rw-r--r--src/librustc_interface/passes.rs4
-rw-r--r--src/librustc_metadata/cstore_impl.rs31
-rw-r--r--src/librustc_metadata/dependency_format.rs370
-rw-r--r--src/librustc_metadata/encoder.rs26
-rw-r--r--src/librustc_metadata/lib.rs1
-rw-r--r--src/test/codegen-units/partitioning/auxiliary/shared_generics_aux.rs1
-rw-r--r--src/test/codegen-units/partitioning/shared-generics.rs1
-rw-r--r--src/test/compile-fail/two-panic-runtimes.rs1
-rw-r--r--src/test/run-make-fulldeps/issue-64319/Makefile39
-rw-r--r--src/test/run-make-fulldeps/issue-64319/bar.rs5
-rw-r--r--src/test/run-make-fulldeps/issue-64319/foo.rs9
-rw-r--r--src/test/run-make-fulldeps/symbol-visibility/Makefile4
-rw-r--r--src/test/ui/panic-runtime/transitive-link-a-bunch.rs3
-rw-r--r--src/test/ui/panic-runtime/want-unwind-got-abort.rs3
-rw-r--r--src/test/ui/panic-runtime/want-unwind-got-abort2.rs3
26 files changed, 565 insertions, 433 deletions
diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs
index 96b99fe4cdc..8b2bf55ccc1 100644
--- a/src/librustc/middle/dependency_format.rs
+++ b/src/librustc/middle/dependency_format.rs
@@ -1,64 +1,10 @@
-//! Resolution of mixing rlibs and dylibs
+//! Type definitions for learning about the dependency formats of all upstream
+//! crates (rlibs/dylibs/oh my).
 //!
-//! When producing a final artifact, such as a dynamic library, the compiler has
-//! a choice between linking an rlib or linking a dylib of all upstream
-//! dependencies. The linking phase must guarantee, however, that a library only
-//! show up once in the object file. For example, it is illegal for library A to
-//! be statically linked to B and C in separate dylibs, and then link B and C
-//! into a crate D (because library A appears twice).
-//!
-//! The job of this module is to calculate what format each upstream crate
-//! should be used when linking each output type requested in this session. This
-//! generally follows this set of rules:
-//!
-//!     1. Each library must appear exactly once in the output.
-//!     2. Each rlib contains only one library (it's just an object file)
-//!     3. Each dylib can contain more than one library (due to static linking),
-//!        and can also bring in many dynamic dependencies.
-//!
-//! With these constraints in mind, it's generally a very difficult problem to
-//! find a solution that's not "all rlibs" or "all dylibs". I have suspicions
-//! that NP-ness may come into the picture here...
-//!
-//! The current selection algorithm below looks mostly similar to:
-//!
-//!     1. If static linking is required, then require all upstream dependencies
-//!        to be available as rlibs. If not, generate an error.
-//!     2. If static linking is requested (generating an executable), then
-//!        attempt to use all upstream dependencies as rlibs. If any are not
-//!        found, bail out and continue to step 3.
-//!     3. Static linking has failed, at least one library must be dynamically
-//!        linked. Apply a heuristic by greedily maximizing the number of
-//!        dynamically linked libraries.
-//!     4. Each upstream dependency available as a dynamic library is
-//!        registered. The dependencies all propagate, adding to a map. It is
-//!        possible for a dylib to add a static library as a dependency, but it
-//!        is illegal for two dylibs to add the same static library as a
-//!        dependency. The same dylib can be added twice. Additionally, it is
-//!        illegal to add a static dependency when it was previously found as a
-//!        dylib (and vice versa)
-//!     5. After all dynamic dependencies have been traversed, re-traverse the
-//!        remaining dependencies and add them statically (if they haven't been
-//!        added already).
-//!
-//! While not perfect, this algorithm should help support use-cases such as leaf
-//! dependencies being static while the larger tree of inner dependencies are
-//! all dynamic. This isn't currently very well battle tested, so it will likely
-//! fall short in some use cases.
-//!
-//! Currently, there is no way to specify the preference of linkage with a
-//! particular library (other than a global dynamic/static switch).
-//! Additionally, the algorithm is geared towards finding *any* solution rather
-//! than finding a number of solutions (there are normally quite a few).
-
-use crate::hir::def_id::CrateNum;
+//! For all the gory details, see the provider of the `dependency_formats`
+//! query.
 
 use crate::session::config;
-use crate::ty::TyCtxt;
-use crate::middle::cstore::{self, DepKind};
-use crate::middle::cstore::LinkagePreference::{self, RequireStatic, RequireDynamic};
-use crate::util::nodemap::FxHashMap;
-use rustc_target::spec::PanicStrategy;
 
 /// A list of dependencies for a certain crate type.
 ///
@@ -71,324 +17,12 @@ pub type DependencyList = Vec<Linkage>;
 /// A mapping of all required dependencies for a particular flavor of output.
 ///
 /// This is local to the tcx, and is generally relevant to one session.
-pub type Dependencies = FxHashMap<config::CrateType, DependencyList>;
+pub type Dependencies = Vec<(config::CrateType, DependencyList)>;
 
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Debug, HashStable)]
 pub enum Linkage {
     NotLinked,
     IncludedFromDylib,
     Static,
     Dynamic,
 }
-
-pub fn calculate(tcx: TyCtxt<'_>) {
-    let sess = &tcx.sess;
-    let fmts = sess.crate_types.borrow().iter().map(|&ty| {
-        let linkage = calculate_type(tcx, ty);
-        verify_ok(tcx, &linkage);
-        (ty, linkage)
-    }).collect::<FxHashMap<_, _>>();
-    sess.abort_if_errors();
-    sess.dependency_formats.set(fmts);
-}
-
-fn calculate_type(tcx: TyCtxt<'_>, ty: config::CrateType) -> DependencyList {
-    let sess = &tcx.sess;
-
-    if !sess.opts.output_types.should_codegen() {
-        return Vec::new();
-    }
-
-    let preferred_linkage = match ty {
-        // cdylibs must have all static dependencies.
-        config::CrateType::Cdylib => Linkage::Static,
-
-        // Generating a dylib without `-C prefer-dynamic` means that we're going
-        // to try to eagerly statically link all dependencies. This is normally
-        // done for end-product dylibs, not intermediate products.
-        config::CrateType::Dylib if !sess.opts.cg.prefer_dynamic => Linkage::Static,
-        config::CrateType::Dylib => Linkage::Dynamic,
-
-        // If the global prefer_dynamic switch is turned off, or the final
-        // executable will be statically linked, prefer static crate linkage.
-        config::CrateType::Executable if !sess.opts.cg.prefer_dynamic ||
-            sess.crt_static() => Linkage::Static,
-        config::CrateType::Executable => Linkage::Dynamic,
-
-        // proc-macro crates are mostly cdylibs, but we also need metadata.
-        config::CrateType::ProcMacro => Linkage::Static,
-
-        // No linkage happens with rlibs, we just needed the metadata (which we
-        // got long ago), so don't bother with anything.
-        config::CrateType::Rlib => Linkage::NotLinked,
-
-        // staticlibs must have all static dependencies.
-        config::CrateType::Staticlib => Linkage::Static,
-    };
-
-    if preferred_linkage == Linkage::NotLinked {
-        // If the crate is not linked, there are no link-time dependencies.
-        return Vec::new();
-    }
-
-    if preferred_linkage == Linkage::Static {
-        // Attempt static linkage first. For dylibs and executables, we may be
-        // able to retry below with dynamic linkage.
-        if let Some(v) = attempt_static(tcx) {
-            return v;
-        }
-
-        // Staticlibs, cdylibs, and static executables must have all static
-        // dependencies. If any are not found, generate some nice pretty errors.
-        if ty == config::CrateType::Cdylib || ty == config::CrateType::Staticlib ||
-                (ty == config::CrateType::Executable && sess.crt_static() &&
-                !sess.target.target.options.crt_static_allows_dylibs) {
-            for &cnum in tcx.crates().iter() {
-                if tcx.dep_kind(cnum).macros_only() { continue }
-                let src = tcx.used_crate_source(cnum);
-                if src.rlib.is_some() { continue }
-                sess.err(&format!("crate `{}` required to be available in rlib format, \
-                                   but was not found in this form",
-                                  tcx.crate_name(cnum)));
-            }
-            return Vec::new();
-        }
-    }
-
-    let mut formats = FxHashMap::default();
-
-    // Sweep all crates for found dylibs. Add all dylibs, as well as their
-    // dependencies, ensuring there are no conflicts. The only valid case for a
-    // dependency to be relied upon twice is for both cases to rely on a dylib.
-    for &cnum in tcx.crates().iter() {
-        if tcx.dep_kind(cnum).macros_only() { continue }
-        let name = tcx.crate_name(cnum);
-        let src = tcx.used_crate_source(cnum);
-        if src.dylib.is_some() {
-            info!("adding dylib: {}", name);
-            add_library(tcx, cnum, RequireDynamic, &mut formats);
-            let deps = tcx.dylib_dependency_formats(cnum);
-            for &(depnum, style) in deps.iter() {
-                info!("adding {:?}: {}", style, tcx.crate_name(depnum));
-                add_library(tcx, depnum, style, &mut formats);
-            }
-        }
-    }
-
-    // Collect what we've got so far in the return vector.
-    let last_crate = tcx.crates().len();
-    let mut ret = (1..last_crate+1).map(|cnum| {
-        match formats.get(&CrateNum::new(cnum)) {
-            Some(&RequireDynamic) => Linkage::Dynamic,
-            Some(&RequireStatic) => Linkage::IncludedFromDylib,
-            None => Linkage::NotLinked,
-        }
-    }).collect::<Vec<_>>();
-
-    // Run through the dependency list again, and add any missing libraries as
-    // static libraries.
-    //
-    // If the crate hasn't been included yet and it's not actually required
-    // (e.g., it's an allocator) then we skip it here as well.
-    for &cnum in tcx.crates().iter() {
-        let src = tcx.used_crate_source(cnum);
-        if src.dylib.is_none() &&
-           !formats.contains_key(&cnum) &&
-           tcx.dep_kind(cnum) == DepKind::Explicit {
-            assert!(src.rlib.is_some() || src.rmeta.is_some());
-            info!("adding staticlib: {}", tcx.crate_name(cnum));
-            add_library(tcx, cnum, RequireStatic, &mut formats);
-            ret[cnum.as_usize() - 1] = Linkage::Static;
-        }
-    }
-
-    // We've gotten this far because we're emitting some form of a final
-    // artifact which means that we may need to inject dependencies of some
-    // form.
-    //
-    // Things like allocators and panic runtimes may not have been activated
-    // quite yet, so do so here.
-    activate_injected_dep(*sess.injected_panic_runtime.get(), &mut ret,
-                          &|cnum| tcx.is_panic_runtime(cnum));
-
-    // When dylib B links to dylib A, then when using B we must also link to A.
-    // It could be the case, however, that the rlib for A is present (hence we
-    // found metadata), but the dylib for A has since been removed.
-    //
-    // For situations like this, we perform one last pass over the dependencies,
-    // making sure that everything is available in the requested format.
-    for (cnum, kind) in ret.iter().enumerate() {
-        let cnum = CrateNum::new(cnum + 1);
-        let src = tcx.used_crate_source(cnum);
-        match *kind {
-            Linkage::NotLinked |
-            Linkage::IncludedFromDylib => {}
-            Linkage::Static if src.rlib.is_some() => continue,
-            Linkage::Dynamic if src.dylib.is_some() => continue,
-            kind => {
-                let kind = match kind {
-                    Linkage::Static => "rlib",
-                    _ => "dylib",
-                };
-                sess.err(&format!("crate `{}` required to be available in {} format, \
-                                   but was not found in this form",
-                                  tcx.crate_name(cnum), kind));
-            }
-        }
-    }
-
-    ret
-}
-
-fn add_library(
-    tcx: TyCtxt<'_>,
-    cnum: CrateNum,
-    link: LinkagePreference,
-    m: &mut FxHashMap<CrateNum, LinkagePreference>,
-) {
-    match m.get(&cnum) {
-        Some(&link2) => {
-            // If the linkages differ, then we'd have two copies of the library
-            // if we continued linking. If the linkages are both static, then we
-            // would also have two copies of the library (static from two
-            // different locations).
-            //
-            // This error is probably a little obscure, but I imagine that it
-            // can be refined over time.
-            if link2 != link || link == RequireStatic {
-                tcx.sess.struct_err(&format!("cannot satisfy dependencies so `{}` only \
-                                              shows up once", tcx.crate_name(cnum)))
-                    .help("having upstream crates all available in one format \
-                           will likely make this go away")
-                    .emit();
-            }
-        }
-        None => { m.insert(cnum, link); }
-    }
-}
-
-fn attempt_static(tcx: TyCtxt<'_>) -> Option<DependencyList> {
-    let sess = &tcx.sess;
-    let crates = cstore::used_crates(tcx, RequireStatic);
-    if !crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) {
-        return None
-    }
-
-    // All crates are available in an rlib format, so we're just going to link
-    // everything in explicitly so long as it's actually required.
-    let last_crate = tcx.crates().len();
-    let mut ret = (1..last_crate+1).map(|cnum| {
-        if tcx.dep_kind(CrateNum::new(cnum)) == DepKind::Explicit {
-            Linkage::Static
-        } else {
-            Linkage::NotLinked
-        }
-    }).collect::<Vec<_>>();
-
-    // Our allocator/panic runtime may not have been linked above if it wasn't
-    // explicitly linked, which is the case for any injected dependency. Handle
-    // that here and activate them.
-    activate_injected_dep(*sess.injected_panic_runtime.get(), &mut ret,
-                          &|cnum| tcx.is_panic_runtime(cnum));
-
-    Some(ret)
-}
-
-// Given a list of how to link upstream dependencies so far, ensure that an
-// injected dependency is activated. This will not do anything if one was
-// transitively included already (e.g., via a dylib or explicitly so).
-//
-// If an injected dependency was not found then we're guaranteed the
-// metadata::creader module has injected that dependency (not listed as
-// a required dependency) in one of the session's field. If this field is not
-// set then this compilation doesn't actually need the dependency and we can
-// also skip this step entirely.
-fn activate_injected_dep(injected: Option<CrateNum>,
-                         list: &mut DependencyList,
-                         replaces_injected: &dyn Fn(CrateNum) -> bool) {
-    for (i, slot) in list.iter().enumerate() {
-        let cnum = CrateNum::new(i + 1);
-        if !replaces_injected(cnum) {
-            continue
-        }
-        if *slot != Linkage::NotLinked {
-            return
-        }
-    }
-    if let Some(injected) = injected {
-        let idx = injected.as_usize() - 1;
-        assert_eq!(list[idx], Linkage::NotLinked);
-        list[idx] = Linkage::Static;
-    }
-}
-
-// After the linkage for a crate has been determined we need to verify that
-// there's only going to be one allocator in the output.
-fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) {
-    let sess = &tcx.sess;
-    if list.len() == 0 {
-        return
-    }
-    let mut panic_runtime = None;
-    for (i, linkage) in list.iter().enumerate() {
-        if let Linkage::NotLinked = *linkage {
-            continue
-        }
-        let cnum = CrateNum::new(i + 1);
-
-        if tcx.is_panic_runtime(cnum) {
-            if let Some((prev, _)) = panic_runtime {
-                let prev_name = tcx.crate_name(prev);
-                let cur_name = tcx.crate_name(cnum);
-                sess.err(&format!("cannot link together two \
-                                   panic runtimes: {} and {}",
-                                  prev_name, cur_name));
-            }
-            panic_runtime = Some((cnum, tcx.panic_strategy(cnum)));
-        }
-    }
-
-    // If we found a panic runtime, then we know by this point that it's the
-    // only one, but we perform validation here that all the panic strategy
-    // compilation modes for the whole DAG are valid.
-    if let Some((cnum, found_strategy)) = panic_runtime {
-        let desired_strategy = sess.panic_strategy();
-
-        // First up, validate that our selected panic runtime is indeed exactly
-        // our same strategy.
-        if found_strategy != desired_strategy {
-            sess.err(&format!("the linked panic runtime `{}` is \
-                               not compiled with this crate's \
-                               panic strategy `{}`",
-                              tcx.crate_name(cnum),
-                              desired_strategy.desc()));
-        }
-
-        // Next up, verify that all other crates are compatible with this panic
-        // strategy. If the dep isn't linked, we ignore it, and if our strategy
-        // is abort then it's compatible with everything. Otherwise all crates'
-        // panic strategy must match our own.
-        for (i, linkage) in list.iter().enumerate() {
-            if let Linkage::NotLinked = *linkage {
-                continue
-            }
-            if desired_strategy == PanicStrategy::Abort {
-                continue
-            }
-            let cnum = CrateNum::new(i + 1);
-            let found_strategy = tcx.panic_strategy(cnum);
-            let is_compiler_builtins = tcx.is_compiler_builtins(cnum);
-            if is_compiler_builtins || desired_strategy == found_strategy {
-                continue
-            }
-
-            sess.err(&format!("the crate `{}` is compiled with the \
-                               panic strategy `{}` which is \
-                               incompatible with this crate's \
-                               strategy of `{}`",
-                              tcx.crate_name(cnum),
-                              found_strategy.desc(),
-                              desired_strategy.desc()));
-        }
-    }
-}
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs
index c7260945295..fd99995480a 100644
--- a/src/librustc/query/mod.rs
+++ b/src/librustc/query/mod.rs
@@ -630,6 +630,12 @@ rustc_queries! {
                                         -> &'tcx [(CrateNum, LinkagePreference)] {
             desc { "dylib dependency formats of crate" }
         }
+
+        query dependency_formats(_: CrateNum)
+            -> Lrc<crate::middle::dependency_format::Dependencies>
+        {
+            desc { "get the linkage format of all dependencies" }
+        }
     }
 
     Codegen {
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 5eda3df3781..a9a20a92434 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -687,7 +687,7 @@ pub enum EntryFnType {
 
 impl_stable_hash_via_hash!(EntryFnType);
 
-#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug)]
+#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, HashStable)]
 pub enum CrateType {
     Executable,
     Dylib,
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 49342d95fdb..8e9c2735c39 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -7,7 +7,6 @@ use rustc_data_structures::fingerprint::Fingerprint;
 
 use crate::lint;
 use crate::lint::builtin::BuiltinLintDiagnostics;
-use crate::middle::dependency_format;
 use crate::session::config::{OutputType, PrintRequest, SwitchWithOptPath};
 use crate::session::search_paths::{PathKind, SearchPath};
 use crate::util::nodemap::{FxHashMap, FxHashSet};
@@ -91,7 +90,6 @@ pub struct Session {
     pub plugin_llvm_passes: OneThread<RefCell<Vec<String>>>,
     pub plugin_attributes: Lock<Vec<(Symbol, AttributeType)>>,
     pub crate_types: Once<Vec<config::CrateType>>,
-    pub dependency_formats: Once<dependency_format::Dependencies>,
     /// The `crate_disambiguator` is constructed out of all the `-C metadata`
     /// arguments passed to the compiler. Its value together with the crate-name
     /// forms a unique global identifier for the crate. It is used to allow
@@ -1248,7 +1246,6 @@ fn build_session_(
         plugin_llvm_passes: OneThread::new(RefCell::new(Vec::new())),
         plugin_attributes: Lock::new(Vec::new()),
         crate_types: Once::new(),
-        dependency_formats: Once::new(),
         crate_disambiguator: Once::new(),
         features: Once::new(),
         recursion_limit: Once::new(),
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 0155803e305..c03dbb2b1ed 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1510,9 +1510,9 @@ impl<'tcx> TyCtxt<'tcx> {
                 CrateType::Executable |
                 CrateType::Staticlib  |
                 CrateType::ProcMacro  |
+                CrateType::Dylib      |
                 CrateType::Cdylib     => false,
-                CrateType::Rlib       |
-                CrateType::Dylib      => true,
+                CrateType::Rlib       => true,
             }
         })
     }
diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs
index 9b044d9b453..3b7ae5e33d5 100644
--- a/src/librustc_codegen_ssa/back/link.rs
+++ b/src/librustc_codegen_ssa/back/link.rs
@@ -219,15 +219,24 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB
     (linker.to_path_buf(), cmd)
 }
 
-pub fn each_linked_rlib(sess: &Session,
-                               info: &CrateInfo,
-                               f: &mut dyn FnMut(CrateNum, &Path)) -> Result<(), String> {
+pub fn each_linked_rlib(
+    info: &CrateInfo,
+    f: &mut dyn FnMut(CrateNum, &Path),
+) -> Result<(), String> {
     let crates = info.used_crates_static.iter();
-    let fmts = sess.dependency_formats.borrow();
-    let fmts = fmts.get(&config::CrateType::Executable)
-                   .or_else(|| fmts.get(&config::CrateType::Staticlib))
-                   .or_else(|| fmts.get(&config::CrateType::Cdylib))
-                   .or_else(|| fmts.get(&config::CrateType::ProcMacro));
+    let mut fmts = None;
+    for (ty, list) in info.dependency_formats.iter() {
+        match ty {
+            config::CrateType::Executable |
+            config::CrateType::Staticlib |
+            config::CrateType::Cdylib |
+            config::CrateType::ProcMacro => {
+                fmts = Some(list);
+                break;
+            }
+            _ => {}
+        }
+    }
     let fmts = match fmts {
         Some(f) => f,
         None => return Err("could not find formats for rlibs".to_string())
@@ -406,7 +415,7 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
                            tempdir);
     let mut all_native_libs = vec![];
 
-    let res = each_linked_rlib(sess, &codegen_results.crate_info, &mut |cnum, path| {
+    let res = each_linked_rlib(&codegen_results.crate_info, &mut |cnum, path| {
         let name = &codegen_results.crate_info.crate_name[&cnum];
         let native_libs = &codegen_results.crate_info.native_libraries[&cnum];
 
@@ -1294,11 +1303,13 @@ pub fn add_local_native_libraries(cmd: &mut dyn Linker,
 // Rust crates are not considered at all when creating an rlib output. All
 // dependencies will be linked when producing the final output (instead of
 // the intermediate rlib version)
-fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(cmd: &mut dyn Linker,
-                            sess: &'a Session,
-                            codegen_results: &CodegenResults,
-                            crate_type: config::CrateType,
-                            tmpdir: &Path) {
+fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
+    cmd: &mut dyn Linker,
+    sess: &'a Session,
+    codegen_results: &CodegenResults,
+    crate_type: config::CrateType,
+    tmpdir: &Path,
+) {
     // All of the heavy lifting has previously been accomplished by the
     // dependency_format module of the compiler. This is just crawling the
     // output of that module, adding crates as necessary.
@@ -1307,8 +1318,10 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(cmd: &mut dyn Linker,
     // will slurp up the object files inside), and linking to a dynamic library
     // involves just passing the right -l flag.
 
-    let formats = sess.dependency_formats.borrow();
-    let data = formats.get(&crate_type).unwrap();
+    let (_, data) = codegen_results.crate_info.dependency_formats
+        .iter()
+        .find(|(ty, _)| *ty == crate_type)
+        .expect("failed to find crate type in dependency format list");
 
     // Invoke get_used_crates to ensure that we get a topological sorting of
     // crates.
@@ -1620,10 +1633,12 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(cmd: &mut dyn Linker,
 // generic function calls a native function, then the generic function must
 // be instantiated in the target crate, meaning that the native symbol must
 // also be resolved in the target crate.
-pub fn add_upstream_native_libraries(cmd: &mut dyn Linker,
-                                 sess: &Session,
-                                 codegen_results: &CodegenResults,
-                                 crate_type: config::CrateType) {
+pub fn add_upstream_native_libraries(
+    cmd: &mut dyn Linker,
+    sess: &Session,
+    codegen_results: &CodegenResults,
+    crate_type: config::CrateType,
+) {
     // Be sure to use a topological sorting of crates because there may be
     // interdependencies between native libraries. When passing -nodefaultlibs,
     // for example, almost all native libraries depend on libc, so we have to
@@ -1633,8 +1648,10 @@ pub fn add_upstream_native_libraries(cmd: &mut dyn Linker,
     // This passes RequireStatic, but the actual requirement doesn't matter,
     // we're just getting an ordering of crate numbers, we're not worried about
     // the paths.
-    let formats = sess.dependency_formats.borrow();
-    let data = formats.get(&crate_type).unwrap();
+    let (_, data) = codegen_results.crate_info.dependency_formats
+        .iter()
+        .find(|(ty, _)| *ty == crate_type)
+        .expect("failed to find crate type in dependency format list");
 
     let crates = &codegen_results.crate_info.used_crates_static;
     for &(cnum, _) in crates {
diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs
index c42cd024926..ff87f0b1547 100644
--- a/src/librustc_codegen_ssa/back/linker.rs
+++ b/src/librustc_codegen_ssa/back/linker.rs
@@ -14,6 +14,7 @@ use rustc::middle::dependency_format::Linkage;
 use rustc::session::Session;
 use rustc::session::config::{self, CrateType, OptLevel, DebugInfo,
                              LinkerPluginLto, Lto};
+use rustc::middle::exported_symbols::ExportedSymbol;
 use rustc::ty::TyCtxt;
 use rustc_target::spec::{LinkerFlavor, LldFlavor};
 use rustc_serialize::{json, Encoder};
@@ -1092,18 +1093,41 @@ fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
         }
     }
 
-    let formats = tcx.sess.dependency_formats.borrow();
-    let deps = formats[&crate_type].iter();
+    let formats = tcx.dependency_formats(LOCAL_CRATE);
+    let deps = formats.iter().filter_map(|(t, list)| {
+        if *t == crate_type {
+            Some(list)
+        } else {
+            None
+        }
+    }).next().unwrap();
 
-    for (index, dep_format) in deps.enumerate() {
+    for (index, dep_format) in deps.iter().enumerate() {
         let cnum = CrateNum::new(index + 1);
         // For each dependency that we are linking to statically ...
         if *dep_format == Linkage::Static {
             // ... we add its symbol list to our export list.
             for &(symbol, level) in tcx.exported_symbols(cnum).iter() {
-                if level.is_below_threshold(export_threshold) {
-                    symbols.push(symbol.symbol_name(tcx).to_string());
+                if !level.is_below_threshold(export_threshold) {
+                    continue;
                 }
+
+                // Do not export generic symbols from upstream crates in linked
+                // artifact (notably the `dylib` crate type). The main reason
+                // for this is that `symbol_name` is actually wrong for generic
+                // symbols because it guesses the name we'd give them locally
+                // rather than the name it has upstream (depending on
+                // `share_generics` settings and such).
+                //
+                // To fix that issue we just say that linked artifacts, aka
+                // `dylib`s, never export generic symbols and they aren't
+                // available to downstream crates. (the not available part is
+                // handled elsewhere).
+                if let ExportedSymbol::Generic(..) = symbol {
+                    continue;
+                }
+
+                symbols.push(symbol.symbol_name(tcx).to_string());
             }
         }
     }
diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/src/librustc_codegen_ssa/back/symbol_export.rs
index 7e700e68194..3e4b7695447 100644
--- a/src/librustc_codegen_ssa/back/symbol_export.rs
+++ b/src/librustc_codegen_ssa/back/symbol_export.rs
@@ -298,7 +298,7 @@ fn upstream_monomorphizations_provider(
     };
 
     for &cnum in cnums.iter() {
-        for &(ref exported_symbol, _) in tcx.exported_symbols(cnum).iter() {
+        for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() {
             if let &ExportedSymbol::Generic(def_id, substs) = exported_symbol {
                 let substs_map = instances.entry(def_id).or_default();
 
diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs
index 1bba479c1fd..1a2c23ae0d4 100644
--- a/src/librustc_codegen_ssa/back/write.rs
+++ b/src/librustc_codegen_ssa/back/write.rs
@@ -1048,7 +1048,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
     }).expect("failed to spawn helper thread");
 
     let mut each_linked_rlib_for_lto = Vec::new();
-    drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| {
+    drop(link::each_linked_rlib(crate_info, &mut |cnum, path| {
         if link::ignored_for_lto(sess, crate_info, cnum) {
             return
         }
diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs
index 4acbe0356b4..98d3022a418 100644
--- a/src/librustc_codegen_ssa/base.rs
+++ b/src/librustc_codegen_ssa/base.rs
@@ -539,7 +539,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
     // linkage, then it's already got an allocator shim and we'll be using that
     // one instead. If nothing exists then it's our job to generate the
     // allocator!
-    let any_dynamic_crate = tcx.sess.dependency_formats.borrow()
+    let any_dynamic_crate = tcx.dependency_formats(LOCAL_CRATE)
         .iter()
         .any(|(_, list)| {
             use rustc::middle::dependency_format::Linkage;
@@ -731,6 +731,7 @@ impl CrateInfo {
             used_crate_source: Default::default(),
             lang_item_to_crate: Default::default(),
             missing_lang_items: Default::default(),
+            dependency_formats: tcx.dependency_formats(LOCAL_CRATE),
         };
         let lang_items = tcx.lang_items();
 
diff --git a/src/librustc_codegen_ssa/lib.rs b/src/librustc_codegen_ssa/lib.rs
index 1708d7235b4..161d3ce61f0 100644
--- a/src/librustc_codegen_ssa/lib.rs
+++ b/src/librustc_codegen_ssa/lib.rs
@@ -33,6 +33,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lrc;
 use rustc_data_structures::svh::Svh;
 use rustc::middle::cstore::{LibSource, CrateSource, NativeLibrary};
+use rustc::middle::dependency_format::Dependencies;
 use syntax_pos::symbol::Symbol;
 
 mod error_codes;
@@ -142,6 +143,7 @@ pub struct CrateInfo {
     pub used_crates_dynamic: Vec<(CrateNum, LibSource)>,
     pub lang_item_to_crate: FxHashMap<LangItem, CrateNum>,
     pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>,
+    pub dependency_formats: Lrc<Dependencies>,
 }
 
 
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs
index 33de518c596..56db695f254 100644
--- a/src/librustc_interface/passes.rs
+++ b/src/librustc_interface/passes.rs
@@ -1075,10 +1075,6 @@ pub fn start_codegen<'tcx>(
         tcx.print_debug_stats();
     }
 
-    time(tcx.sess, "resolving dependency formats", || {
-        middle::dependency_format::calculate(tcx)
-    });
-
     let (metadata, need_metadata_module) = time(tcx.sess, "metadata encoding and writing", || {
         encode_and_write_metadata(tcx, outputs)
     });
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index a1e3bbcbf8e..0bc53dbde5f 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -10,6 +10,7 @@ use rustc::middle::cstore::{CrateStore, DepKind,
                             EncodedMetadata, NativeLibraryKind};
 use rustc::middle::exported_symbols::ExportedSymbol;
 use rustc::middle::stability::DeprecationEntry;
+use rustc::middle::dependency_format::Linkage;
 use rustc::hir::def;
 use rustc::hir;
 use rustc::session::{CrateDisambiguator, Session};
@@ -239,7 +240,30 @@ provide! { <'tcx> tcx, def_id, other, cdata,
 
     used_crate_source => { Lrc::new(cdata.source.clone()) }
 
-    exported_symbols => { Arc::new(cdata.exported_symbols(tcx)) }
+    exported_symbols => {
+        let mut syms = cdata.exported_symbols(tcx);
+
+        // When linked into a dylib crates don't export their generic symbols,
+        // so if that's happening then we can't load upstream monomorphizations
+        // from this crate.
+        let formats = tcx.dependency_formats(LOCAL_CRATE);
+        let remove_generics = formats.iter().any(|(_ty, list)| {
+            match list.get(def_id.krate.as_usize() - 1) {
+                Some(Linkage::IncludedFromDylib) | Some(Linkage::Dynamic) => true,
+                _ => false,
+            }
+        });
+        if remove_generics {
+            syms.retain(|(sym, _threshold)| {
+                match sym {
+                    ExportedSymbol::Generic(..) => false,
+                    _ => return true,
+                }
+            });
+        }
+
+        Arc::new(syms)
+    }
 }
 
 pub fn provide(providers: &mut Providers<'_>) {
@@ -370,6 +394,11 @@ pub fn provide(providers: &mut Providers<'_>) {
             tcx.arena.alloc(visible_parent_map)
         },
 
+        dependency_formats: |tcx, cnum| {
+            assert_eq!(cnum, LOCAL_CRATE);
+            Lrc::new(crate::dependency_format::calculate(tcx))
+        },
+
         ..*providers
     };
 }
diff --git a/src/librustc_metadata/dependency_format.rs b/src/librustc_metadata/dependency_format.rs
new file mode 100644
index 00000000000..9a30623b33d
--- /dev/null
+++ b/src/librustc_metadata/dependency_format.rs
@@ -0,0 +1,370 @@
+//! Resolution of mixing rlibs and dylibs
+//!
+//! When producing a final artifact, such as a dynamic library, the compiler has
+//! a choice between linking an rlib or linking a dylib of all upstream
+//! dependencies. The linking phase must guarantee, however, that a library only
+//! show up once in the object file. For example, it is illegal for library A to
+//! be statically linked to B and C in separate dylibs, and then link B and C
+//! into a crate D (because library A appears twice).
+//!
+//! The job of this module is to calculate what format each upstream crate
+//! should be used when linking each output type requested in this session. This
+//! generally follows this set of rules:
+//!
+//!     1. Each library must appear exactly once in the output.
+//!     2. Each rlib contains only one library (it's just an object file)
+//!     3. Each dylib can contain more than one library (due to static linking),
+//!        and can also bring in many dynamic dependencies.
+//!
+//! With these constraints in mind, it's generally a very difficult problem to
+//! find a solution that's not "all rlibs" or "all dylibs". I have suspicions
+//! that NP-ness may come into the picture here...
+//!
+//! The current selection algorithm below looks mostly similar to:
+//!
+//!     1. If static linking is required, then require all upstream dependencies
+//!        to be available as rlibs. If not, generate an error.
+//!     2. If static linking is requested (generating an executable), then
+//!        attempt to use all upstream dependencies as rlibs. If any are not
+//!        found, bail out and continue to step 3.
+//!     3. Static linking has failed, at least one library must be dynamically
+//!        linked. Apply a heuristic by greedily maximizing the number of
+//!        dynamically linked libraries.
+//!     4. Each upstream dependency available as a dynamic library is
+//!        registered. The dependencies all propagate, adding to a map. It is
+//!        possible for a dylib to add a static library as a dependency, but it
+//!        is illegal for two dylibs to add the same static library as a
+//!        dependency. The same dylib can be added twice. Additionally, it is
+//!        illegal to add a static dependency when it was previously found as a
+//!        dylib (and vice versa)
+//!     5. After all dynamic dependencies have been traversed, re-traverse the
+//!        remaining dependencies and add them statically (if they haven't been
+//!        added already).
+//!
+//! While not perfect, this algorithm should help support use-cases such as leaf
+//! dependencies being static while the larger tree of inner dependencies are
+//! all dynamic. This isn't currently very well battle tested, so it will likely
+//! fall short in some use cases.
+//!
+//! Currently, there is no way to specify the preference of linkage with a
+//! particular library (other than a global dynamic/static switch).
+//! Additionally, the algorithm is geared towards finding *any* solution rather
+//! than finding a number of solutions (there are normally quite a few).
+
+use rustc::hir::def_id::CrateNum;
+use rustc::middle::cstore::LinkagePreference::{self, RequireStatic, RequireDynamic};
+use rustc::middle::cstore::{self, DepKind};
+use rustc::middle::dependency_format::{DependencyList, Dependencies, Linkage};
+use rustc::session::config;
+use rustc::ty::TyCtxt;
+use rustc::util::nodemap::FxHashMap;
+use rustc_target::spec::PanicStrategy;
+
+pub fn calculate(tcx: TyCtxt<'_>) -> Dependencies {
+    tcx.sess.crate_types.borrow().iter().map(|&ty| {
+        let linkage = calculate_type(tcx, ty);
+        verify_ok(tcx, &linkage);
+        (ty, linkage)
+    }).collect::<Vec<_>>()
+}
+
+fn calculate_type(tcx: TyCtxt<'_>, ty: config::CrateType) -> DependencyList {
+    let sess = &tcx.sess;
+
+    if !sess.opts.output_types.should_codegen() {
+        return Vec::new();
+    }
+
+    let preferred_linkage = match ty {
+        // cdylibs must have all static dependencies.
+        config::CrateType::Cdylib => Linkage::Static,
+
+        // Generating a dylib without `-C prefer-dynamic` means that we're going
+        // to try to eagerly statically link all dependencies. This is normally
+        // done for end-product dylibs, not intermediate products.
+        config::CrateType::Dylib if !sess.opts.cg.prefer_dynamic => Linkage::Static,
+        config::CrateType::Dylib => Linkage::Dynamic,
+
+        // If the global prefer_dynamic switch is turned off, or the final
+        // executable will be statically linked, prefer static crate linkage.
+        config::CrateType::Executable if !sess.opts.cg.prefer_dynamic ||
+            sess.crt_static() => Linkage::Static,
+        config::CrateType::Executable => Linkage::Dynamic,
+
+        // proc-macro crates are mostly cdylibs, but we also need metadata.
+        config::CrateType::ProcMacro => Linkage::Static,
+
+        // No linkage happens with rlibs, we just needed the metadata (which we
+        // got long ago), so don't bother with anything.
+        config::CrateType::Rlib => Linkage::NotLinked,
+
+        // staticlibs must have all static dependencies.
+        config::CrateType::Staticlib => Linkage::Static,
+    };
+
+    if preferred_linkage == Linkage::NotLinked {
+        // If the crate is not linked, there are no link-time dependencies.
+        return Vec::new();
+    }
+
+    if preferred_linkage == Linkage::Static {
+        // Attempt static linkage first. For dylibs and executables, we may be
+        // able to retry below with dynamic linkage.
+        if let Some(v) = attempt_static(tcx) {
+            return v;
+        }
+
+        // Staticlibs, cdylibs, and static executables must have all static
+        // dependencies. If any are not found, generate some nice pretty errors.
+        if ty == config::CrateType::Cdylib || ty == config::CrateType::Staticlib ||
+                (ty == config::CrateType::Executable && sess.crt_static() &&
+                !sess.target.target.options.crt_static_allows_dylibs) {
+            for &cnum in tcx.crates().iter() {
+                if tcx.dep_kind(cnum).macros_only() { continue }
+                let src = tcx.used_crate_source(cnum);
+                if src.rlib.is_some() { continue }
+                sess.err(&format!("crate `{}` required to be available in rlib format, \
+                                   but was not found in this form",
+                                  tcx.crate_name(cnum)));
+            }
+            return Vec::new();
+        }
+    }
+
+    let mut formats = FxHashMap::default();
+
+    // Sweep all crates for found dylibs. Add all dylibs, as well as their
+    // dependencies, ensuring there are no conflicts. The only valid case for a
+    // dependency to be relied upon twice is for both cases to rely on a dylib.
+    for &cnum in tcx.crates().iter() {
+        if tcx.dep_kind(cnum).macros_only() { continue }
+        let name = tcx.crate_name(cnum);
+        let src = tcx.used_crate_source(cnum);
+        if src.dylib.is_some() {
+            log::info!("adding dylib: {}", name);
+            add_library(tcx, cnum, RequireDynamic, &mut formats);
+            let deps = tcx.dylib_dependency_formats(cnum);
+            for &(depnum, style) in deps.iter() {
+                log::info!("adding {:?}: {}", style, tcx.crate_name(depnum));
+                add_library(tcx, depnum, style, &mut formats);
+            }
+        }
+    }
+
+    // Collect what we've got so far in the return vector.
+    let last_crate = tcx.crates().len();
+    let mut ret = (1..last_crate+1).map(|cnum| {
+        match formats.get(&CrateNum::new(cnum)) {
+            Some(&RequireDynamic) => Linkage::Dynamic,
+            Some(&RequireStatic) => Linkage::IncludedFromDylib,
+            None => Linkage::NotLinked,
+        }
+    }).collect::<Vec<_>>();
+
+    // Run through the dependency list again, and add any missing libraries as
+    // static libraries.
+    //
+    // If the crate hasn't been included yet and it's not actually required
+    // (e.g., it's an allocator) then we skip it here as well.
+    for &cnum in tcx.crates().iter() {
+        let src = tcx.used_crate_source(cnum);
+        if src.dylib.is_none() &&
+           !formats.contains_key(&cnum) &&
+           tcx.dep_kind(cnum) == DepKind::Explicit {
+            assert!(src.rlib.is_some() || src.rmeta.is_some());
+            log::info!("adding staticlib: {}", tcx.crate_name(cnum));
+            add_library(tcx, cnum, RequireStatic, &mut formats);
+            ret[cnum.as_usize() - 1] = Linkage::Static;
+        }
+    }
+
+    // We've gotten this far because we're emitting some form of a final
+    // artifact which means that we may need to inject dependencies of some
+    // form.
+    //
+    // Things like allocators and panic runtimes may not have been activated
+    // quite yet, so do so here.
+    activate_injected_dep(*sess.injected_panic_runtime.get(), &mut ret,
+                          &|cnum| tcx.is_panic_runtime(cnum));
+
+    // When dylib B links to dylib A, then when using B we must also link to A.
+    // It could be the case, however, that the rlib for A is present (hence we
+    // found metadata), but the dylib for A has since been removed.
+    //
+    // For situations like this, we perform one last pass over the dependencies,
+    // making sure that everything is available in the requested format.
+    for (cnum, kind) in ret.iter().enumerate() {
+        let cnum = CrateNum::new(cnum + 1);
+        let src = tcx.used_crate_source(cnum);
+        match *kind {
+            Linkage::NotLinked |
+            Linkage::IncludedFromDylib => {}
+            Linkage::Static if src.rlib.is_some() => continue,
+            Linkage::Dynamic if src.dylib.is_some() => continue,
+            kind => {
+                let kind = match kind {
+                    Linkage::Static => "rlib",
+                    _ => "dylib",
+                };
+                sess.err(&format!("crate `{}` required to be available in {} format, \
+                                   but was not found in this form",
+                                  tcx.crate_name(cnum), kind));
+            }
+        }
+    }
+
+    ret
+}
+
+fn add_library(
+    tcx: TyCtxt<'_>,
+    cnum: CrateNum,
+    link: LinkagePreference,
+    m: &mut FxHashMap<CrateNum, LinkagePreference>,
+) {
+    match m.get(&cnum) {
+        Some(&link2) => {
+            // If the linkages differ, then we'd have two copies of the library
+            // if we continued linking. If the linkages are both static, then we
+            // would also have two copies of the library (static from two
+            // different locations).
+            //
+            // This error is probably a little obscure, but I imagine that it
+            // can be refined over time.
+            if link2 != link || link == RequireStatic {
+                tcx.sess.struct_err(&format!("cannot satisfy dependencies so `{}` only \
+                                              shows up once", tcx.crate_name(cnum)))
+                    .help("having upstream crates all available in one format \
+                           will likely make this go away")
+                    .emit();
+            }
+        }
+        None => { m.insert(cnum, link); }
+    }
+}
+
+fn attempt_static(tcx: TyCtxt<'_>) -> Option<DependencyList> {
+    let sess = &tcx.sess;
+    let crates = cstore::used_crates(tcx, RequireStatic);
+    if !crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) {
+        return None
+    }
+
+    // All crates are available in an rlib format, so we're just going to link
+    // everything in explicitly so long as it's actually required.
+    let last_crate = tcx.crates().len();
+    let mut ret = (1..last_crate+1).map(|cnum| {
+        if tcx.dep_kind(CrateNum::new(cnum)) == DepKind::Explicit {
+            Linkage::Static
+        } else {
+            Linkage::NotLinked
+        }
+    }).collect::<Vec<_>>();
+
+    // Our allocator/panic runtime may not have been linked above if it wasn't
+    // explicitly linked, which is the case for any injected dependency. Handle
+    // that here and activate them.
+    activate_injected_dep(*sess.injected_panic_runtime.get(), &mut ret,
+                          &|cnum| tcx.is_panic_runtime(cnum));
+
+    Some(ret)
+}
+
+// Given a list of how to link upstream dependencies so far, ensure that an
+// injected dependency is activated. This will not do anything if one was
+// transitively included already (e.g., via a dylib or explicitly so).
+//
+// If an injected dependency was not found then we're guaranteed the
+// metadata::creader module has injected that dependency (not listed as
+// a required dependency) in one of the session's field. If this field is not
+// set then this compilation doesn't actually need the dependency and we can
+// also skip this step entirely.
+fn activate_injected_dep(injected: Option<CrateNum>,
+                         list: &mut DependencyList,
+                         replaces_injected: &dyn Fn(CrateNum) -> bool) {
+    for (i, slot) in list.iter().enumerate() {
+        let cnum = CrateNum::new(i + 1);
+        if !replaces_injected(cnum) {
+            continue
+        }
+        if *slot != Linkage::NotLinked {
+            return
+        }
+    }
+    if let Some(injected) = injected {
+        let idx = injected.as_usize() - 1;
+        assert_eq!(list[idx], Linkage::NotLinked);
+        list[idx] = Linkage::Static;
+    }
+}
+
+// After the linkage for a crate has been determined we need to verify that
+// there's only going to be one allocator in the output.
+fn verify_ok(tcx: TyCtxt<'_>, list: &[Linkage]) {
+    let sess = &tcx.sess;
+    if list.len() == 0 {
+        return
+    }
+    let mut panic_runtime = None;
+    for (i, linkage) in list.iter().enumerate() {
+        if let Linkage::NotLinked = *linkage {
+            continue
+        }
+        let cnum = CrateNum::new(i + 1);
+
+        if tcx.is_panic_runtime(cnum) {
+            if let Some((prev, _)) = panic_runtime {
+                let prev_name = tcx.crate_name(prev);
+                let cur_name = tcx.crate_name(cnum);
+                sess.err(&format!("cannot link together two \
+                                   panic runtimes: {} and {}",
+                                  prev_name, cur_name));
+            }
+            panic_runtime = Some((cnum, tcx.panic_strategy(cnum)));
+        }
+    }
+
+    // If we found a panic runtime, then we know by this point that it's the
+    // only one, but we perform validation here that all the panic strategy
+    // compilation modes for the whole DAG are valid.
+    if let Some((cnum, found_strategy)) = panic_runtime {
+        let desired_strategy = sess.panic_strategy();
+
+        // First up, validate that our selected panic runtime is indeed exactly
+        // our same strategy.
+        if found_strategy != desired_strategy {
+            sess.err(&format!("the linked panic runtime `{}` is \
+                               not compiled with this crate's \
+                               panic strategy `{}`",
+                              tcx.crate_name(cnum),
+                              desired_strategy.desc()));
+        }
+
+        // Next up, verify that all other crates are compatible with this panic
+        // strategy. If the dep isn't linked, we ignore it, and if our strategy
+        // is abort then it's compatible with everything. Otherwise all crates'
+        // panic strategy must match our own.
+        for (i, linkage) in list.iter().enumerate() {
+            if let Linkage::NotLinked = *linkage {
+                continue
+            }
+            if desired_strategy == PanicStrategy::Abort {
+                continue
+            }
+            let cnum = CrateNum::new(i + 1);
+            let found_strategy = tcx.panic_strategy(cnum);
+            let is_compiler_builtins = tcx.is_compiler_builtins(cnum);
+            if is_compiler_builtins || desired_strategy == found_strategy {
+                continue
+            }
+
+            sess.err(&format!("the crate `{}` is compiled with the \
+                               panic strategy `{}` which is \
+                               incompatible with this crate's \
+                               strategy of `{}`",
+                              tcx.crate_name(cnum),
+                              found_strategy.desc(),
+                              desired_strategy.desc()));
+        }
+    }
+}
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index f430f01542e..be083cf6020 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -1649,20 +1649,22 @@ impl EncodeContext<'tcx> {
     }
 
     fn encode_dylib_dependency_formats(&mut self) -> Lazy<[Option<LinkagePreference>]> {
-        match self.tcx.sess.dependency_formats.borrow().get(&config::CrateType::Dylib) {
-            Some(arr) => {
-                self.lazy(arr.iter().map(|slot| {
-                    match *slot {
-                        Linkage::NotLinked |
-                        Linkage::IncludedFromDylib => None,
-
-                        Linkage::Dynamic => Some(LinkagePreference::RequireDynamic),
-                        Linkage::Static => Some(LinkagePreference::RequireStatic),
-                    }
-                }))
+        let formats = self.tcx.dependency_formats(LOCAL_CRATE);
+        for (ty, arr) in formats.iter() {
+            if *ty != config::CrateType::Dylib {
+                continue;
             }
-            None => Lazy::empty(),
+            return self.lazy(arr.iter().map(|slot| {
+                match *slot {
+                    Linkage::NotLinked |
+                    Linkage::IncludedFromDylib => None,
+
+                    Linkage::Dynamic => Some(LinkagePreference::RequireDynamic),
+                    Linkage::Static => Some(LinkagePreference::RequireStatic),
+                }
+            }));
         }
+        Lazy::empty()
     }
 
     fn encode_info_for_foreign_item(&mut self,
diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs
index e6104e629e9..9273b064ba9 100644
--- a/src/librustc_metadata/lib.rs
+++ b/src/librustc_metadata/lib.rs
@@ -32,6 +32,7 @@ mod schema;
 mod native_libs;
 mod link_args;
 mod foreign_modules;
+mod dependency_format;
 
 pub mod creader;
 pub mod cstore;
diff --git a/src/test/codegen-units/partitioning/auxiliary/shared_generics_aux.rs b/src/test/codegen-units/partitioning/auxiliary/shared_generics_aux.rs
index d50a7910fe0..9050e8f1671 100644
--- a/src/test/codegen-units/partitioning/auxiliary/shared_generics_aux.rs
+++ b/src/test/codegen-units/partitioning/auxiliary/shared_generics_aux.rs
@@ -1,4 +1,5 @@
 // compile-flags:-Zshare-generics=yes
+// no-prefer-dynamic
 
 #![crate_type="rlib"]
 
diff --git a/src/test/codegen-units/partitioning/shared-generics.rs b/src/test/codegen-units/partitioning/shared-generics.rs
index 010412b8bae..58e485be003 100644
--- a/src/test/codegen-units/partitioning/shared-generics.rs
+++ b/src/test/codegen-units/partitioning/shared-generics.rs
@@ -1,4 +1,5 @@
 // ignore-tidy-linelength
+// no-prefer-dynamic
 // compile-flags:-Zprint-mono-items=eager -Zshare-generics=yes -Zincremental=tmp/partitioning-tests/shared-generics-exe
 
 #![crate_type="rlib"]
diff --git a/src/test/compile-fail/two-panic-runtimes.rs b/src/test/compile-fail/two-panic-runtimes.rs
index 66b184e521a..671d44564e6 100644
--- a/src/test/compile-fail/two-panic-runtimes.rs
+++ b/src/test/compile-fail/two-panic-runtimes.rs
@@ -5,6 +5,7 @@
 // aux-build:panic-runtime-lang-items.rs
 
 #![no_std]
+#![no_main]
 
 extern crate panic_runtime_unwind;
 extern crate panic_runtime_unwind2;
diff --git a/src/test/run-make-fulldeps/issue-64319/Makefile b/src/test/run-make-fulldeps/issue-64319/Makefile
new file mode 100644
index 00000000000..b2c6b8b3cbb
--- /dev/null
+++ b/src/test/run-make-fulldeps/issue-64319/Makefile
@@ -0,0 +1,39 @@
+-include ../tools.mk
+
+# Different optimization levels imply different values for `-Zshare-generics`,
+# so try out a whole bunch of combinations to make sure everything is compatible
+all:
+	# First up, try some defaults
+	$(RUSTC) --crate-type rlib foo.rs
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=3
+
+	# Next try mixing up some things explicitly
+	$(RUSTC) --crate-type rlib foo.rs -Z share-generics=no
+	$(RUSTC) --crate-type dylib bar.rs -Z share-generics=no
+	$(RUSTC) --crate-type rlib foo.rs -Z share-generics=no
+	$(RUSTC) --crate-type dylib bar.rs -Z share-generics=yes
+	$(RUSTC) --crate-type rlib foo.rs -Z share-generics=yes
+	$(RUSTC) --crate-type dylib bar.rs -Z share-generics=no
+	$(RUSTC) --crate-type rlib foo.rs -Z share-generics=yes
+	$(RUSTC) --crate-type dylib bar.rs -Z share-generics=yes
+
+	# Now combine a whole bunch of options together
+	$(RUSTC) --crate-type rlib foo.rs
+	$(RUSTC) --crate-type dylib bar.rs
+	$(RUSTC) --crate-type dylib bar.rs -Z share-generics=no
+	$(RUSTC) --crate-type dylib bar.rs -Z share-generics=yes
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=1
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=1 -Z share-generics=no
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=1 -Z share-generics=yes
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=2
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=2 -Z share-generics=no
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=2 -Z share-generics=yes
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=3
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=3 -Z share-generics=no
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=3 -Z share-generics=yes
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=s
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=s -Z share-generics=no
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=s -Z share-generics=yes
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=z
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=z -Z share-generics=no
+	$(RUSTC) --crate-type dylib bar.rs -C opt-level=z -Z share-generics=yes
diff --git a/src/test/run-make-fulldeps/issue-64319/bar.rs b/src/test/run-make-fulldeps/issue-64319/bar.rs
new file mode 100644
index 00000000000..3895c0b6cdb
--- /dev/null
+++ b/src/test/run-make-fulldeps/issue-64319/bar.rs
@@ -0,0 +1,5 @@
+extern crate foo;
+
+pub fn bar() {
+    foo::foo();
+}
diff --git a/src/test/run-make-fulldeps/issue-64319/foo.rs b/src/test/run-make-fulldeps/issue-64319/foo.rs
new file mode 100644
index 00000000000..c54a238e9ad
--- /dev/null
+++ b/src/test/run-make-fulldeps/issue-64319/foo.rs
@@ -0,0 +1,9 @@
+pub fn foo() {
+    bar::<usize>();
+}
+
+pub fn bar<T>() {
+    baz();
+}
+
+fn baz() {}
diff --git a/src/test/run-make-fulldeps/symbol-visibility/Makefile b/src/test/run-make-fulldeps/symbol-visibility/Makefile
index 7901866015b..840fe801a95 100644
--- a/src/test/run-make-fulldeps/symbol-visibility/Makefile
+++ b/src/test/run-make-fulldeps/symbol-visibility/Makefile
@@ -79,12 +79,12 @@ all:
 	# Check that a Rust dylib exports its monomorphic functions, including generics this time
 	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rust_dylib)" -eq "1" ]
 	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rust_dylib)" -eq "1" ]
-	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_generic_function_from_rust_dylib)" -eq "1" ]
+	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_generic_function_from_rust_dylib)" -eq "0" ]
 
 	# Check that a Rust dylib exports the monomorphic functions from its dependencies
 	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ]
 	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rlib)" -eq "1" ]
-	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_generic_function_from_rlib)" -eq "1" ]
+	[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_generic_function_from_rlib)" -eq "0" ]
 
 	# Check that an executable does not export any dynamic symbols
 	[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_c_function_from_rlib)" -eq "0" ]
diff --git a/src/test/ui/panic-runtime/transitive-link-a-bunch.rs b/src/test/ui/panic-runtime/transitive-link-a-bunch.rs
index 9b083a640a6..5d72771c2dc 100644
--- a/src/test/ui/panic-runtime/transitive-link-a-bunch.rs
+++ b/src/test/ui/panic-runtime/transitive-link-a-bunch.rs
@@ -7,9 +7,8 @@
 // ignore-wasm32-bare compiled with panic=abort by default
 
 #![no_std]
+#![no_main]
 
 extern crate wants_panic_runtime_unwind;
 extern crate wants_panic_runtime_abort;
 extern crate panic_runtime_lang_items;
-
-fn main() {}
diff --git a/src/test/ui/panic-runtime/want-unwind-got-abort.rs b/src/test/ui/panic-runtime/want-unwind-got-abort.rs
index c9ee8a032a3..4c25c09d643 100644
--- a/src/test/ui/panic-runtime/want-unwind-got-abort.rs
+++ b/src/test/ui/panic-runtime/want-unwind-got-abort.rs
@@ -4,8 +4,7 @@
 // ignore-wasm32-bare compiled with panic=abort by default
 
 #![no_std]
+#![no_main]
 
 extern crate panic_runtime_abort;
 extern crate panic_runtime_lang_items;
-
-fn main() {}
diff --git a/src/test/ui/panic-runtime/want-unwind-got-abort2.rs b/src/test/ui/panic-runtime/want-unwind-got-abort2.rs
index 5219826eec6..478af451e7f 100644
--- a/src/test/ui/panic-runtime/want-unwind-got-abort2.rs
+++ b/src/test/ui/panic-runtime/want-unwind-got-abort2.rs
@@ -5,8 +5,7 @@
 // ignore-wasm32-bare compiled with panic=abort by default
 
 #![no_std]
+#![no_main]
 
 extern crate wants_panic_runtime_abort;
 extern crate panic_runtime_lang_items;
-
-fn main() {}