Emit function declarations for functions with #[linkage="extern_weak"]
Currently, when declaring an extern weak function in Rust, we use the following syntax: ```rust unsafe extern "C" { #[linkage = "extern_weak"] static FOO: Option<unsafe extern "C" fn() -> ()>; } ``` This allows runtime-checking the extern weak symbol through the Option. When emitting LLVM-IR, the Rust compiler currently emits this static as an i8, and a pointer that is initialized with the value of the global i8 and represents the nullabilty e.g. ``` @FOO = extern_weak global i8 @_rust_extern_with_linkage_FOO = internal global ptr @FOO ``` This approach does not work well with CFI, where we need to attach CFI metadata to a concrete function declaration, which was pointed out in https://github.com/rust-lang/rust/issues/115199. This change switches to emitting a proper function declaration instead of a global i8. This allows CFI to work for extern_weak functions. We keep initializing the Rust internal symbol with the function declaration, which preserves the correct behavior for runtime checking the Option. Co-authored-by: Jakob Koschel <jakobkoschel@google.com>
This commit is contained in:
parent
705421b522
commit
b30cf11b96
2 changed files with 48 additions and 4 deletions
|
@ -5,6 +5,7 @@ use rustc_abi::{
|
|||
};
|
||||
use rustc_codegen_ssa::common;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
|
||||
|
@ -12,9 +13,9 @@ use rustc_middle::mir::interpret::{
|
|||
Allocation, ConstAllocation, ErrorHandled, InitChunk, Pointer, Scalar as InterpScalar,
|
||||
read_target_uint,
|
||||
};
|
||||
use rustc_middle::mir::mono::MonoItem;
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::mir::mono::{Linkage, MonoItem};
|
||||
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
|
||||
use rustc_middle::ty::{self, Instance};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
|
@ -171,8 +172,27 @@ fn check_and_apply_linkage<'ll, 'tcx>(
|
|||
if let Some(linkage) = attrs.import_linkage {
|
||||
debug!("get_static: sym={} linkage={:?}", sym, linkage);
|
||||
|
||||
// Declare a symbol `foo` with the desired linkage.
|
||||
let g1 = cx.declare_global(sym, cx.type_i8());
|
||||
// Declare a symbol `foo`. If `foo` is an extern_weak symbol, we declare
|
||||
// an extern_weak function, otherwise a global with the desired linkage.
|
||||
let g1 = if matches!(attrs.import_linkage, Some(Linkage::ExternalWeak)) {
|
||||
// An `extern_weak` function is represented as an `Option<unsafe extern ...>`,
|
||||
// we extract the function signature and declare it as an extern_weak function
|
||||
// instead of an extern_weak i8.
|
||||
let instance = Instance::mono(cx.tcx, def_id);
|
||||
if let ty::Adt(struct_def, args) = instance.ty(cx.tcx, cx.typing_env()).kind()
|
||||
&& cx.tcx.is_lang_item(struct_def.did(), LangItem::Option)
|
||||
&& let ty::FnPtr(sig, header) = args.type_at(0).kind()
|
||||
{
|
||||
let fn_sig = sig.with(*header);
|
||||
|
||||
let fn_abi = cx.fn_abi_of_fn_ptr(fn_sig, ty::List::empty());
|
||||
cx.declare_fn(sym, &fn_abi, None)
|
||||
} else {
|
||||
cx.declare_global(sym, cx.type_i8())
|
||||
}
|
||||
} else {
|
||||
cx.declare_global(sym, cx.type_i8())
|
||||
};
|
||||
llvm::set_linkage(g1, base::linkage_to_llvm(linkage));
|
||||
|
||||
// Declare an internal global `extern_with_linkage_foo` which
|
||||
|
|
24
tests/codegen/sanitizer/cfi/external_weak_symbols.rs
Normal file
24
tests/codegen/sanitizer/cfi/external_weak_symbols.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Verifies that type metadata identifiers for for weakly-linked symbols are
|
||||
// emitted correctly.
|
||||
//
|
||||
//@ needs-sanitizer-cfi
|
||||
//@ compile-flags: -Clinker-plugin-lto -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static
|
||||
#![crate_type = "bin"]
|
||||
#![feature(linkage)]
|
||||
|
||||
unsafe extern "C" {
|
||||
#[linkage = "extern_weak"]
|
||||
static FOO: Option<unsafe extern "C" fn(f64) -> ()>;
|
||||
}
|
||||
// CHECK: @_rust_extern_with_linkage_FOO = internal global ptr @FOO
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
if let Some(method) = FOO {
|
||||
method(4.2);
|
||||
// CHECK: call i1 @llvm.type.test(ptr {{%method|%0}}, metadata !"_ZTSFvdE")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK: declare !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} extern_weak void @FOO(double) unnamed_addr #{{[0-9]+}}
|
Loading…
Add table
Add a link
Reference in a new issue