Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
unsafe {
1//! Validates all used crates and extern libraries and loads their metadata23use std::collections::BTreeMap;4use std::error::Error;5use std::path::Path;6use std::str::FromStr;7use std::time::Duration;8use std::{cmp, env, iter};910use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, AllocatorKind, global_fn_name};11use rustc_ast::{self as ast, *};12use rustc_data_structures::fx::FxHashSet;13use rustc_data_structures::owned_slice::OwnedSlice;14use rustc_data_structures::svh::Svh;15use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard};16use rustc_data_structures::unord::UnordMap;17use rustc_expand::base::SyntaxExtension;18use rustc_fs_util::try_canonicalize;19use rustc_hir as hir;20use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId};21use rustc_hir::definitions::Definitions;22use rustc_index::IndexVec;23use rustc_middle::bug;24use rustc_middle::ty::data_structures::IndexSet;25use rustc_middle::ty::{TyCtxt, TyCtxtFeed};26use rustc_proc_macro::bridge::client::ProcMacro;27use rustc_session::config::mitigation_coverage::DeniedPartialMitigationLevel;28use rustc_session::config::{29 CrateType, ExtendedTargetModifierInfo, ExternLocation, Externs, OptionsTargetModifiers,30 TargetModifier,31};32use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource};33use rustc_session::output::validate_crate_name;34use rustc_session::search_paths::PathKind;35use rustc_session::{Session, lint};36use rustc_span::def_id::DefId;37use rustc_span::edition::Edition;38use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};39use rustc_target::spec::{PanicStrategy, Target};40use tracing::{debug, info, trace};4142use crate::errors;43use crate::locator::{CrateError, CrateLocator, CratePaths, CrateRejections};44use crate::rmeta::{45 CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob, TargetModifiers,46};4748/// The backend's way to give the crate store access to the metadata in a library.49/// Note that it returns the raw metadata bytes stored in the library file, whether50/// it is compressed, uncompressed, some weird mix, etc.51/// rmeta files are backend independent and not handled here.52pub trait MetadataLoader {53 fn get_rlib_metadata(&self, target: &Target, filename: &Path) -> Result<OwnedSlice, String>;54 fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result<OwnedSlice, String>;55}5657pub type MetadataLoaderDyn = dyn MetadataLoader + Send + Sync + sync::DynSend + sync::DynSync;5859pub struct CStore {60 metadata_loader: Box<MetadataLoaderDyn>,6162 metas: IndexVec<CrateNum, Option<Box<CrateMetadata>>>,63 injected_panic_runtime: Option<CrateNum>,64 /// This crate needs an allocator and either provides it itself, or finds it in a dependency.65 /// If the above is true, then this field denotes the kind of the found allocator.66 allocator_kind: Option<AllocatorKind>,67 /// This crate needs an allocation error handler and either provides it itself, or finds it in a dependency.68 /// If the above is true, then this field denotes the kind of the found allocator.69 alloc_error_handler_kind: Option<AllocatorKind>,70 /// This crate has a `#[global_allocator]` item.71 has_global_allocator: bool,72 /// This crate has a `#[alloc_error_handler]` item.73 has_alloc_error_handler: bool,7475 /// Names that were used to load the crates via `extern crate` or paths.76 resolved_externs: UnordMap<Symbol, CrateNum>,7778 /// Unused externs of the crate79 unused_externs: Vec<Symbol>,8081 used_extern_options: FxHashSet<Symbol>,82 /// Whether there was a failure in resolving crate,83 /// it's used to suppress some diagnostics that would otherwise too noisey.84 has_crate_resolve_with_fail: bool,85}8687impl std::fmt::Debug for CStore {88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {89 f.debug_struct("CStore").finish_non_exhaustive()90 }91}9293pub enum LoadedMacro {94 MacroDef {95 def: MacroDef,96 ident: Ident,97 attrs: Vec<hir::Attribute>,98 span: Span,99 edition: Edition,100 },101 ProcMacro(SyntaxExtension),102}103104pub(crate) struct Library {105 pub source: CrateSource,106 pub metadata: MetadataBlob,107}108109enum LoadResult {110 Previous(CrateNum),111 Loaded(Library),112}113114struct CrateDump<'a>(&'a CStore);115116impl<'a> std::fmt::Debug for CrateDump<'a> {117 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {118 writeln!(fmt, "resolved crates:")?;119 for (cnum, data) in self.0.iter_crate_data() {120 writeln!(fmt, " name: {}", data.name())?;121 writeln!(fmt, " cnum: {cnum}")?;122 writeln!(fmt, " hash: {}", data.hash())?;123 writeln!(fmt, " reqd: {:?}", data.dep_kind())?;124 writeln!(fmt, " priv: {:?}", data.is_private_dep())?;125 let CrateSource { dylib, rlib, rmeta, sdylib_interface } = data.source();126 if let Some(dylib) = dylib {127 writeln!(fmt, " dylib: {}", dylib.display())?;128 }129 if let Some(rlib) = rlib {130 writeln!(fmt, " rlib: {}", rlib.display())?;131 }132 if let Some(rmeta) = rmeta {133 writeln!(fmt, " rmeta: {}", rmeta.display())?;134 }135 if let Some(sdylib_interface) = sdylib_interface {136 writeln!(fmt, " sdylib interface: {}", sdylib_interface.display())?;137 }138 }139 Ok(())140 }141}142143/// Reason that a crate is being sourced as a dependency.144#[derive(Clone, Copy)]145enum CrateOrigin<'a> {146 /// This crate was a dependency of another crate.147 IndirectDependency {148 /// Where this dependency was included from. Should only be used in error messages.149 dep_root_for_errors: &'a CratePaths,150 /// True if the parent is private, meaning the dependent should also be private.151 parent_private: bool,152 /// Dependency info about this crate.153 dep: &'a CrateDep,154 },155 /// Injected by `rustc`.156 Injected,157 /// Provided by `extern crate foo` or as part of the extern prelude.158 Extern,159}160161impl<'a> CrateOrigin<'a> {162 /// Return the dependency root, if any.163 fn dep_root_for_errors(&self) -> Option<&'a CratePaths> {164 match self {165 CrateOrigin::IndirectDependency { dep_root_for_errors, .. } => {166 Some(dep_root_for_errors)167 }168 _ => None,169 }170 }171172 /// Return dependency information, if any.173 fn dep(&self) -> Option<&'a CrateDep> {174 match self {175 CrateOrigin::IndirectDependency { dep, .. } => Some(dep),176 _ => None,177 }178 }179180 /// `Some(true)` if the dependency is private or its parent is private, `Some(false)` if the181 /// dependency is not private, `None` if it could not be determined.182 fn private_dep(&self) -> Option<bool> {183 match self {184 CrateOrigin::IndirectDependency { parent_private, dep, .. } => {185 Some(dep.is_private || *parent_private)186 }187 CrateOrigin::Injected => Some(true),188 _ => None,189 }190 }191}192193impl CStore {194 pub fn from_tcx(tcx: TyCtxt<'_>) -> FreezeReadGuard<'_, CStore> {195 FreezeReadGuard::map(tcx.untracked().cstore.read(), |cstore| {196 cstore.as_any().downcast_ref::<CStore>().expect("`tcx.cstore` is not a `CStore`")197 })198 }199200 pub fn from_tcx_mut(tcx: TyCtxt<'_>) -> FreezeWriteGuard<'_, CStore> {201 FreezeWriteGuard::map(tcx.untracked().cstore.write(), |cstore| {202 cstore.untracked_as_any().downcast_mut().expect("`tcx.cstore` is not a `CStore`")203 })204 }205206 fn intern_stable_crate_id<'tcx>(207 &mut self,208 tcx: TyCtxt<'tcx>,209 root: &CrateRoot,210 ) -> Result<TyCtxtFeed<'tcx, CrateNum>, CrateError> {211 assert_eq!(self.metas.len(), tcx.untracked().stable_crate_ids.read().len());212 let num = tcx.create_crate_num(root.stable_crate_id()).map_err(|existing| {213 // Check for (potential) conflicts with the local crate214 if existing == LOCAL_CRATE {215 CrateError::SymbolConflictsCurrent(root.name())216 } else if let Some(crate_name1) = self.metas[existing].as_ref().map(|data| data.name())217 {218 let crate_name0 = root.name();219 CrateError::StableCrateIdCollision(crate_name0, crate_name1)220 } else {221 CrateError::NotFound(root.name())222 }223 })?;224225 self.metas.push(None);226 Ok(num)227 }228229 pub fn has_crate_data(&self, cnum: CrateNum) -> bool {230 self.metas[cnum].is_some()231 }232233 pub(crate) fn get_crate_data(&self, cnum: CrateNum) -> &CrateMetadata {234 self.metas[cnum].as_ref().unwrap_or_else(|| panic!("Failed to get crate data for {cnum:?}"))235 }236237 pub(crate) fn get_crate_data_mut(&mut self, cnum: CrateNum) -> &mut CrateMetadata {238 self.metas[cnum].as_mut().unwrap_or_else(|| panic!("Failed to get crate data for {cnum:?}"))239 }240241 fn set_crate_data(&mut self, cnum: CrateNum, data: CrateMetadata) {242 assert!(self.metas[cnum].is_none(), "Overwriting crate metadata entry");243 self.metas[cnum] = Some(Box::new(data));244 }245246 /// Save the name used to resolve the extern crate in the local crate247 ///248 /// The name isn't always the crate's own name, because `sess.opts.externs` can assign it another name.249 /// It's also not always the same as the `DefId`'s symbol due to renames `extern crate resolved_name as defid_name`.250 pub(crate) fn set_resolved_extern_crate_name(&mut self, name: Symbol, extern_crate: CrateNum) {251 self.resolved_externs.insert(name, extern_crate);252 }253254 /// Crate resolved and loaded via the given extern name255 /// (corresponds to names in `sess.opts.externs`)256 ///257 /// May be `None` if the crate wasn't used258 pub fn resolved_extern_crate(&self, externs_name: Symbol) -> Option<CrateNum> {259 self.resolved_externs.get(&externs_name).copied()260 }261262 pub(crate) fn iter_crate_data(&self) -> impl Iterator<Item = (CrateNum, &CrateMetadata)> {263 self.metas264 .iter_enumerated()265 .filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data)))266 }267268 pub fn all_proc_macro_def_ids(&self, tcx: TyCtxt<'_>) -> impl Iterator<Item = DefId> {269 self.iter_crate_data().flat_map(move |(krate, data)| data.proc_macros_for_crate(tcx, krate))270 }271272 fn push_dependencies_in_postorder(&self, deps: &mut IndexSet<CrateNum>, cnum: CrateNum) {273 if !deps.contains(&cnum) {274 let cdata = self.get_crate_data(cnum);275 for dep in cdata.dependencies() {276 if dep != cnum {277 self.push_dependencies_in_postorder(deps, dep);278 }279 }280281 deps.insert(cnum);282 }283 }284285 pub(crate) fn crate_dependencies_in_postorder(&self, cnum: CrateNum) -> IndexSet<CrateNum> {286 let mut deps = IndexSet::default();287 if cnum == LOCAL_CRATE {288 for (cnum, _) in self.iter_crate_data() {289 self.push_dependencies_in_postorder(&mut deps, cnum);290 }291 } else {292 self.push_dependencies_in_postorder(&mut deps, cnum);293 }294 deps295 }296297 pub(crate) fn injected_panic_runtime(&self) -> Option<CrateNum> {298 self.injected_panic_runtime299 }300301 pub(crate) fn allocator_kind(&self) -> Option<AllocatorKind> {302 self.allocator_kind303 }304305 pub(crate) fn alloc_error_handler_kind(&self) -> Option<AllocatorKind> {306 self.alloc_error_handler_kind307 }308309 pub(crate) fn has_global_allocator(&self) -> bool {310 self.has_global_allocator311 }312313 pub(crate) fn has_alloc_error_handler(&self) -> bool {314 self.has_alloc_error_handler315 }316317 pub fn had_extern_crate_load_failure(&self) -> bool {318 self.has_crate_resolve_with_fail319 }320321 pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) {322 let json_unused_externs = tcx.sess.opts.json_unused_externs;323324 // We put the check for the option before the lint_level_at_node call325 // because the call mutates internal state and introducing it326 // leads to some ui tests failing.327 if !json_unused_externs.is_enabled() {328 return;329 }330 let level = tcx331 .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)332 .level;333 if level != lint::Level::Allow {334 let unused_externs =335 self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();336 let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>();337 tcx.dcx().emit_unused_externs(level, json_unused_externs.is_loud(), &unused_externs);338 }339 }340341 fn report_target_modifiers_extended(342 tcx: TyCtxt<'_>,343 krate: &Crate,344 mods: &TargetModifiers,345 dep_mods: &TargetModifiers,346 data: &CrateMetadata,347 ) {348 let span = krate.spans.inner_span.shrink_to_lo();349 let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch;350 let local_crate = tcx.crate_name(LOCAL_CRATE);351 let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone());352 let report_diff = |prefix: &String,353 opt_name: &String,354 flag_local_value: Option<&String>,355 flag_extern_value: Option<&String>| {356 if allowed_flag_mismatches.contains(&opt_name) {357 return;358 }359 let extern_crate = data.name();360 let flag_name = opt_name.clone();361 let flag_name_prefixed = format!("-{}{}", prefix, opt_name);362363 match (flag_local_value, flag_extern_value) {364 (Some(local_value), Some(extern_value)) => {365 tcx.dcx().emit_err(errors::IncompatibleTargetModifiers {366 span,367 extern_crate,368 local_crate,369 flag_name,370 flag_name_prefixed,371 local_value: local_value.to_string(),372 extern_value: extern_value.to_string(),373 })374 }375 (None, Some(extern_value)) => {376 tcx.dcx().emit_err(errors::IncompatibleTargetModifiersLMissed {377 span,378 extern_crate,379 local_crate,380 flag_name,381 flag_name_prefixed,382 extern_value: extern_value.to_string(),383 })384 }385 (Some(local_value), None) => {386 tcx.dcx().emit_err(errors::IncompatibleTargetModifiersRMissed {387 span,388 extern_crate,389 local_crate,390 flag_name,391 flag_name_prefixed,392 local_value: local_value.to_string(),393 })394 }395 (None, None) => panic!("Incorrect target modifiers report_diff(None, None)"),396 };397 };398 let mut it1 = mods.iter().map(tmod_extender);399 let mut it2 = dep_mods.iter().map(tmod_extender);400 let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None;401 let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None;402 loop {403 left_name_val = left_name_val.or_else(|| it1.next());404 right_name_val = right_name_val.or_else(|| it2.next());405 match (&left_name_val, &right_name_val) {406 (Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) {407 cmp::Ordering::Equal => {408 if !l.1.consistent(&tcx.sess, Some(&r.1)) {409 report_diff(410 &l.0.prefix,411 &l.0.name,412 Some(&l.1.value_name),413 Some(&r.1.value_name),414 );415 }416 left_name_val = None;417 right_name_val = None;418 }419 cmp::Ordering::Greater => {420 if !r.1.consistent(&tcx.sess, None) {421 report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));422 }423 right_name_val = None;424 }425 cmp::Ordering::Less => {426 if !l.1.consistent(&tcx.sess, None) {427 report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);428 }429 left_name_val = None;430 }431 },432 (Some(l), None) => {433 if !l.1.consistent(&tcx.sess, None) {434 report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);435 }436 left_name_val = None;437 }438 (None, Some(r)) => {439 if !r.1.consistent(&tcx.sess, None) {440 report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));441 }442 right_name_val = None;443 }444 (None, None) => break,445 }446 }447 }448449 pub fn report_session_incompatibilities(&self, tcx: TyCtxt<'_>, krate: &Crate) {450 self.report_incompatible_target_modifiers(tcx, krate);451 self.report_incompatible_partial_mitigations(tcx, krate);452 self.report_incompatible_async_drop_feature(tcx, krate);453 }454455 pub fn report_incompatible_target_modifiers(&self, tcx: TyCtxt<'_>, krate: &Crate) {456 for flag_name in &tcx.sess.opts.cg.unsafe_allow_abi_mismatch {457 if !OptionsTargetModifiers::is_target_modifier(flag_name) {458 tcx.dcx().emit_err(errors::UnknownTargetModifierUnsafeAllowed {459 span: krate.spans.inner_span.shrink_to_lo(),460 flag_name: flag_name.clone(),461 });462 }463 }464 let mods = tcx.sess.opts.gather_target_modifiers();465 for (_cnum, data) in self.iter_crate_data() {466 if data.is_proc_macro_crate() {467 continue;468 }469 let dep_mods = data.target_modifiers();470 if mods != dep_mods {471 Self::report_target_modifiers_extended(tcx, krate, &mods, &dep_mods, data);472 }473 }474 }475476 pub fn report_incompatible_partial_mitigations(&self, tcx: TyCtxt<'_>, krate: &Crate) {477 let my_mitigations = tcx.sess.gather_enabled_denied_partial_mitigations();478 let mut my_mitigations: BTreeMap<_, _> =479 my_mitigations.iter().map(|mitigation| (mitigation.kind, mitigation)).collect();480 for skipped_mitigation in tcx.sess.opts.allowed_partial_mitigations(tcx.sess.edition()) {481 my_mitigations.remove(&skipped_mitigation);482 }483 const MAX_ERRORS_PER_MITIGATION: usize = 5;484 let mut errors_per_mitigation = BTreeMap::new();485 for (_cnum, data) in self.iter_crate_data() {486 if data.is_proc_macro_crate() {487 continue;488 }489 let their_mitigations = data.enabled_denied_partial_mitigations();490 for my_mitigation in my_mitigations.values() {491 let their_mitigation = their_mitigations492 .iter()493 .find(|mitigation| mitigation.kind == my_mitigation.kind)494 .map_or(DeniedPartialMitigationLevel::Enabled(false), |m| m.level);495 if their_mitigation < my_mitigation.level {496 let errors = errors_per_mitigation.entry(my_mitigation.kind).or_insert(0);497 if *errors >= MAX_ERRORS_PER_MITIGATION {498 continue;499 }500 *errors += 1;501502 tcx.dcx().emit_err(errors::MitigationLessStrictInDependency {503 span: krate.spans.inner_span.shrink_to_lo(),504 mitigation_name: my_mitigation.kind.to_string(),505 mitigation_level: my_mitigation.level.level_str().to_string(),506 extern_crate: data.name(),507 });508 }509 }510 }511 }512513 // Report about async drop types in dependency if async drop feature is disabled514 pub fn report_incompatible_async_drop_feature(&self, tcx: TyCtxt<'_>, krate: &Crate) {515 if tcx.features().async_drop() {516 return;517 }518 for (_cnum, data) in self.iter_crate_data() {519 if data.is_proc_macro_crate() {520 continue;521 }522 if data.has_async_drops() {523 let extern_crate = data.name();524 let local_crate = tcx.crate_name(LOCAL_CRATE);525 tcx.dcx().emit_warn(errors::AsyncDropTypesInDependency {526 span: krate.spans.inner_span.shrink_to_lo(),527 extern_crate,528 local_crate,529 });530 }531 }532 }533534 pub fn new(metadata_loader: Box<MetadataLoaderDyn>) -> CStore {535 CStore {536 metadata_loader,537 // We add an empty entry for LOCAL_CRATE (which maps to zero) in538 // order to make array indices in `metas` match with the539 // corresponding `CrateNum`. This first entry will always remain540 // `None`.541 metas: IndexVec::from_iter(iter::once(None)),542 injected_panic_runtime: None,543 allocator_kind: None,544 alloc_error_handler_kind: None,545 has_global_allocator: false,546 has_alloc_error_handler: false,547 resolved_externs: UnordMap::default(),548 unused_externs: Vec::new(),549 used_extern_options: Default::default(),550 has_crate_resolve_with_fail: false,551 }552 }553554 fn existing_match(&self, name: Symbol, hash: Option<Svh>) -> Option<CrateNum> {555 let hash = hash?;556557 for (cnum, data) in self.iter_crate_data() {558 if data.name() != name {559 trace!("{} did not match {}", data.name(), name);560 continue;561 }562563 if hash == data.hash() {564 return Some(cnum);565 } else {566 debug!("actual hash {} did not match expected {}", hash, data.hash());567 }568 }569570 None571 }572573 /// Determine whether a dependency should be considered private.574 ///575 /// Dependencies are private if they get extern option specified, e.g. `--extern priv:mycrate`.576 /// This is stored in metadata, so `private_dep` can be correctly set during load. A `Some`577 /// value for `private_dep` indicates that the crate is known to be private or public (note578 /// that any `None` or `Some(false)` use of the same crate will make it public).579 ///580 /// Sometimes the directly dependent crate is not specified by `--extern`, in this case,581 /// `private-dep` is none during loading. This is equivalent to the scenario where the582 /// command parameter is set to `public-dependency`583 fn is_private_dep(&self, externs: &Externs, name: Symbol, private_dep: Option<bool>) -> bool {584 let extern_private = externs.get(name.as_str()).map(|e| e.is_private_dep);585 match (extern_private, private_dep) {586 // Explicit non-private via `--extern`, explicit non-private from metadata, or587 // unspecified with default to public.588 (Some(false), _) | (_, Some(false)) | (None, None) => false,589 // Marked private via `--extern priv:mycrate` or in metadata.590 (Some(true) | None, Some(true) | None) => true,591 }592 }593594 fn register_crate<'tcx>(595 &mut self,596 tcx: TyCtxt<'tcx>,597 host_lib: Option<Library>,598 origin: CrateOrigin<'_>,599 lib: Library,600 dep_kind: CrateDepKind,601 name: Symbol,602 private_dep: Option<bool>,603 ) -> Result<CrateNum, CrateError> {604 let _prof_timer =605 tcx.sess.prof.generic_activity_with_arg("metadata_register_crate", name.as_str());606607 let Library { source, metadata } = lib;608 let crate_root = metadata.get_root();609 let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash());610 let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep);611612 // Claim this crate number and cache it613 let feed = self.intern_stable_crate_id(tcx, &crate_root)?;614 let cnum = feed.key();615616 info!(617 "register crate `{}` (cnum = {}. private_dep = {})",618 crate_root.name(),619 cnum,620 private_dep621 );622623 // Maintain a reference to the top most crate.624 // Stash paths for top-most crate locally if necessary.625 let crate_paths;626 let dep_root_for_errors = if let Some(dep_root_for_errors) = origin.dep_root_for_errors() {627 dep_root_for_errors628 } else {629 crate_paths = CratePaths::new(crate_root.name(), source.clone());630 &crate_paths631 };632633 let cnum_map = self.resolve_crate_deps(634 tcx,635 dep_root_for_errors,636 &crate_root,637 &metadata,638 cnum,639 dep_kind,640 private_dep,641 )?;642643 let raw_proc_macros = if crate_root.is_proc_macro_crate() {644 let temp_root;645 let (dlsym_source, dlsym_root) = match &host_lib {646 Some(host_lib) => (&host_lib.source, {647 temp_root = host_lib.metadata.get_root();648 &temp_root649 }),650 None => (&source, &crate_root),651 };652 let dlsym_dylib = dlsym_source.dylib.as_ref().expect("no dylib for a proc-macro crate");653 Some(self.dlsym_proc_macros(tcx.sess, dlsym_dylib, dlsym_root.stable_crate_id())?)654 } else {655 None656 };657658 let crate_metadata = CrateMetadata::new(659 tcx,660 metadata,661 crate_root,662 raw_proc_macros,663 cnum,664 cnum_map,665 dep_kind,666 source,667 private_dep,668 host_hash,669 );670671 self.set_crate_data(cnum, crate_metadata);672673 Ok(cnum)674 }675676 fn load_proc_macro<'a, 'b>(677 &self,678 sess: &'a Session,679 locator: &mut CrateLocator<'b>,680 crate_rejections: &mut CrateRejections,681 path_kind: PathKind,682 host_hash: Option<Svh>,683 ) -> Result<Option<(LoadResult, Option<Library>)>, CrateError>684 where685 'a: 'b,686 {687 if sess.opts.unstable_opts.dual_proc_macros {688 // Use a new crate locator and crate rejections so trying to load a proc macro doesn't689 // affect the error message we emit690 let mut proc_macro_locator = locator.clone();691692 // Try to load a proc macro693 proc_macro_locator.for_target_proc_macro(sess, path_kind);694695 // Load the proc macro crate for the target696 let target_result =697 match self.load(&mut proc_macro_locator, &mut CrateRejections::default())? {698 Some(LoadResult::Previous(cnum)) => {699 return Ok(Some((LoadResult::Previous(cnum), None)));700 }701 Some(LoadResult::Loaded(library)) => Some(LoadResult::Loaded(library)),702 None => return Ok(None),703 };704705 // Use the existing crate_rejections as we want the error message to be affected by706 // loading the host proc macro.707 *crate_rejections = CrateRejections::default();708709 // Load the proc macro crate for the host710 locator.for_proc_macro(sess, path_kind);711712 locator.hash = host_hash;713714 let Some(host_result) = self.load(locator, crate_rejections)? else {715 return Ok(None);716 };717718 let host_result = match host_result {719 LoadResult::Previous(..) => {720 panic!("host and target proc macros must be loaded in lock-step")721 }722 LoadResult::Loaded(library) => library,723 };724 Ok(Some((target_result.unwrap(), Some(host_result))))725 } else {726 // Use a new crate locator and crate rejections so trying to load a proc macro doesn't727 // affect the error message we emit728 let mut proc_macro_locator = locator.clone();729730 // Load the proc macro crate for the host731 proc_macro_locator.for_proc_macro(sess, path_kind);732733 let Some(host_result) =734 self.load(&mut proc_macro_locator, &mut CrateRejections::default())?735 else {736 return Ok(None);737 };738739 Ok(Some((host_result, None)))740 }741 }742743 fn resolve_crate<'tcx>(744 &mut self,745 tcx: TyCtxt<'tcx>,746 name: Symbol,747 span: Span,748 dep_kind: CrateDepKind,749 origin: CrateOrigin<'_>,750 ) -> Option<CrateNum> {751 self.used_extern_options.insert(name);752 match self.maybe_resolve_crate(tcx, name, dep_kind, origin) {753 Ok(cnum) => {754 self.set_used_recursively(cnum);755 Some(cnum)756 }757 Err(err) => {758 debug!("failed to resolve crate {} {:?}", name, dep_kind);759 // crate maybe injrected with `standard_library_imports::inject`, their span is dummy.760 // we ignore compiler-injected prelude/sysroot loads here so they don't suppress761 // unrelated diagnostics, such as `unsupported targets for std library` etc,762 // these maybe helpful for users to resolve crate loading failure.763 if !tcx.sess.dcx().has_errors().is_some() && !span.is_dummy() {764 self.has_crate_resolve_with_fail = true;765 }766 let missing_core = self767 .maybe_resolve_crate(768 tcx,769 sym::core,770 CrateDepKind::Unconditional,771 CrateOrigin::Extern,772 )773 .is_err();774 err.report(tcx.sess, span, missing_core);775 None776 }777 }778 }779780 fn maybe_resolve_crate<'b, 'tcx>(781 &'b mut self,782 tcx: TyCtxt<'tcx>,783 name: Symbol,784 mut dep_kind: CrateDepKind,785 origin: CrateOrigin<'b>,786 ) -> Result<CrateNum, CrateError> {787 info!("resolving crate `{}`", name);788 if !name.as_str().is_ascii() {789 return Err(CrateError::NonAsciiName(name));790 }791792 let dep_root_for_errors = origin.dep_root_for_errors();793 let dep = origin.dep();794 let hash = dep.map(|d| d.hash);795 let host_hash = dep.map(|d| d.host_hash).flatten();796 let extra_filename = dep.map(|d| &d.extra_filename[..]);797 let path_kind = if dep.is_some() { PathKind::Dependency } else { PathKind::Crate };798 let private_dep = origin.private_dep();799800 let result = if let Some(cnum) = self.existing_match(name, hash) {801 (LoadResult::Previous(cnum), None)802 } else {803 info!("falling back to a load");804 let mut locator = CrateLocator::new(805 tcx.sess,806 &*self.metadata_loader,807 name,808 // The all loop is because `--crate-type=rlib --crate-type=rlib` is809 // legal and produces both inside this type.810 tcx.crate_types().iter().all(|c| *c == CrateType::Rlib),811 hash,812 extra_filename,813 path_kind,814 );815 let mut crate_rejections = CrateRejections::default();816817 match self.load(&mut locator, &mut crate_rejections)? {818 Some(res) => (res, None),819 None => {820 info!("falling back to loading proc_macro");821 dep_kind = CrateDepKind::MacrosOnly;822 match self.load_proc_macro(823 tcx.sess,824 &mut locator,825 &mut crate_rejections,826 path_kind,827 host_hash,828 )? {829 Some(res) => res,830 None => {831 return Err(832 locator.into_error(crate_rejections, dep_root_for_errors.cloned())833 );834 }835 }836 }837 }838 };839840 match result {841 (LoadResult::Previous(cnum), None) => {842 info!("library for `{}` was loaded previously, cnum {cnum}", name);843 // When `private_dep` is none, it indicates the directly dependent crate. If it is844 // not specified by `--extern` on command line parameters, it may be845 // `private-dependency` when `register_crate` is called for the first time. Then it must be updated to846 // `public-dependency` here.847 let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep);848 let cdata = self.get_crate_data_mut(cnum);849 if cdata.is_proc_macro_crate() {850 dep_kind = CrateDepKind::MacrosOnly;851 }852 cdata.set_dep_kind(cmp::max(cdata.dep_kind(), dep_kind));853 cdata.update_and_private_dep(private_dep);854 Ok(cnum)855 }856 (LoadResult::Loaded(library), host_library) => {857 info!("register newly loaded library for `{}`", name);858 self.register_crate(tcx, host_library, origin, library, dep_kind, name, private_dep)859 }860 _ => panic!(),861 }862 }863864 fn load(865 &self,866 locator: &CrateLocator<'_>,867 crate_rejections: &mut CrateRejections,868 ) -> Result<Option<LoadResult>, CrateError> {869 let Some(library) = locator.maybe_load_library_crate(crate_rejections)? else {870 return Ok(None);871 };872873 // In the case that we're loading a crate, but not matching874 // against a hash, we could load a crate which has the same hash875 // as an already loaded crate. If this is the case prevent876 // duplicates by just using the first crate.877 let root = library.metadata.get_root();878 let mut result = LoadResult::Loaded(library);879 for (cnum, data) in self.iter_crate_data() {880 if data.name() == root.name() && root.hash() == data.hash() {881 assert!(locator.hash.is_none());882 info!("load success, going to previous cnum: {}", cnum);883 result = LoadResult::Previous(cnum);884 break;885 }886 }887 Ok(Some(result))888 }889890 /// Go through the crate metadata and load any crates that it references.891 fn resolve_crate_deps(892 &mut self,893 tcx: TyCtxt<'_>,894 dep_root_for_errors: &CratePaths,895 crate_root: &CrateRoot,896 metadata: &MetadataBlob,897 krate: CrateNum,898 dep_kind: CrateDepKind,899 parent_is_private: bool,900 ) -> Result<CrateNumMap, CrateError> {901 debug!(902 "resolving deps of external crate `{}` with dep root `{}`",903 crate_root.name(),904 dep_root_for_errors.name905 );906 if crate_root.is_proc_macro_crate() {907 return Ok(CrateNumMap::new());908 }909910 // The map from crate numbers in the crate we're resolving to local crate numbers.911 // We map 0 and all other holes in the map to our parent crate. The "additional"912 // self-dependencies should be harmless.913 let deps = crate_root.decode_crate_deps(metadata);914 let mut crate_num_map = CrateNumMap::with_capacity(1 + deps.len());915 crate_num_map.push(krate);916 for dep in deps {917 info!(918 "resolving dep `{}`->`{}` hash: `{}` extra filename: `{}` private {}",919 crate_root.name(),920 dep.name,921 dep.hash,922 dep.extra_filename,923 dep.is_private,924 );925 let dep_kind = match dep_kind {926 CrateDepKind::MacrosOnly => CrateDepKind::MacrosOnly,927 _ => dep.kind,928 };929 let cnum = self.maybe_resolve_crate(930 tcx,931 dep.name,932 dep_kind,933 CrateOrigin::IndirectDependency {934 dep_root_for_errors,935 parent_private: parent_is_private,936 dep: &dep,937 },938 )?;939 crate_num_map.push(cnum);940 }941942 debug!("resolve_crate_deps: cnum_map for {:?} is {:?}", krate, crate_num_map);943 Ok(crate_num_map)944 }945946 fn dlsym_proc_macros(947 &self,948 sess: &Session,949 path: &Path,950 stable_crate_id: StableCrateId,951 ) -> Result<&'static [ProcMacro], CrateError> {952 let sym_name = sess.generate_proc_macro_decls_symbol(stable_crate_id);953 debug!("trying to dlsym proc_macros {} for symbol `{}`", path.display(), sym_name);954955 unsafe {956 let result = load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name);957 match result {958 Ok(result) => {959 debug!("loaded dlsym proc_macros {} for symbol `{}`", path.display(), sym_name);960 Ok(*result)961 }962 Err(err) => {963 debug!(964 "failed to dlsym proc_macros {} for symbol `{}`",965 path.display(),966 sym_name967 );968 Err(err.into())969 }970 }971 }972 }973974 fn inject_panic_runtime(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {975 // If we're only compiling an rlib, then there's no need to select a976 // panic runtime, so we just skip this section entirely.977 let only_rlib = tcx.crate_types().iter().all(|ct| *ct == CrateType::Rlib);978 if only_rlib {979 info!("panic runtime injection skipped, only generating rlib");980 return;981 }982983 // If we need a panic runtime, we try to find an existing one here. At984 // the same time we perform some general validation of the DAG we've got985 // going such as ensuring everything has a compatible panic strategy.986 let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime);987 for (_cnum, data) in self.iter_crate_data() {988 needs_panic_runtime |= data.needs_panic_runtime();989 }990991 // If we just don't need a panic runtime at all, then we're done here992 // and there's nothing else to do.993 if !needs_panic_runtime {994 return;995 }996997 // By this point we know that we need a panic runtime. Here we just load998 // an appropriate default runtime for our panic strategy.999 //1000 // We may resolve to an already loaded crate (as the crate may not have1001 // been explicitly linked prior to this), but this is fine.1002 //1003 // Also note that we have yet to perform validation of the crate graph1004 // in terms of everyone has a compatible panic runtime format, that's1005 // performed later as part of the `dependency_format` module.1006 let desired_strategy = tcx.sess.panic_strategy();1007 let name = match desired_strategy {1008 PanicStrategy::Unwind => sym::panic_unwind,1009 PanicStrategy::Abort => sym::panic_abort,1010 PanicStrategy::ImmediateAbort => {1011 // Immediate-aborting panics don't use a runtime.1012 return;1013 }1014 };1015 info!("panic runtime not found -- loading {}", name);10161017 // This has to be conditional as both panic_unwind and panic_abort may be present in the1018 // crate graph at the same time. One of them will later be activated in dependency_formats.1019 let Some(cnum) = self.resolve_crate(1020 tcx,1021 name,1022 DUMMY_SP,1023 CrateDepKind::Conditional,1024 CrateOrigin::Injected,1025 ) else {1026 return;1027 };1028 let cdata = self.get_crate_data(cnum);10291030 // Sanity check the loaded crate to ensure it is indeed a panic runtime1031 // and the panic strategy is indeed what we thought it was.1032 if !cdata.is_panic_runtime() {1033 tcx.dcx().emit_err(errors::CrateNotPanicRuntime { crate_name: name });1034 }1035 if cdata.required_panic_strategy() != Some(desired_strategy) {1036 tcx.dcx()1037 .emit_err(errors::NoPanicStrategy { crate_name: name, strategy: desired_strategy });1038 }10391040 self.injected_panic_runtime = Some(cnum);1041 }10421043 fn inject_profiler_runtime(&mut self, tcx: TyCtxt<'_>) {1044 let needs_profiler_runtime =1045 tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled();1046 if !needs_profiler_runtime || tcx.sess.opts.unstable_opts.no_profiler_runtime {1047 return;1048 }10491050 info!("loading profiler");10511052 // HACK: This uses conditional despite actually being unconditional to ensure that1053 // there is no error emitted when two dylibs independently depend on profiler_builtins.1054 // This is fine as profiler_builtins is always statically linked into the dylib just1055 // like compiler_builtins. Unlike compiler_builtins however there is no guaranteed1056 // common dylib that the duplicate crate check believes the crate to be included in.1057 // add_upstream_rust_crates has a corresponding check that forces profiler_builtins1058 // to be statically linked in even when marked as NotLinked.1059 let name = Symbol::intern(&tcx.sess.opts.unstable_opts.profiler_runtime);1060 let Some(cnum) = self.resolve_crate(1061 tcx,1062 name,1063 DUMMY_SP,1064 CrateDepKind::Conditional,1065 CrateOrigin::Injected,1066 ) else {1067 return;1068 };1069 let cdata = self.get_crate_data(cnum);10701071 // Sanity check the loaded crate to ensure it is indeed a profiler runtime1072 if !cdata.is_profiler_runtime() {1073 tcx.dcx().emit_err(errors::NotProfilerRuntime { crate_name: name });1074 }1075 }10761077 fn inject_allocator_crate(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1078 self.has_global_allocator =1079 match &*fn_spans(krate, Symbol::intern(&global_fn_name(sym::alloc))) {1080 [span1, span2, ..] => {1081 tcx.dcx()1082 .emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 });1083 true1084 }1085 spans => !spans.is_empty(),1086 };1087 let alloc_error_handler = Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER));1088 self.has_alloc_error_handler = match &*fn_spans(krate, alloc_error_handler) {1089 [span1, span2, ..] => {1090 tcx.dcx()1091 .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 });1092 true1093 }1094 spans => !spans.is_empty(),1095 };10961097 // Check to see if we actually need an allocator. This desire comes1098 // about through the `#![needs_allocator]` attribute and is typically1099 // written down in liballoc.1100 if !attr::contains_name(&krate.attrs, sym::needs_allocator)1101 && !self.iter_crate_data().any(|(_, data)| data.needs_allocator())1102 {1103 return;1104 }11051106 // At this point we've determined that we need an allocator. Let's see1107 // if our compilation session actually needs an allocator based on what1108 // we're emitting.1109 let all_rlib = tcx.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib));1110 if all_rlib {1111 return;1112 }11131114 // Ok, we need an allocator. Not only that but we're actually going to1115 // create an artifact that needs one linked in. Let's go find the one1116 // that we're going to link in.1117 //1118 // First up we check for global allocators. Look at the crate graph here1119 // and see what's a global allocator, including if we ourselves are a1120 // global allocator.1121 #[allow(rustc::symbol_intern_string_literal)]1122 let this_crate = Symbol::intern("this crate");11231124 let mut global_allocator = self.has_global_allocator.then_some(this_crate);1125 for (_, data) in self.iter_crate_data() {1126 if data.has_global_allocator() {1127 match global_allocator {1128 Some(other_crate) => {1129 tcx.dcx().emit_err(errors::ConflictingGlobalAlloc {1130 crate_name: data.name(),1131 other_crate_name: other_crate,1132 });1133 }1134 None => global_allocator = Some(data.name()),1135 }1136 }1137 }1138 let mut alloc_error_handler = self.has_alloc_error_handler.then_some(this_crate);1139 for (_, data) in self.iter_crate_data() {1140 if data.has_alloc_error_handler() {1141 match alloc_error_handler {1142 Some(other_crate) => {1143 tcx.dcx().emit_err(errors::ConflictingAllocErrorHandler {1144 crate_name: data.name(),1145 other_crate_name: other_crate,1146 });1147 }1148 None => alloc_error_handler = Some(data.name()),1149 }1150 }1151 }11521153 if global_allocator.is_some() {1154 self.allocator_kind = Some(AllocatorKind::Global);1155 } else {1156 // Ok we haven't found a global allocator but we still need an1157 // allocator. At this point our allocator request is typically fulfilled1158 // by the standard library, denoted by the `#![default_lib_allocator]`1159 // attribute.1160 if !attr::contains_name(&krate.attrs, sym::default_lib_allocator)1161 && !self.iter_crate_data().any(|(_, data)| data.has_default_lib_allocator())1162 {1163 tcx.dcx().emit_err(errors::GlobalAllocRequired);1164 }1165 self.allocator_kind = Some(AllocatorKind::Default);1166 }11671168 if alloc_error_handler.is_some() {1169 self.alloc_error_handler_kind = Some(AllocatorKind::Global);1170 } else {1171 // The alloc crate provides a default allocation error handler if1172 // one isn't specified.1173 self.alloc_error_handler_kind = Some(AllocatorKind::Default);1174 }1175 }11761177 fn inject_forced_externs(&mut self, tcx: TyCtxt<'_>) {1178 for (name, entry) in tcx.sess.opts.externs.iter() {1179 if entry.force {1180 let name_interned = Symbol::intern(name);1181 if !self.used_extern_options.contains(&name_interned) {1182 self.resolve_crate(1183 tcx,1184 name_interned,1185 DUMMY_SP,1186 CrateDepKind::Unconditional,1187 CrateOrigin::Extern,1188 );1189 }1190 }1191 }1192 }11931194 /// Inject the `compiler_builtins` crate if it is not already in the graph.1195 fn inject_compiler_builtins(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1196 // `compiler_builtins` does not get extern builtins, nor do `#![no_core]` crates1197 if attr::contains_name(&krate.attrs, sym::compiler_builtins)1198 || attr::contains_name(&krate.attrs, sym::no_core)1199 {1200 info!("`compiler_builtins` unneeded");1201 return;1202 }12031204 // If a `#![compiler_builtins]` crate already exists, avoid injecting it twice. This is1205 // the common case since usually it appears as a dependency of `std` or `alloc`.1206 for (cnum, cmeta) in self.iter_crate_data() {1207 if cmeta.is_compiler_builtins() {1208 info!("`compiler_builtins` already exists (cnum = {cnum}); skipping injection");1209 return;1210 }1211 }12121213 // `compiler_builtins` is not yet in the graph; inject it. Error on resolution failure.1214 let Some(cnum) = self.resolve_crate(1215 tcx,1216 sym::compiler_builtins,1217 krate.spans.inner_span.shrink_to_lo(),1218 CrateDepKind::Unconditional,1219 CrateOrigin::Injected,1220 ) else {1221 info!("`compiler_builtins` not resolved");1222 return;1223 };12241225 // Sanity check that the loaded crate is `#![compiler_builtins]`1226 let cdata = self.get_crate_data(cnum);1227 if !cdata.is_compiler_builtins() {1228 tcx.dcx().emit_err(errors::CrateNotCompilerBuiltins { crate_name: cdata.name() });1229 }1230 }12311232 fn report_unused_deps_in_crate(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1233 // Make a point span rather than covering the whole file1234 let span = krate.spans.inner_span.shrink_to_lo();1235 // Complain about anything left over1236 for (name, entry) in tcx.sess.opts.externs.iter() {1237 if let ExternLocation::FoundInLibrarySearchDirectories = entry.location {1238 // Don't worry about pathless `--extern foo` sysroot references1239 continue;1240 }1241 if entry.nounused_dep || entry.force {1242 // We're not worried about this one1243 continue;1244 }1245 let name_interned = Symbol::intern(name);1246 if self.used_extern_options.contains(&name_interned) {1247 continue;1248 }12491250 // Got a real unused --extern1251 if tcx.sess.opts.json_unused_externs.is_enabled() {1252 self.unused_externs.push(name_interned);1253 continue;1254 }12551256 tcx.sess.psess.buffer_lint(1257 lint::builtin::UNUSED_CRATE_DEPENDENCIES,1258 span,1259 ast::CRATE_NODE_ID,1260 errors::UnusedCrateDependency {1261 extern_crate: name_interned,1262 local_crate: tcx.crate_name(LOCAL_CRATE),1263 },1264 );1265 }1266 }12671268 fn report_future_incompatible_deps(&self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1269 let name = tcx.crate_name(LOCAL_CRATE);12701271 if name.as_str() == "wasm_bindgen" {1272 let major = env::var("CARGO_PKG_VERSION_MAJOR")1273 .ok()1274 .and_then(|major| u64::from_str(&major).ok());1275 let minor = env::var("CARGO_PKG_VERSION_MINOR")1276 .ok()1277 .and_then(|minor| u64::from_str(&minor).ok());1278 let patch = env::var("CARGO_PKG_VERSION_PATCH")1279 .ok()1280 .and_then(|patch| u64::from_str(&patch).ok());12811282 match (major, minor, patch) {1283 // v1 or bigger is valid.1284 (Some(1..), _, _) => return,1285 // v0.3 or bigger is valid.1286 (Some(0), Some(3..), _) => return,1287 // v0.2.88 or bigger is valid.1288 (Some(0), Some(2), Some(88..)) => return,1289 // Not using Cargo.1290 (None, None, None) => return,1291 _ => (),1292 }12931294 // Make a point span rather than covering the whole file1295 let span = krate.spans.inner_span.shrink_to_lo();12961297 tcx.sess.dcx().emit_err(errors::WasmCAbi { span });1298 }1299 }13001301 pub fn postprocess(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {1302 self.inject_compiler_builtins(tcx, krate);1303 self.inject_forced_externs(tcx);1304 self.inject_profiler_runtime(tcx);1305 self.inject_allocator_crate(tcx, krate);1306 self.inject_panic_runtime(tcx, krate);13071308 self.report_unused_deps_in_crate(tcx, krate);1309 self.report_future_incompatible_deps(tcx, krate);13101311 info!("{:?}", CrateDump(self));1312 }13131314 /// Process an `extern crate foo` AST node.1315 pub fn process_extern_crate(1316 &mut self,1317 tcx: TyCtxt<'_>,1318 item: &ast::Item,1319 def_id: LocalDefId,1320 definitions: &Definitions,1321 ) -> Option<CrateNum> {1322 match item.kind {1323 ast::ItemKind::ExternCrate(orig_name, ident) => {1324 debug!("resolving extern crate stmt. ident: {} orig_name: {:?}", ident, orig_name);1325 let name = match orig_name {1326 Some(orig_name) => {1327 validate_crate_name(tcx.sess, orig_name, Some(item.span));1328 orig_name1329 }1330 None => ident.name,1331 };1332 let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) {1333 CrateDepKind::MacrosOnly1334 } else {1335 CrateDepKind::Unconditional1336 };13371338 let cnum =1339 self.resolve_crate(tcx, name, item.span, dep_kind, CrateOrigin::Extern)?;13401341 let path_len = definitions.def_path(def_id).data.len();1342 self.update_extern_crate(1343 cnum,1344 name,1345 ExternCrate {1346 src: ExternCrateSource::Extern(def_id.to_def_id()),1347 span: item.span,1348 path_len,1349 dependency_of: LOCAL_CRATE,1350 },1351 );1352 Some(cnum)1353 }1354 _ => bug!(),1355 }1356 }13571358 pub fn process_path_extern(1359 &mut self,1360 tcx: TyCtxt<'_>,1361 name: Symbol,1362 span: Span,1363 ) -> Option<CrateNum> {1364 let cnum =1365 self.resolve_crate(tcx, name, span, CrateDepKind::Unconditional, CrateOrigin::Extern)?;13661367 self.update_extern_crate(1368 cnum,1369 name,1370 ExternCrate {1371 src: ExternCrateSource::Path,1372 span,1373 // to have the least priority in `update_extern_crate`1374 path_len: usize::MAX,1375 dependency_of: LOCAL_CRATE,1376 },1377 );13781379 Some(cnum)1380 }13811382 pub fn maybe_process_path_extern(&mut self, tcx: TyCtxt<'_>, name: Symbol) -> Option<CrateNum> {1383 self.maybe_resolve_crate(tcx, name, CrateDepKind::Unconditional, CrateOrigin::Extern).ok()1384 }1385}13861387fn fn_spans(krate: &ast::Crate, name: Symbol) -> Vec<Span> {1388 struct Finder {1389 name: Symbol,1390 spans: Vec<Span>,1391 }1392 impl<'ast> visit::Visitor<'ast> for Finder {1393 fn visit_item(&mut self, item: &'ast ast::Item) {1394 if let Some(ident) = item.kind.ident()1395 && ident.name == self.name1396 && attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol)1397 {1398 self.spans.push(item.span);1399 }1400 visit::walk_item(self, item)1401 }1402 }14031404 let mut f = Finder { name, spans: Vec::new() };1405 visit::walk_crate(&mut f, krate);1406 f.spans1407}14081409fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String {1410 e.sources().map(|e| format!(": {e}")).collect()1411}14121413fn attempt_load_dylib(path: &Path) -> Result<libloading::Library, libloading::Error> {1414 #[cfg(target_os = "aix")]1415 if let Some(ext) = path.extension()1416 && ext.eq("a")1417 {1418 // On AIX, we ship all libraries as .a big_af archive1419 // the expected format is lib<name>.a(libname.so) for the actual1420 // dynamic library1421 let library_name = path.file_stem().expect("expect a library name");1422 let mut archive_member = std::ffi::OsString::from("a(");1423 archive_member.push(library_name);1424 archive_member.push(".so)");1425 let new_path = path.with_extension(archive_member);14261427 // On AIX, we need RTLD_MEMBER to dlopen an archived shared1428 let flags = libc::RTLD_LAZY | libc::RTLD_LOCAL | libc::RTLD_MEMBER;1429 return unsafe { libloading::os::unix::Library::open(Some(&new_path), flags) }1430 .map(|lib| lib.into());1431 }14321433 unsafe { libloading::Library::new(&path) }1434}14351436// On Windows the compiler would sometimes intermittently fail to open the1437// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the1438// system still holds a lock on the file, so we retry a few times before calling it1439// an error.1440fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, String> {1441 assert!(max_attempts > 0);14421443 let mut last_error = None;14441445 for attempt in 0..max_attempts {1446 debug!("Attempt to load proc-macro `{}`.", path.display());1447 match attempt_load_dylib(path) {1448 Ok(lib) => {1449 if attempt > 0 {1450 debug!(1451 "Loaded proc-macro `{}` after {} attempts.",1452 path.display(),1453 attempt + 11454 );1455 }1456 return Ok(lib);1457 }1458 Err(err) => {1459 // Only try to recover from this specific error.1460 if !matches!(err, libloading::Error::LoadLibraryExW { .. }) {1461 debug!("Failed to load proc-macro `{}`. Not retrying", path.display());1462 let err = format_dlopen_err(&err);1463 // We include the path of the dylib in the error ourselves, so1464 // if it's in the error, we strip it.1465 if let Some(err) = err.strip_prefix(&format!(": {}", path.display())) {1466 return Err(err.to_string());1467 }1468 return Err(err);1469 }14701471 last_error = Some(err);1472 std::thread::sleep(Duration::from_millis(100));1473 debug!("Failed to load proc-macro `{}`. Retrying.", path.display());1474 }1475 }1476 }14771478 debug!("Failed to load proc-macro `{}` even after {} attempts.", path.display(), max_attempts);14791480 let last_error = last_error.unwrap();1481 let message = if let Some(src) = last_error.source() {1482 format!("{} ({src}) (retried {max_attempts} times)", format_dlopen_err(&last_error))1483 } else {1484 format!("{} (retried {max_attempts} times)", format_dlopen_err(&last_error))1485 };1486 Err(message)1487}14881489pub enum DylibError {1490 DlOpen(String, String),1491 DlSym(String, String),1492}14931494impl From<DylibError> for CrateError {1495 fn from(err: DylibError) -> CrateError {1496 match err {1497 DylibError::DlOpen(path, err) => CrateError::DlOpen(path, err),1498 DylibError::DlSym(path, err) => CrateError::DlSym(path, err),1499 }1500 }1501}15021503pub unsafe fn load_symbol_from_dylib<T: Copy>(1504 path: &Path,1505 sym_name: &str,1506) -> Result<T, DylibError> {1507 // Make sure the path contains a / or the linker will search for it.1508 let path = try_canonicalize(path).unwrap();1509 let lib =1510 load_dylib(&path, 5).map_err(|err| DylibError::DlOpen(path.display().to_string(), err))?;15111512 let sym = unsafe { lib.get::<T>(sym_name.as_bytes()) }1513 .map_err(|err| DylibError::DlSym(path.display().to_string(), format_dlopen_err(&err)))?;15141515 // Intentionally leak the dynamic library. We can't ever unload it1516 // since the library can make things that will live arbitrarily long.1517 let sym = unsafe { sym.into_raw() };1518 std::mem::forget(lib);15191520 Ok(*sym)1521}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.