Skip to content

Commit a5d38ed

Browse files
authored
Rollup merge of rust-lang#145724 - folkertdev:track-caller-drop-no-mangle, r=fee1-dead
the `#[track_caller]` shim should not inherit `#[no_mangle]` fixes rust-lang#143162 builds on rust-lang#143293 which introduced a mechanism to strip attributes from shims. cc `@Jules-Bertholet` `@workingjubilee` `@bjorn3` --- Summary: This PR fixes an interaction between `#[track_caller]`, `#[no_mangle]`, and casting to a function pointer. A function annotated with `#[track_caller]` internally has a hidden extra argument for the panic location. The `#[track_caller]` attribute is only allowed on `extern "Rust"` functions. When a function is annotated with both `#[no_mangle]` and `#[track_caller]`, the exported symbol has the signature that includes the extra panic location argument. This works on stable rust today: ```rust extern "Rust" { #[track_caller] fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static>; } mod provides { use std::panic::Location; #[track_caller] // UB if we did not have this! #[no_mangle] fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static> { Location::caller() } } ``` When a `#[track_caller]` function is converted to a function pointer, a shim is added to drop the additional argument. So this is a valid program: ```rust #[track_caller] fn foo() {} fn main() { let f = foo as fn(); f(); } ``` The issue arises when `foo` is additionally annotated with `#[no_mangle]`, the generated shim currently inherits this attribute, also exporting a symbol named `foo`, but one without the hidden panic location argument. The linker rightfully complains about a duplicate symbol. The solution of this PR is to have the generated shim drop the `#[no_mangle]` attribute.
2 parents b46db5c + 9db7781 commit a5d38ed

File tree

6 files changed

+69
-12
lines changed

6 files changed

+69
-12
lines changed

compiler/rustc_codegen_cranelift/src/abi/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
467467
true
468468
} else {
469469
instance.is_some_and(|inst| {
470-
fx.tcx.codegen_fn_attrs(inst.def_id()).flags.contains(CodegenFnAttrFlags::COLD)
470+
fx.tcx.codegen_instance_attrs(inst.def).flags.contains(CodegenFnAttrFlags::COLD)
471471
})
472472
};
473473
if is_cold {

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,11 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
200200
let fn_ty = bx.fn_decl_backend_type(fn_abi);
201201

202202
let fn_attrs = if bx.tcx().def_kind(fx.instance.def_id()).has_codegen_attrs() {
203-
Some(bx.tcx().codegen_fn_attrs(fx.instance.def_id()))
203+
Some(bx.tcx().codegen_instance_attrs(fx.instance.def))
204204
} else {
205205
None
206206
};
207+
let fn_attrs = fn_attrs.as_deref();
207208

208209
if !fn_abi.can_unwind {
209210
unwind = mir::UnwindAction::Unreachable;

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ impl<'tcx> TyCtxt<'tcx> {
1313
self,
1414
instance_kind: InstanceKind<'_>,
1515
) -> Cow<'tcx, CodegenFnAttrs> {
16+
// NOTE: we try to not clone the `CodegenFnAttrs` when that is not needed.
17+
// The `to_mut` method used below clones the inner value.
1618
let mut attrs = Cow::Borrowed(self.codegen_fn_attrs(instance_kind.def_id()));
1719

1820
// Drop the `#[naked]` attribute on non-item `InstanceKind`s, like the shims that
@@ -23,6 +25,28 @@ impl<'tcx> TyCtxt<'tcx> {
2325
}
2426
}
2527

28+
// A shim created by `#[track_caller]` should not inherit any attributes
29+
// that modify the symbol name. Failing to remove these attributes from
30+
// the shim leads to errors like `symbol `foo` is already defined`.
31+
//
32+
// A `ClosureOnceShim` with the track_caller attribute does not have a symbol,
33+
// and therefore can be skipped here.
34+
if let InstanceKind::ReifyShim(_, _) = instance_kind
35+
&& attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER)
36+
{
37+
if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
38+
attrs.to_mut().flags.remove(CodegenFnAttrFlags::NO_MANGLE);
39+
}
40+
41+
if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
42+
attrs.to_mut().flags.remove(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
43+
}
44+
45+
if attrs.symbol_name.is_some() {
46+
attrs.to_mut().symbol_name = None;
47+
}
48+
}
49+
2650
attrs
2751
}
2852
}

