rust/compiler/rustc_metadata/src/native_libs.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

537 lines
22 KiB
Rust
Raw Normal View History

use rustc_ast::{NestedMetaItem, CRATE_NODE_ID};
use rustc_attr as attr;
2019-12-24 04:02:53 +00:00
use rustc_data_structures::fx::FxHashSet;
2023-07-15 10:17:37 +00:00
use rustc_middle::query::LocalCrate;
use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
use rustc_session::config::CrateType;
2023-07-15 10:17:37 +00:00
use rustc_session::cstore::{
DllCallingConvention, DllImport, ForeignModule, NativeLib, PeImportNameType,
};
use rustc_session::parse::feature_err;
use rustc_session::search_paths::PathKind;
use rustc_session::utils::NativeLibKind;
use rustc_session::Session;
2023-07-15 10:17:37 +00:00
use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;
use crate::errors;
use std::path::PathBuf;
pub fn find_native_static_library(
name: &str,
verbatim: bool,
search_paths: &[PathBuf],
sess: &Session,
) -> PathBuf {
let formats = if verbatim {
vec![("".into(), "".into())]
} else {
let os = (sess.target.staticlib_prefix.clone(), sess.target.staticlib_suffix.clone());
// On Windows, static libraries sometimes show up as libfoo.a and other
// times show up as foo.lib
let unix = ("lib".into(), ".a".into());
if os == unix { vec![os] } else { vec![os, unix] }
};
for path in search_paths {
for (prefix, suffix) in &formats {
let test = path.join(format!("{prefix}{name}{suffix}"));
if test.exists() {
return test;
}
}
}
sess.emit_fatal(errors::MissingNativeLibrary::new(name, verbatim));
}
fn find_bundled_library(
name: Symbol,
verbatim: Option<bool>,
kind: NativeLibKind,
has_cfg: bool,
sess: &Session,
) -> Option<Symbol> {
if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind
&& sess.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib))
&& (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
{
let verbatim = verbatim.unwrap_or(false);
let search_paths = &sess.target_filesearch(PathKind::Native).search_path_dirs();
2023-03-16 23:59:08 +00:00
return find_native_static_library(name.as_str(), verbatim, search_paths, sess)
.file_name()
.and_then(|s| s.to_str())
.map(Symbol::intern);
}
None
}
2023-07-15 10:17:37 +00:00
pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
let mut collector = Collector { tcx, libs: Vec::new() };
2023-07-15 10:17:37 +00:00
if tcx.sess.opts.unstable_opts.link_directives {
for module in tcx.foreign_modules(LOCAL_CRATE).values() {
collector.process_module(module);
}
}
collector.process_command_line();
collector.libs
}
pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
match lib.cfg {
Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, CRATE_NODE_ID, None),
None => true,
}
}
struct Collector<'tcx> {
2019-06-13 21:48:52 +00:00
tcx: TyCtxt<'tcx>,
libs: Vec<NativeLib>,
}
impl<'tcx> Collector<'tcx> {
2023-07-15 10:17:37 +00:00
fn process_module(&mut self, module: &ForeignModule) {
let ForeignModule { def_id, abi, ref foreign_items } = *module;
let def_id = def_id.expect_local();
2023-07-15 10:17:37 +00:00
let sess = self.tcx.sess;
if matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic) {
return;
}
// Process all of the #[link(..)]-style arguments
let features = self.tcx.features();
2023-07-15 10:17:37 +00:00
for m in self.tcx.get_attrs(def_id, sym::link) {
2022-02-18 23:48:49 +00:00
let Some(items) = m.meta_item_list() else {
continue;
};
let mut name = None;
let mut kind = None;
let mut modifiers = None;
let mut cfg = None;
let mut wasm_import_module = None;
2022-07-12 20:52:35 +00:00
let mut import_name_type = None;
for item in items.iter() {
match item.name_or_empty() {
sym::name => {
if name.is_some() {
sess.emit_err(errors::MultipleNamesInLink { span: item.span() });
continue;
}
let Some(link_name) = item.value_str() else {
sess.emit_err(errors::LinkNameForm { span: item.span() });
continue;
};
let span = item.name_value_literal_span().unwrap();
if link_name.is_empty() {
sess.emit_err(errors::EmptyLinkName { span });
}
name = Some((link_name, span));
}
sym::kind => {
if kind.is_some() {
sess.emit_err(errors::MultipleKindsInLink { span: item.span() });
continue;
}
let Some(link_kind) = item.value_str() else {
sess.emit_err(errors::LinkKindForm { span: item.span() });
continue;
};
let span = item.name_value_literal_span().unwrap();
let link_kind = match link_kind.as_str() {
"static" => NativeLibKind::Static { bundle: None, whole_archive: None },
"dylib" => NativeLibKind::Dylib { as_needed: None },
"framework" => {
if !sess.target.is_like_osx {
sess.emit_err(errors::LinkFrameworkApple { span });
}
NativeLibKind::Framework { as_needed: None }
}
"raw-dylib" => {
if !sess.target.is_like_windows {
sess.emit_err(errors::FrameworkOnlyWindows { span });
}
NativeLibKind::RawDylib
}
kind => {
sess.emit_err(errors::UnknownLinkKind { span, kind });
continue;
}
};
kind = Some(link_kind);
}
sym::modifiers => {
if modifiers.is_some() {
sess.emit_err(errors::MultipleLinkModifiers { span: item.span() });
continue;
}
let Some(link_modifiers) = item.value_str() else {
sess.emit_err(errors::LinkModifiersForm { span: item.span() });
continue;
};
modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
}
sym::cfg => {
if cfg.is_some() {
sess.emit_err(errors::MultipleCfgs { span: item.span() });
continue;
}
let Some(link_cfg) = item.meta_item_list() else {
sess.emit_err(errors::LinkCfgForm { span: item.span() });
continue;
};
let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
sess.emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
continue;
};
if !features.link_cfg {
feature_err(
&sess.parse_sess,
sym::link_cfg,
item.span(),
"link cfg is unstable",
)
.emit();
}
cfg = Some(link_cfg.clone());
}
sym::wasm_import_module => {
if wasm_import_module.is_some() {
sess.emit_err(errors::MultipleWasmImport { span: item.span() });
continue;
}
let Some(link_wasm_import_module) = item.value_str() else {
sess.emit_err(errors::WasmImportForm { span: item.span() });
continue;
};
wasm_import_module = Some((link_wasm_import_module, item.span()));
}
2022-07-12 20:52:35 +00:00
sym::import_name_type => {
if import_name_type.is_some() {
sess.emit_err(errors::MultipleImportNameType { span: item.span() });
2022-07-12 20:52:35 +00:00
continue;
}
let Some(link_import_name_type) = item.value_str() else {
sess.emit_err(errors::ImportNameTypeForm { span: item.span() });
2022-07-12 20:52:35 +00:00
continue;
};
if self.tcx.sess.target.arch != "x86" {
sess.emit_err(errors::ImportNameTypeX86 { span: item.span() });
2022-07-12 20:52:35 +00:00
continue;
}
let link_import_name_type = match link_import_name_type.as_str() {
"decorated" => PeImportNameType::Decorated,
"noprefix" => PeImportNameType::NoPrefix,
"undecorated" => PeImportNameType::Undecorated,
import_name_type => {
sess.emit_err(errors::UnknownImportNameType {
span: item.span(),
import_name_type,
});
2022-07-12 20:52:35 +00:00
continue;
}
};
import_name_type = Some((link_import_name_type, item.span()));
}
_ => {
sess.emit_err(errors::UnexpectedLinkArg { span: item.span() });
}
}
}
// Do this outside the above loop so we don't depend on modifiers coming after kinds
let mut verbatim = None;
if let Some((modifiers, span)) = modifiers {
for modifier in modifiers.as_str().split(',') {
let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
Some(m) => (m, modifier.starts_with('+')),
None => {
sess.emit_err(errors::InvalidLinkModifier { span });
continue;
}
};
macro report_unstable_modifier($feature: ident) {
if !features.$feature {
feature_err(
&sess.parse_sess,
sym::$feature,
span,
format!("linking modifier `{modifier}` is unstable"),
)
.emit();
}
}
let assign_modifier = |dst: &mut Option<bool>| {
if dst.is_some() {
sess.emit_err(errors::MultipleModifiers { span, modifier });
} else {
*dst = Some(value);
}
};
match (modifier, &mut kind) {
("bundle", Some(NativeLibKind::Static { bundle, .. })) => {
assign_modifier(bundle)
}
("bundle", _) => {
sess.emit_err(errors::BundleNeedsStatic { span });
}
("verbatim", _) => assign_modifier(&mut verbatim),
("whole-archive", Some(NativeLibKind::Static { whole_archive, .. })) => {
assign_modifier(whole_archive)
}
("whole-archive", _) => {
sess.emit_err(errors::WholeArchiveNeedsStatic { span });
}
("as-needed", Some(NativeLibKind::Dylib { as_needed }))
| ("as-needed", Some(NativeLibKind::Framework { as_needed })) => {
report_unstable_modifier!(native_link_modifiers_as_needed);
assign_modifier(as_needed)
}
("as-needed", _) => {
sess.emit_err(errors::AsNeededCompatibility { span });
}
_ => {
sess.emit_err(errors::UnknownLinkModifier { span, modifier });
}
}
}
}
if let Some((_, span)) = wasm_import_module {
if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
sess.emit_err(errors::IncompatibleWasmLink { span });
}
}
if wasm_import_module.is_some() {
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
}
let Some((name, name_span)) = name else {
sess.emit_err(errors::LinkRequiresName { span: m.span });
continue;
};
2022-07-12 20:52:35 +00:00
// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
if let Some((_, span)) = import_name_type {
if kind != Some(NativeLibKind::RawDylib) {
sess.emit_err(errors::ImportNameTypeRaw { span });
2022-07-12 20:52:35 +00:00
}
}
let dll_imports = match kind {
Some(NativeLibKind::RawDylib) => {
if name.as_str().contains('\0') {
sess.emit_err(errors::RawDylibNoNul { span: name_span });
}
2023-07-15 10:17:37 +00:00
foreign_items
.iter()
2023-07-15 10:17:37 +00:00
.map(|&child_item| {
2022-07-12 20:52:35 +00:00
self.build_dll_import(
abi,
import_name_type.map(|(import_name_type, _)| import_name_type),
child_item,
)
})
.collect()
}
_ => {
2023-07-15 10:17:37 +00:00
for &child_item in foreign_items {
if self.tcx.def_kind(child_item).has_codegen_attrs()
&& self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some()
{
2023-07-15 10:17:37 +00:00
let link_ordinal_attr =
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
sess.emit_err(errors::LinkOrdinalRawDylib {
span: link_ordinal_attr.span,
});
}
}
Vec::new()
}
};
let kind = kind.unwrap_or(NativeLibKind::Unspecified);
let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), sess);
self.libs.push(NativeLib {
name,
filename,
kind,
cfg,
2023-07-15 10:17:37 +00:00
foreign_module: Some(def_id.to_def_id()),
verbatim,
dll_imports,
});
2019-08-27 14:42:44 +00:00
}
}
// Process libs passed on the command line
fn process_command_line(&mut self) {
// First, check for errors
let mut renames = FxHashSet::default();
for lib in &self.tcx.sess.opts.libs {
if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
// Cannot check this when parsing options because the target is not yet available.
self.tcx.sess.emit_err(errors::LibFrameworkApple);
}
if let Some(ref new_name) = lib.new_name {
let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
if new_name.is_empty() {
self.tcx.sess.emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
} else if !any_duplicate {
self.tcx.sess.emit_err(errors::RenamingNoLink { lib_name: &lib.name });
} else if !renames.insert(&lib.name) {
self.tcx.sess.emit_err(errors::MultipleRenamings { lib_name: &lib.name });
}
}
}
2018-08-19 13:30:23 +00:00
// Update kind and, optionally, the name of all native libraries
// (there may be more than one) with the specified name. If any
// library is mentioned more than once, keep the latest mention
// of it, so that any possible dependent libraries appear before
// it. (This ensures that the linker is able to see symbols from
// all possible dependent libraries before linking in the library
// in question.)
for passed_lib in &self.tcx.sess.opts.libs {
// If we've already added any native libraries with the same
2018-12-20 21:19:55 +00:00
// name, they will be pulled out into `existing`, so that we
// can move them to the end of the list below.
let mut existing = self
.libs
.extract_if(|lib| {
if lib.name.as_str() == passed_lib.name {
// FIXME: This whole logic is questionable, whether modifiers are
// involved or not, library reordering and kind overriding without
// explicit `:rename` in particular.
if lib.has_modifiers() || passed_lib.has_modifiers() {
match lib.foreign_module {
Some(def_id) => self.tcx.sess.emit_err(errors::NoLinkModOverride {
span: Some(self.tcx.def_span(def_id)),
}),
None => {
self.tcx.sess.emit_err(errors::NoLinkModOverride { span: None })
}
};
}
if passed_lib.kind != NativeLibKind::Unspecified {
lib.kind = passed_lib.kind;
}
if let Some(new_name) = &passed_lib.new_name {
lib.name = Symbol::intern(new_name);
}
lib.verbatim = passed_lib.verbatim;
return true;
}
false
})
.collect::<Vec<_>>();
if existing.is_empty() {
// Add if not found
2021-10-07 21:18:39 +00:00
let new_name: Option<&str> = passed_lib.new_name.as_deref();
let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
let sess = self.tcx.sess;
let filename =
find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, false, sess);
self.libs.push(NativeLib {
name,
filename,
kind: passed_lib.kind,
cfg: None,
rustc: Add a `#[wasm_import_module]` attribute This commit adds a new attribute to the Rust compiler specific to the wasm target (and no other targets). The `#[wasm_import_module]` attribute is used to specify the module that a name is imported from, and is used like so: #[wasm_import_module = "./foo.js"] extern { fn some_js_function(); } Here the import of the symbol `some_js_function` is tagged with the `./foo.js` module in the wasm output file. Wasm-the-format includes two fields on all imports, a module and a field. The field is the symbol name (`some_js_function` above) and the module has historically unconditionally been `"env"`. I'm not sure if this `"env"` convention has asm.js or LLVM roots, but regardless we'd like the ability to configure it! The proposed ES module integration with wasm (aka a wasm module is "just another ES module") requires that the import module of wasm imports is interpreted as an ES module import, meaning that you'll need to encode paths, NPM packages, etc. As a result, we'll need this to be something other than `"env"`! Unfortunately neither our version of LLVM nor LLD supports custom import modules (aka anything not `"env"`). My hope is that by the time LLVM 7 is released both will have support, but in the meantime this commit adds some primitive encoding/decoding of wasm files to the compiler. This way rustc postprocesses the wasm module that LLVM emits to ensure it's got all the imports we'd like to have in it. Eventually I'd ideally like to unconditionally require this attribute to be placed on all `extern { ... }` blocks. For now though it seemed prudent to add it as an unstable attribute, so for now it's not required (as that'd force usage of a feature gate). Hopefully it doesn't take too long to "stabilize" this! cc rust-lang-nursery/rust-wasm#29
2018-02-10 22:28:17 +00:00
foreign_module: None,
verbatim: passed_lib.verbatim,
dll_imports: Vec::new(),
});
} else {
// Move all existing libraries with the same name to the
// end of the command line.
self.libs.append(&mut existing);
}
}
}
2023-07-15 10:17:37 +00:00
fn i686_arg_list_size(&self, item: DefId) -> usize {
let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions(
self.tcx
2023-07-15 10:17:37 +00:00
.type_of(item)
.instantiate_identity()
.fn_sig(self.tcx)
.inputs()
Rename many interner functions. (This is a large commit. The changes to `compiler/rustc_middle/src/ty/context.rs` are the most important ones.) The current naming scheme is a mess, with a mix of `_intern_`, `intern_` and `mk_` prefixes, with little consistency. In particular, in many cases it's easy to use an iterator interner when a (preferable) slice interner is available. The guiding principles of the new naming system: - No `_intern_` prefixes. - The `intern_` prefix is for internal operations. - The `mk_` prefix is for external operations. - For cases where there is a slice interner and an iterator interner, the former is `mk_foo` and the latter is `mk_foo_from_iter`. Also, `slice_interners!` and `direct_interners!` can now be `pub` or non-`pub`, which helps enforce the internal/external operations division. It's not perfect, but I think it's a clear improvement. The following lists show everything that was renamed. slice_interners - const_list - mk_const_list -> mk_const_list_from_iter - intern_const_list -> mk_const_list - substs - mk_substs -> mk_substs_from_iter - intern_substs -> mk_substs - check_substs -> check_and_mk_substs (this is a weird one) - canonical_var_infos - intern_canonical_var_infos -> mk_canonical_var_infos - poly_existential_predicates - mk_poly_existential_predicates -> mk_poly_existential_predicates_from_iter - intern_poly_existential_predicates -> mk_poly_existential_predicates - _intern_poly_existential_predicates -> intern_poly_existential_predicates - predicates - mk_predicates -> mk_predicates_from_iter - intern_predicates -> mk_predicates - _intern_predicates -> intern_predicates - projs - intern_projs -> mk_projs - place_elems - mk_place_elems -> mk_place_elems_from_iter - intern_place_elems -> mk_place_elems - bound_variable_kinds - mk_bound_variable_kinds -> mk_bound_variable_kinds_from_iter - intern_bound_variable_kinds -> mk_bound_variable_kinds direct_interners - region - intern_region (unchanged) - const - mk_const_internal -> intern_const - const_allocation - intern_const_alloc -> mk_const_alloc - layout - intern_layout -> mk_layout - adt_def - intern_adt_def -> mk_adt_def_from_data (unusual case, hard to avoid) - alloc_adt_def(!) -> mk_adt_def - external_constraints - intern_external_constraints -> mk_external_constraints Other - type_list - mk_type_list -> mk_type_list_from_iter - intern_type_list -> mk_type_list - tup - mk_tup -> mk_tup_from_iter - intern_tup -> mk_tup
2023-02-17 03:33:08 +00:00
.map_bound(|slice| self.tcx.mk_type_list(slice)),
);
argument_types
.iter()
.map(|ty| {
let layout = self
.tcx
.layout_of(ParamEnvAnd { param_env: ParamEnv::empty(), value: ty })
.expect("layout")
.layout;
// In both stdcall and fastcall, we always round up the argument size to the
// nearest multiple of 4 bytes.
(layout.size().bytes_usize() + 3) & !3
})
.sum()
}
2022-07-12 20:52:35 +00:00
fn build_dll_import(
&self,
abi: Abi,
import_name_type: Option<PeImportNameType>,
2023-07-15 10:17:37 +00:00
item: DefId,
2022-07-12 20:52:35 +00:00
) -> DllImport {
2023-07-15 10:17:37 +00:00
let span = self.tcx.def_span(item);
let calling_convention = if self.tcx.sess.target.arch == "x86" {
match abi {
Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C,
Abi::Stdcall { .. } | Abi::System { .. } => {
DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
}
Abi::Fastcall { .. } => {
DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
}
2022-07-19 17:40:26 +00:00
Abi::Vectorcall { .. } => {
DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
}
_ => {
2023-07-15 10:17:37 +00:00
self.tcx.sess.emit_fatal(errors::UnsupportedAbiI686 { span });
}
}
} else {
match abi {
Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
_ => {
2023-07-15 10:17:37 +00:00
self.tcx.sess.emit_fatal(errors::UnsupportedAbi { span });
}
}
};
2023-07-15 10:17:37 +00:00
let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item);
2022-09-12 21:03:19 +00:00
let import_name_type = codegen_fn_attrs
2022-07-12 20:52:35 +00:00
.link_ordinal
.map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord)));
DllImport {
2023-07-15 10:17:37 +00:00
name: codegen_fn_attrs.link_name.unwrap_or(self.tcx.item_name(item)),
2022-07-12 20:52:35 +00:00
import_name_type,
calling_convention,
2023-07-15 10:17:37 +00:00
span,
is_fn: self.tcx.def_kind(item).is_fn_like(),
}
}
}