Skip to content

Commit

Permalink
Allow #[link(kind = "dylib")] without a name
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisDenton committed Oct 25, 2024
1 parent 1d4a767 commit a1b2f15
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 52 deletions.
3 changes: 1 addition & 2 deletions compiler/rustc_codegen_llvm/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
// MinGW: For backward compatibility we rely on the linker to decide whether it
// should use dllimport for functions.
if cx.use_dll_storage_attrs
&& let Some(library) = tcx.native_library(instance_def_id)
&& library.kind.is_dllimport()
&& tcx.is_dllimport(instance_def_id)
&& !matches!(tcx.sess.target.env.as_ref(), "gnu" | "uclibc")
{
llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport);
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_codegen_llvm/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,7 @@ impl<'ll> CodegenCx<'ll, '_> {
}
}

if self.use_dll_storage_attrs
&& let Some(library) = self.tcx.native_library(def_id)
&& library.kind.is_dllimport()
{
if self.use_dll_storage_attrs && self.tcx.is_dllimport(def_id) {
// For foreign (native) libs we know the exact storage type to use.
unsafe {
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ declare_features! (
(unstable, async_fn_track_caller, "1.73.0", Some(110011)),
/// Allows `for await` loops.
(unstable, async_for_loop, "1.77.0", Some(118898)),
/// Allows `#[link(kind = "dylib")]` without a library name.
(unstable, bare_link_kind, "CURRENT_RUSTC_VERSION", Some(132061)),
/// Allows using C-variadics.
(unstable, c_variadic, "1.34.0", Some(44930)),
/// Allows the use of `#[cfg(<true/false>)]`.
Expand Down
77 changes: 54 additions & 23 deletions compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
use rustc_session::Session;
use rustc_session::config::CrateType;
use rustc_session::cstore::{
DllCallingConvention, DllImport, ForeignModule, NativeLib, PeImportNameType,
DllCallingConvention, DllImport, ForeignModule, NativeLib, NativeLibs, PeImportNameType,
};
use rustc_session::parse::feature_err;
use rustc_session::search_paths::PathKind;
use rustc_session::utils::NativeLibKind;
use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::symbol::{Symbol, sym};
use rustc_span::symbol::{Symbol, kw, sym};
use rustc_target::spec::LinkSelfContainedComponents;
use rustc_target::spec::abi::Abi;

Expand Down Expand Up @@ -173,15 +173,15 @@ fn find_bundled_library(
None
}

pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> {
pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> NativeLibs {
let mut collector = Collector { tcx, libs: Vec::new() };
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
collector.libs.into()
}

pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
Expand All @@ -191,6 +191,20 @@ pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
}
}

pub(crate) fn find_by_id<'a, I: Iterator<Item = &'a NativeLib>>(
tcx: TyCtxt<'_>,
id: DefId,
iter: I,
) -> Option<&'a NativeLib> {
iter.filter(|lib| relevant_lib(tcx.sess, lib)).find(|lib| {
let Some(fm_id) = lib.foreign_module else {
return false;
};
let map = tcx.foreign_modules(id.krate);
map.get(&fm_id).expect("failed to find foreign module").foreign_items.contains(&id)
})
}

struct Collector<'tcx> {
tcx: TyCtxt<'tcx>,
libs: Vec<NativeLib>,
Expand Down Expand Up @@ -445,10 +459,6 @@ impl<'tcx> Collector<'tcx> {
if wasm_import_module.is_some() {
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
}
let Some((name, name_span)) = name else {
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
continue;
};

// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
if let Some((_, span)) = import_name_type {
Expand All @@ -457,8 +467,25 @@ impl<'tcx> Collector<'tcx> {
}
}

if kind != Some(NativeLibKind::RawDylib) {
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()
{
let link_ordinal_attr =
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
sess.dcx()
.emit_err(errors::LinkOrdinalRawDylib { span: link_ordinal_attr.span });
}
}
}

let dll_imports = match kind {
Some(NativeLibKind::RawDylib) => {
let Some((name, name_span)) = name else {
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
continue;
};
if name.as_str().contains('\0') {
sess.dcx().emit_err(errors::RawDylibNoNul { span: name_span });
}
Expand All @@ -473,25 +500,29 @@ impl<'tcx> Collector<'tcx> {
})
.collect()
}
_ => {
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()
{
let link_ordinal_attr =
self.tcx.get_attr(child_item, sym::link_ordinal).unwrap();
sess.dcx().emit_err(errors::LinkOrdinalRawDylib {
span: link_ordinal_attr.span,
});
}
}
_ => Vec::new(),
};

Vec::new()
}
// Allow kind of "dylib" or "static" without adding a native lib.
let name = if self.tcx.features().bare_link_kind()
&& matches!(kind, Some(NativeLibKind::Dylib { .. } | NativeLibKind::Static { .. }))
&& items.len() == 1
{
kw::Empty
} else {
let Some((name, _)) = name else {
sess.dcx().emit_err(errors::LinkRequiresName { span: m.span });
continue;
};
name
};