compiler/rustc_mir_transform/src/inline.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> {
245245
fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str) {
246246
let tcx = self.tcx();
247247
let InlineAttr::Force { attr_span, reason: justification } =
248-
tcx.codegen_fn_attrs(callsite.callee.def_id()).inline
248+
tcx.codegen_instance_attrs(callsite.callee.def).inline
249249
else {
250250
bug!("called on item without required inlining");
251251
};
@@ -603,7 +603,8 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
603603
let tcx = inliner.tcx();
604604
check_mir_is_available(inliner, caller_body, callsite.callee)?;
605605

606-
let callee_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id());
606+
let callee_attrs = tcx.codegen_instance_attrs(callsite.callee.def);
607+
let callee_attrs = callee_attrs.as_ref();
607608
check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?;
608609
check_codegen_attributes(inliner, callsite, callee_attrs)?;
609610
inliner.check_codegen_attributes_extra(callee_attrs)?;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@ run-pass
2+
#![feature(rustc_attrs)]
3+
4+
// The shim that is generated for a function annotated with `#[track_caller]` should not inherit
5+
// attributes that modify its symbol name. Failing to remove these attributes from the shim
6+
// leads to errors like `symbol `foo` is already defined`.
7+
//
8+
// See also https://github.com/rust-lang/rust/issues/143162.
9+
10+
#[unsafe(no_mangle)]
11+
#[track_caller]
12+
pub fn foo() {}
13+
14+
#[unsafe(export_name = "bar")]
15+
#[track_caller]
16+
pub fn bar() {}
17+
18+
#[rustc_std_internal_symbol]
19+
#[track_caller]
20+
pub fn baz() {}
21+
22+
fn main() {
23+
let _a = foo as fn();
24+
let _b = bar as fn();
25+
let _c = baz as fn();
26+
}

tests/ui/rfcs/rfc-2091-track-caller/track-caller-ffi.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
use std::panic::Location;
44

5-
extern "Rust" {
5+
unsafe extern "Rust" {
66
#[track_caller]
7-
fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static>;
8-
fn rust_track_caller_ffi_test_untracked() -> &'static Location<'static>;
7+
safe fn rust_track_caller_ffi_test_tracked() -> &'static Location<'static>;
8+
safe fn rust_track_caller_ffi_test_untracked() -> &'static Location<'static>;
99
}
1010

1111
fn rust_track_caller_ffi_test_nested_tracked() -> &'static Location<'static> {
12-
unsafe { rust_track_caller_ffi_test_tracked() }
12+
rust_track_caller_ffi_test_tracked()
1313
}
1414

1515
mod provides {
@@ -31,18 +31,23 @@ fn main() {
3131
assert_eq!(location.line(), 29);
3232
assert_eq!(location.column(), 20);
3333

34-
let tracked = unsafe { rust_track_caller_ffi_test_tracked() };
34+
let tracked = rust_track_caller_ffi_test_tracked();
3535
assert_eq!(tracked.file(), file!());
3636
assert_eq!(tracked.line(), 34);
37-
assert_eq!(tracked.column(), 28);
37+
assert_eq!(tracked.column(), 19);
3838

39-
let untracked = unsafe { rust_track_caller_ffi_test_untracked() };
39+
let untracked = rust_track_caller_ffi_test_untracked();
4040
assert_eq!(untracked.file(), file!());
4141
assert_eq!(untracked.line(), 24);
4242
assert_eq!(untracked.column(), 9);
4343

4444
let contained = rust_track_caller_ffi_test_nested_tracked();
4545
assert_eq!(contained.file(), file!());
4646
assert_eq!(contained.line(), 12);
47-
assert_eq!(contained.column(), 14);
47+
assert_eq!(contained.column(), 5);
48+
49+
let indirect = (rust_track_caller_ffi_test_tracked as fn() -> &'static Location<'static>)();
50+
assert_eq!(indirect.file(), file!());
51+
assert_eq!(indirect.line(), 7);
52+
assert_eq!(indirect.column(), 5);
4853
}

0 commit comments

Comments
 (0)