diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs index 3e17a51528e3e..f03eab857fe6b 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -23,6 +23,7 @@ use crate::llvm; use crate::metadata; use crate::value::Value; +use log::debug; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_codegen_ssa::traits::*; @@ -75,6 +76,187 @@ pub fn write_compressed_metadata<'tcx>( } } +fn new_global<'ll>( + name: &[&str], + llmod: &'ll llvm::Module, + llvalue: &'ll llvm::Value, + linkage: llvm::Linkage, + section: &str, +) -> &'ll llvm::Value { + let name = CString::new(name.join(".")).unwrap(); + let section = SmallCStr::new(section); + + unsafe { + let llglobal = llvm::LLVMAddGlobal(llmod, common::val_ty(llvalue), name.as_ptr()); + + llvm::LLVMSetInitializer(llglobal, llvalue); + llvm::LLVMRustSetLinkage(llglobal, linkage); + llvm::LLVMSetSection(llglobal, section.as_ptr()); + + llglobal + } +} + +unsafe fn get_rva<'ll>( + llctx: &'ll llvm::Context, + llptr: &'ll llvm::Value, + llbase: &'ll llvm::Value, +) -> &'ll llvm::Value { + let llrva_ty = llvm::LLVMInt32TypeInContext(llctx); + + let llbase_val = llvm::LLVMConstPtrToInt(llbase, llrva_ty); + let llptr_val = llvm::LLVMConstPtrToInt(llptr, llrva_ty); + + llvm::LLVMConstSub(llptr_val, llbase_val) +} + +pub fn write_idata_sections<'tcx>( + _tcx: TyCtxt<'tcx>, + raw_dylibs: &[RawDylibImports], + llvm_module: &mut ModuleLlvm, +) { + let (idata_llctx, idata_llmod) = (&*llvm_module.llcx, llvm_module.llmod()); + let llint32 = unsafe { llvm::LLVMInt32TypeInContext(idata_llctx) }; + let llbyte_ptr = unsafe { llvm::LLVMPointerType(llvm::LLVMInt8TypeInContext(idata_llctx), 0) }; + + // import directory table types + let lldir_ty = unsafe { + let lldir_ty_name = SmallCStr::new(".win32.image_import_desc"); + let lldir_ty = llvm::LLVMStructCreateNamed(idata_llctx, lldir_ty_name.as_ptr()); + llvm::LLVMStructSetBody( + lldir_ty, + [llint32, llint32, llint32, llint32, llint32].as_ptr(), + 5, + 0, + ); + + lldir_ty + }; + + // image base constant for computing RVAs + let image_base = unsafe { + let llname = SmallCStr::new("__ImageBase"); + let llty = llvm::LLVMInt8TypeInContext(idata_llctx); + + let llglobal = llvm::LLVMAddGlobal(idata_llmod, llty, llname.as_ptr()); + llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::ExternalLinkage); + + llglobal + }; + + let mut dir_entries = vec![]; + + for raw_dylib in raw_dylibs { + debug!("creating raw dylib idata secions - {:?}", raw_dylib); + + let name = CString::new(&*raw_dylib.name.as_str()).unwrap(); + let llname = common::bytes_in_context(idata_llctx, name.as_bytes()); + + let lldll_name = new_global( + &["import", &*raw_dylib.name.as_str(), "dll_name"], + idata_llmod, + llname, + llvm::Linkage::PrivateLinkage, + ".idata$7", + ); + + unsafe { + llvm::LLVMSetGlobalConstant(&lldll_name, 1); + + let mut lookup_table = raw_dylib + .items + .iter() + .map(|item| { + match item { + RawDylibImportName::Name(s) => { + let mut buf = vec![0, 0]; + buf.extend(s.as_str().as_bytes()); + + if buf.len() % 2 == 1 { + buf.push(0); + } + + let llname = common::bytes_in_context(idata_llctx, &buf); + + let llglobal = new_global( + &["import", &*raw_dylib.name.as_str(), "fn", &*s.as_str()], + idata_llmod, + llname, + llvm::Linkage::PrivateLinkage, + ".idata$6", + ); + + llvm::LLVMSetGlobalConstant(&llglobal, 1); + llvm::LLVMConstPointerCast(llglobal, llbyte_ptr) + } + RawDylibImportName::Ordinal(o) => { + //FIXME: support 32-bit targets + let o = *o as u64 | 0x8000_0000_0000_0000; + let llint64 = llvm::LLVMInt64TypeInContext(idata_llctx); + let llordinal = llvm::LLVMConstInt(llint64, o, 0); + + llvm::LLVMConstIntToPtr(llordinal, llbyte_ptr) + } + } + }) + .collect::>(); + + lookup_table.push(llvm::LLVMConstNull(llbyte_ptr)); + let lltable = + llvm::LLVMConstArray(llbyte_ptr, lookup_table.as_ptr(), lookup_table.len() as u32); + + //import lookup table + let ll_lookup_table = new_global( + &["import", &*raw_dylib.name.as_str(), "desc"], + idata_llmod, + lltable, + llvm::Linkage::PrivateLinkage, + ".idata$4", + ); + + //import address table - filled in at runtime + let ll_addr_table = new_global( + &["import", &*raw_dylib.name.as_str(), "ptr"], + idata_llmod, + lltable, + llvm::Linkage::PrivateLinkage, + ".idata$3", + ); + + let llzero = llvm::LLVMConstInt(llint32, 0, 0); + let lldir_entry = llvm::LLVMConstStructInContext( + idata_llctx, + [ + get_rva(idata_llctx, ll_lookup_table, image_base), + llzero, + llzero, + get_rva(idata_llctx, lldll_name, image_base), + get_rva(idata_llctx, ll_addr_table, image_base), + ] + .as_ptr(), + 5, + 0, + ); + + dir_entries.push(lldir_entry); + } + } + unsafe { + dir_entries.push(llvm::LLVMConstNull(lldir_ty)); + let lldir_table = + llvm::LLVMConstArray(lldir_ty, dir_entries.as_ptr(), dir_entries.len() as u32); + + let lldir_table = new_global( + &[".dllimport"], + idata_llmod, + lldir_table, + llvm::Linkage::ExternalLinkage, + ".idata$2", + ); + llvm::LLVMSetGlobalConstant(&lldir_table, 1); + } +} + pub struct ValueIter<'ll> { cur: Option<&'ll Value>, step: unsafe extern "C" fn(&'ll Value) -> Option<&'ll Value>, diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index 55ee660d9f700..66cc2cd56ff43 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -90,6 +90,14 @@ impl ExtraBackendMethods for LlvmCodegenBackend { ) { base::write_compressed_metadata(tcx, metadata, llvm_module) } + fn write_idata_sections<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + raw_dylibs: &[RawDylibImports], + module: &mut ModuleLlvm, + ) { + base::write_idata_sections(tcx, raw_dylibs, module) + } fn codegen_allocator<'tcx>( &self, tcx: TyCtxt<'tcx>, diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 3fb7ff3cb8dfd..58c6df1d3e9b9 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -848,6 +848,7 @@ extern "C" { pub fn LLVMConstVector(ScalarConstantVals: *const &Value, Size: c_uint) -> &Value; // Constant expressions + pub fn LLVMConstSub(LHS: &'a Value, RHS: &'a Value) -> &'a Value; pub fn LLVMConstInBoundsGEP( ConstantVal: &'a Value, ConstantIndices: *const &'a Value, diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 9a7c4907754b0..2eb62ee4edc96 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1703,8 +1703,7 @@ fn add_local_native_libraries( NativeLibKind::StaticNoBundle => cmd.link_staticlib(name), NativeLibKind::StaticBundle => cmd.link_whole_staticlib(name, &search_path), NativeLibKind::RawDylib => { - // FIXME(#58713): Proper handling for raw dylibs. - bug!("raw_dylib feature not yet implemented"); + // we shouldn't need to do anything here, code has been generated. } } } diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index 5b14258bd25be..d0837b3e3e612 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -598,6 +598,58 @@ pub fn codegen_crate( ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, metadata_module); } + // codegen idata sections for raw-dylib linking + let mut raw_dylib_imports = vec![]; + let native_libs = tcx.native_libraries(LOCAL_CRATE); + + for lib in native_libs.iter() { + if lib.kind == NativeLibKind::RawDylib { + if let (Some(dll_name), Some(def_id)) = (lib.name, lib.foreign_module) { + let foreign_modules = tcx.foreign_modules(LOCAL_CRATE); + for f_mod in foreign_modules { + if f_mod.def_id == def_id { + let items = f_mod + .foreign_items + .iter() + .map(|&def_id| { + let fn_attrs = tcx.codegen_fn_attrs(def_id); + if fn_attrs.link_name.is_some() { + RawDylibImportName::Name(fn_attrs.link_name.unwrap()) + } else if fn_attrs.link_ordinal.is_some() { + RawDylibImportName::Ordinal(fn_attrs.link_ordinal.unwrap()) + } else { + let name = tcx.item_name(def_id); + RawDylibImportName::Name(name) + } + }) + .collect(); + + raw_dylib_imports.push(RawDylibImports { name: dll_name, items }); + } + } + } else { + bug!("not enough information to link raw dylib!",); + } + } + } + + if !raw_dylib_imports.is_empty() { + let idata_cgu_name = + cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("idata")).to_string(); + let mut idata_module = backend.new_metadata(tcx, &idata_cgu_name); + + tcx.sess.time("write_idata_sections", || { + backend.write_idata_sections(tcx, &raw_dylib_imports, &mut idata_module); + }); + + let idata_module = ModuleCodegen { + name: idata_cgu_name, + module_llvm: idata_module, + kind: ModuleKind::Regular, + }; + ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, idata_module); + } + // We sort the codegen units by size. This way we can schedule work for LLVM // a bit more efficiently. let codegen_units = { @@ -896,7 +948,10 @@ pub fn provide_both(providers: &mut Providers<'_>) { .native_libraries(krate) .iter() .filter(|lib| { - if !matches!(lib.kind, NativeLibKind::Dylib | NativeLibKind::Unspecified) { + if !matches!( + lib.kind, + NativeLibKind::Dylib | NativeLibKind::RawDylib | NativeLibKind::Unspecified + ) { return false; } let cfg = match lib.cfg { diff --git a/src/librustc_codegen_ssa/traits/backend.rs b/src/librustc_codegen_ssa/traits/backend.rs index 6cbb47efa99f2..893d54abc41dd 100644 --- a/src/librustc_codegen_ssa/traits/backend.rs +++ b/src/librustc_codegen_ssa/traits/backend.rs @@ -89,6 +89,19 @@ pub trait CodegenBackend { ) -> Result<(), ErrorReported>; } +//FIXME: Put this somewhere else? +#[derive(Debug)] +pub enum RawDylibImportName { + Name(Symbol), + Ordinal(u16), +} + +#[derive(Debug)] +pub struct RawDylibImports { + pub name: Symbol, + pub items: Vec, +} + pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send + Sync { fn new_metadata(&self, sess: TyCtxt<'_>, mod_name: &str) -> Self::Module; fn write_compressed_metadata<'tcx>( @@ -97,6 +110,12 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se metadata: &EncodedMetadata, llvm_module: &mut Self::Module, ); + fn write_idata_sections<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + raw_dylib_imports: &[RawDylibImports], + mods: &mut Self::Module, + ); fn codegen_allocator<'tcx>( &self, tcx: TyCtxt<'tcx>, diff --git a/src/librustc_codegen_ssa/traits/mod.rs b/src/librustc_codegen_ssa/traits/mod.rs index 6b782731d535c..3e32aeaca7871 100644 --- a/src/librustc_codegen_ssa/traits/mod.rs +++ b/src/librustc_codegen_ssa/traits/mod.rs @@ -30,6 +30,7 @@ mod write; pub use self::abi::AbiBuilderMethods; pub use self::asm::{AsmBuilderMethods, AsmMethods, InlineAsmOperandRef}; pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods}; +pub use self::backend::{RawDylibImportName, RawDylibImports}; pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::consts::ConstMethods; pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoMethods}; diff --git a/src/librustc_middle/middle/codegen_fn_attrs.rs b/src/librustc_middle/middle/codegen_fn_attrs.rs index c480944069efb..794cec35a37c4 100644 --- a/src/librustc_middle/middle/codegen_fn_attrs.rs +++ b/src/librustc_middle/middle/codegen_fn_attrs.rs @@ -21,7 +21,7 @@ pub struct CodegenFnAttrs { /// imported function has in the dynamic library. Note that this must not /// be set when `link_name` is set. This is for foreign items with the /// "raw-dylib" kind. - pub link_ordinal: Option, + pub link_ordinal: Option, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). pub target_features: Vec, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 355b4fc413f42..4ccb95d7668d5 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -2660,7 +2660,7 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { false } -fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { +fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { use rustc_ast::ast::{Lit, LitIntType, LitKind}; let meta_item_list = attr.meta_item_list(); let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref); @@ -2669,13 +2669,13 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { _ => None, }; if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list { - if *ordinal <= usize::MAX as u128 { - Some(*ordinal as usize) + if *ordinal <= u16::MAX as u128 { + Some(*ordinal as u16) } else { let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal); tcx.sess .struct_span_err(attr.span, &msg) - .note("the value may not exceed `usize::MAX`") + .note("the value may not exceed `u16::MAX`") .emit(); None } diff --git a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr index 35f9b53fdf720..85da1d243dffb 100644 --- a/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr +++ b/src/test/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr @@ -13,7 +13,7 @@ error: ordinal value in `link_ordinal` is too large: `18446744073709551616` LL | #[link_ordinal(18446744073709551616)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: the value may not exceed `usize::MAX` + = note: the value may not exceed `u16::MAX` error: aborting due to previous error; 1 warning emitted