Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft] Raw_dylib codegen #71497

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions src/librustc_codegen_llvm/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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::<Vec<_>>();

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>,
Expand Down
8 changes: 8 additions & 0 deletions src/librustc_codegen_llvm/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
1 change: 1 addition & 0 deletions src/librustc_codegen_llvm/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_codegen_ssa/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}
}
}
Expand Down
57 changes: 56 additions & 1 deletion src/librustc_codegen_ssa/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,58 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
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 = {
Expand Down Expand Up @@ -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 {
Expand Down
19 changes: 19 additions & 0 deletions src/librustc_codegen_ssa/traits/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RawDylibImportName>,
}

pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send + Sync {
fn new_metadata(&self, sess: TyCtxt<'_>, mod_name: &str) -> Self::Module;
fn write_compressed_metadata<'tcx>(
Expand All @@ -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>,
Expand Down
1 change: 1 addition & 0 deletions src/librustc_codegen_ssa/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_middle/middle/codegen_fn_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize>,
pub link_ordinal: Option<u16>,
/// The `#[target_feature(enable = "...")]` attribute and the enabled
/// features (only enabled features are supported right now).
pub target_features: Vec<Symbol>,
Expand Down
8 changes: 4 additions & 4 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize> {
fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
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);
Expand All @@ -2669,13 +2669,13 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<usize> {
_ => 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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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