let kind = kind.unwrap_or(NativeLibKind::Unspecified);
let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx);
let filename = if !name.is_empty() {
find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx)
} else {
None
};
self.libs.push(NativeLib {
name,
filename,
Expand Down
27 changes: 14 additions & 13 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::util::Providers;
use rustc_session::cstore::{CrateStore, ExternCrate};
use rustc_session::utils::NativeLibKind;
use rustc_session::{Session, StableCrateId};
use rustc_span::Span;
use rustc_span::hygiene::ExpnId;
Expand Down Expand Up @@ -443,22 +444,22 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
alloc_error_handler_kind: |tcx, ()| CStore::from_tcx(tcx).alloc_error_handler_kind(),
is_private_dep: |_tcx, LocalCrate| false,
native_library: |tcx, id| {
tcx.native_libraries(id.krate)
.iter()
.filter(|lib| native_libs::relevant_lib(tcx.sess, lib))
.find(|lib| {
let Some(fm_id) = lib.foreign_module else {
return false;
};
let map = tcx.foreign_modules(id.krate);
map.get(&fm_id)
.expect("failed to find foreign module")
.foreign_items
.contains(&id)
})
native_libs::find_by_id(tcx, id, tcx.native_libraries(id.krate).iter())
},
native_libraries: native_libs::collect,
foreign_modules: foreign_modules::collect,
is_dllimport: |tcx, id| {
native_libs::find_by_id(tcx, id, tcx.native_libraries(id.krate).iter_all_items())
.map(|l| {
matches!(
l.kind,
NativeLibKind::Dylib { .. }
| NativeLibKind::RawDylib
| NativeLibKind::Unspecified
)
})
.unwrap_or(false)
},

// Returns a map from a sufficiently visible external item (i.e., an
// external item that is visible from at least one local module) to a
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1820,7 +1820,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
fn encode_native_libraries(&mut self) -> LazyArray<NativeLib> {
empty_proc_macro!(self);
let used_libraries = self.tcx.native_libraries(LOCAL_CRATE);
self.lazy_array(used_libraries.iter())
self.lazy_array(used_libraries.iter_all_items())
}

fn encode_foreign_modules(&mut self) -> LazyArray<ForeignModule> {
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use rustc_query_system::query::{QueryCache, QueryMode, QueryState, try_get_cache
use rustc_session::Limits;
use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
use rustc_session::cstore::{
CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib,
CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib, NativeLibs,
};
use rustc_session::lint::LintExpectationId;
use rustc_span::def_id::LOCAL_CRATE;
Expand Down Expand Up @@ -406,7 +406,7 @@ rustc_queries! {
/// These are assembled from the following places:
/// - `extern` blocks (depending on their `link` attributes)
/// - the `libs` (`-l`) option
query native_libraries(_: CrateNum) -> &'tcx Vec<NativeLib> {
query native_libraries(_: CrateNum) -> &'tcx NativeLibs {
arena_cache
desc { "looking up the native libraries of a linked crate" }
separate_provide_extern
Expand Down Expand Up @@ -1746,6 +1746,10 @@ rustc_queries! {
desc { |tcx| "getting the native library for `{}`", tcx.def_path_str(def_id) }
}

query is_dllimport(def_id: DefId) -> bool {
desc { |tcx| "determining dllimport status of `{}`", tcx.def_path_str(def_id) }
}

query inherit_sig_for_delegation_item(def_id: LocalDefId) -> &'tcx [Ty<'tcx>] {
desc { "inheriting delegation signature" }
}
Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_session/src/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,31 @@ impl NativeLib {
}
}

#[derive(Debug, HashStable_Generic)]
pub struct NativeLibs {
libs: Vec<NativeLib>,
}
impl NativeLibs {
pub fn iter(&self) -> impl Iterator<Item = &NativeLib> {
// Hide entries without a library name.
self.iter_all_items().filter(|l| !l.name.is_empty())
}

pub fn iter_all_items(&self) -> impl Iterator<Item = &NativeLib> {
self.libs.iter()
}
}
impl From<Vec<NativeLib>> for NativeLibs {
fn from(libs: Vec<NativeLib>) -> Self {
Self { libs }
}
}
impl FromIterator<NativeLib> for NativeLibs {
fn from_iter<T: IntoIterator<Item = NativeLib>>(iter: T) -> Self {
Self { libs: FromIterator::from_iter(iter) }
}
}

/// Different ways that the PE Format can decorate a symbol name.
/// From <https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-name-type>
#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic, PartialEq, Eq)]
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_session/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,6 @@ impl NativeLibKind {
pub fn is_statically_included(&self) -> bool {
matches!(self, NativeLibKind::Static { .. })
}

pub fn is_dllimport(&self) -> bool {
matches!(
self,
NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified
)
}
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ symbols! {
avx512f,
await_macro,
bang,
bare_link_kind,
begin_panic,
bench,
bin,
Expand Down
4 changes: 4 additions & 0 deletions tests/ui/extern/auxiliary/bare_link_kind_cdylib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#![crate_type = "cdylib"]

#[no_mangle]
pub static FOO: u32 = 0xFEDCBA98;
19 changes: 19 additions & 0 deletions tests/ui/extern/bare_link_kind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//@ run-pass
//@ aux-build:bare_link_kind_cdylib.rs

#![feature(bare_link_kind)]

#[link(kind = "dylib")]
extern "C" {
static FOO: u32;
}

#[cfg_attr(not(target_env = "msvc"), link(name = "bare_link_kind_cdylib", kind = "dylib"))]
#[cfg_attr(target_env = "msvc", link(name = "bare_link_kind_cdylib.dll", kind = "dylib"))]
extern "C" {}

fn main() {
unsafe {
assert_eq!(FOO, 0xFEDCBA98);
}
}
6 changes: 6 additions & 0 deletions tests/ui/feature-gates/feature-gate-bare-link-kind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[link(kind = "dylib")] //~ ERROR `#[link]` attribute requires a `name = "string"` argument
extern "C" {
static FOO: u32;
}

fn main() {}
9 changes: 9 additions & 0 deletions tests/ui/feature-gates/feature-gate-bare-link-kind.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0459]: `#[link]` attribute requires a `name = "string"` argument
--> $DIR/feature-gate-bare-link-kind.rs:1:1
|
LL | #[link(kind = "dylib")]
| ^^^^^^^^^^^^^^^^^^^^^^^ missing `name` argument

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0459`.

0 comments on commit a1b2f15

Please sign in to comment.