Skip to content

Commit a074676

Browse files
committed
Create personality stub function for no_std panic=abort crates
**This is a very WIP state. It has no tests, no platform gates, and probably many bugs.** Every crate that may unwind (being compiled with panic=unwind) needs a reference to a personality function in its object code. The personality function is defined in `std`. For crates downstream of `std` (with `std` in the cstore), they will resolve to the personality lang item of std and reference that function from there (using its symbol_name `rust_eh_personality`). For `#![no_std]` crates, they will also reference the `rust_eh_personality` symbol (directly, not via weak lang item machinery). But when `std` is never linked into a crate graph containing panic=unwind crates (which happens on all panic=unwind default targets because of core), the symbol isn't present and we get a linker error. This PR solves this problem by injecting a stub for `rust_eh_personality` into the final link of binaries where the previous conditions were fulfilled. This is implemented in a way that's very similar to the allocator shim. Because we don't want to insta-stabilize this functionality, the stub doesn't just abort, it will first do a volatile read of the `__rust_personality_stub_is_unstable` symbol (once again similar to the allocator) to ensure that this functionality cannot be relied on without providing this symbol. Example code that now works on x86_64-unknown-linux-gnu: ```rust fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { loop {} } unsafe extern "C" { fn write(fd: i32, buf: *const u8, count: usize); } static __rust_personality_stub_is_unstable: u8 = 0; fn main() -> i32 { // bring in some code that requires the personality function [1].sort_unstable(); let msg = b"meow\n"; unsafe { write(1, msg.as_ptr(), msg.len()); } 0 } ``` When compiled with `-Cpanic=abort`, this would previously result in a linker error because the `rust_eh_personality` symbol was not found. Now, it compiles and runs successfully.
1 parent 8c39296 commit a074676

File tree

8 files changed

+201
-0
lines changed

8 files changed

+201
-0
lines changed

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
687687
get_fn(self, instance)
688688
}
689689

690+
/// Get a function pointer for the exception handling personality function.
690691
fn eh_personality(&self) -> &'ll Value {
691692
// The exception handling personality function.
692693
//

compiler/rustc_codegen_llvm/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ mod intrinsic;
7777
pub mod llvm;
7878
mod llvm_util;
7979
mod mono_item;
80+
mod personality_stub;
8081
mod type_;
8182
mod type_of;
8283
mod va_arg;
@@ -122,6 +123,15 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
122123
}
123124
module_llvm
124125
}
126+
fn codegen_personality_stub<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str) -> Self::Module {
127+
let mut module_llvm = ModuleLlvm::new_metadata(tcx, module_name);
128+
129+
unsafe {
130+
personality_stub::codegen(tcx, &mut module_llvm, module_name);
131+
}
132+
133+
module_llvm
134+
}
125135
fn compile_codegen_unit(
126136
&self,
127137
tcx: TyCtxt<'_>,
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use rustc_middle::ty::TyCtxt;
2+
use rustc_session::config::DebugInfo;
3+
4+
use crate::common::AsCCharPtr;
5+
use crate::llvm::{self, Context, False, Module};
6+
use crate::{ModuleLlvm, attributes, debuginfo};
7+
8+
const FEATURE_GATE_SYMBOL: &str = "__rust_personality_stub_is_unstable";
9+
10+
pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, module_llvm: &mut ModuleLlvm, module_name: &str) {
11+
let llcx = &*module_llvm.llcx;
12+
let llmod = module_llvm.llmod();
13+
14+
// rust alloc error handler
15+
create_stub_personality(tcx, llcx, llmod);
16+
17+
if tcx.sess.opts.debuginfo != DebugInfo::None {
18+
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod);
19+
debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);
20+
dbg_cx.finalize(tcx.sess);
21+
}
22+
}
23+
24+
fn create_stub_personality(tcx: TyCtxt<'_>, llcx: &Context, llmod: &Module) {
25+
unsafe {
26+
let llfn = declare_stub_personality(tcx, llcx, llmod);
27+
28+
let i8_ty = llvm::LLVMInt8TypeInContext(llcx);
29+
30+
let feature_gate = llvm::LLVMRustGetOrInsertGlobal(
31+
llmod,
32+
FEATURE_GATE_SYMBOL.as_c_char_ptr(),
33+
FEATURE_GATE_SYMBOL.len(),
34+
i8_ty,
35+
);
36+
37+
let (trap_fn, trap_ty) = trap_intrinsic(llcx, llmod);
38+
39+
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr());
40+
let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
41+
llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
42+
43+
let load = llvm::LLVMBuildLoad2(llbuilder, i8_ty, feature_gate, c"".as_ptr());
44+
llvm::LLVMSetVolatile(load, llvm::True);
45+
46+
llvm::LLVMBuildCallWithOperandBundles(
47+
llbuilder,
48+
trap_ty,
49+
trap_fn,
50+
[].as_ptr(),
51+
0,
52+
[].as_ptr(),
53+
0,
54+
c"".as_ptr(),
55+
);
56+
57+
llvm::LLVMBuildUnreachable(llbuilder);
58+
59+
llvm::LLVMDisposeBuilder(llbuilder);
60+
}
61+
}
62+
63+
fn declare_stub_personality<'ll>(
64+
tcx: TyCtxt<'_>,
65+
llcx: &'ll Context,
66+
llmod: &'ll Module,
67+
) -> &'ll llvm::Value {
68+
let name = "rust_eh_personality";
69+
unsafe {
70+
let no_return = llvm::AttributeKind::NoReturn.create_attr(llcx);
71+
72+
let ty = llvm::LLVMFunctionType(llvm::LLVMVoidTypeInContext(llcx), [].as_ptr(), 0, False);
73+
let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_c_char_ptr(), name.len(), ty);
74+
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]);
75+
llvm::set_visibility(llfn, llvm::Visibility::from_generic(tcx.sess.default_visibility()));
76+
77+
if tcx.sess.must_emit_unwind_tables() {
78+
let uwtable =
79+
attributes::uwtable_attr(llcx, tcx.sess.opts.unstable_opts.use_sync_unwind);
80+
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
81+
}
82+
83+
llfn
84+
}
85+
}
86+
87+
fn trap_intrinsic<'ll>(
88+
llcx: &'ll Context,
89+
llmod: &'ll Module,
90+
) -> (&'ll llvm::Value, &'ll llvm::Type) {
91+
let name = "llvm.trap";
92+
93+
unsafe {
94+
let no_return = llvm::AttributeKind::NoReturn.create_attr(llcx);
95+
let llty = llvm::LLVMFunctionType(llvm::LLVMVoidTypeInContext(llcx), [].as_ptr(), 0, False);
96+
let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_c_char_ptr(), name.len(), llty);
97+
llvm::SetFunctionCallConv(llfn, llvm::CCallConv);
98+
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[no_return]);
99+
100+
(llfn, llty)
101+
}
102+
}

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ pub fn link_binary(
208208
if let Some(ref allocator_module) = codegen_results.allocator_module {
209209
remove_temps_from_module(allocator_module);
210210
}
211+
if let Some(ref personality_stub_module) = codegen_results.personality_stub_module {
212+
remove_temps_from_module(personality_stub_module);
213+
}
211214

212215
// Remove the temporary files if output goes to stdout
213216
for temp in tempfiles_for_stdout_output {
@@ -333,6 +336,11 @@ fn link_rlib<'a>(
333336
if let Some(obj) = obj {
334337
ab.add_file(obj);
335338
}
339+
let obj =
340+
codegen_results.personality_stub_module.as_ref().and_then(|m| m.object.as_ref());
341+
if let Some(obj) = obj {
342+
ab.add_file(obj);
343+
}
336344
}
337345
}
338346

@@ -2197,6 +2205,18 @@ fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &Cod
21972205
}
21982206
}
21992207

2208+
/// Add object files for the personality stub linked once if necessary for the whole crate tree.
2209+
fn add_local_crate_personality_stub_objects(
2210+
cmd: &mut dyn Linker,
2211+
codegen_results: &CodegenResults,
2212+
) {
2213+
if let Some(obj) =
2214+
codegen_results.personality_stub_module.as_ref().and_then(|m| m.object.as_ref())
2215+
{
2216+
cmd.add_object(obj);
2217+
}
2218+
}
2219+
22002220
/// Add object files containing metadata for the current crate.
22012221
fn add_local_crate_metadata_objects(
22022222
cmd: &mut dyn Linker,
@@ -2380,6 +2400,7 @@ fn linker_with_args(
23802400
add_local_crate_regular_objects(cmd, codegen_results);
23812401
add_local_crate_metadata_objects(cmd, crate_type, codegen_results);
23822402
add_local_crate_allocator_objects(cmd, codegen_results);
2403+
add_local_crate_personality_stub_objects(cmd, codegen_results);
23832404

23842405
// Avoid linking to dynamic libraries unless they satisfy some undefined symbols
23852406
// at the point at which they are specified on the command line.

compiler/rustc_codegen_ssa/src/back/write.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ impl ModuleConfig {
141141
|| match kind {
142142
ModuleKind::Regular => sess.opts.output_types.contains_key(&OutputType::Object),
143143
ModuleKind::Allocator => false,
144+
ModuleKind::PersonalityStub => false,
144145
ModuleKind::Metadata => sess.opts.output_types.contains_key(&OutputType::Metadata),
145146
};
146147

@@ -346,6 +347,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
346347
pub regular_module_config: Arc<ModuleConfig>,
347348
pub metadata_module_config: Arc<ModuleConfig>,
348349
pub allocator_module_config: Arc<ModuleConfig>,
350+
pub personality_stub_module_config: Arc<ModuleConfig>,
349351
pub tm_factory: TargetMachineFactoryFn<B>,
350352
pub msvc_imps_needed: bool,
351353
pub is_pe_coff: bool,
@@ -390,6 +392,7 @@ impl<B: WriteBackendMethods> CodegenContext<B> {
390392
ModuleKind::Regular => &self.regular_module_config,
391393
ModuleKind::Metadata => &self.metadata_module_config,
392394
ModuleKind::Allocator => &self.allocator_module_config,
395+
ModuleKind::PersonalityStub => &self.personality_stub_module_config,
393396
}
394397
}
395398
}
@@ -444,6 +447,7 @@ fn generate_lto_work<B: ExtraBackendMethods>(
444447
struct CompiledModules {
445448
modules: Vec<CompiledModule>,
446449
allocator_module: Option<CompiledModule>,
450+
personality_stub_module: Option<CompiledModule>,
447451
}
448452

449453
fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool {
@@ -481,6 +485,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
481485
let regular_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins);
482486
let metadata_config = ModuleConfig::new(ModuleKind::Metadata, tcx, no_builtins);
483487
let allocator_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins);
488+
let personality_stub_config = ModuleConfig::new(ModuleKind::PersonalityStub, tcx, no_builtins);
484489

485490
let (shared_emitter, shared_emitter_main) = SharedEmitter::new();
486491
let (codegen_worker_send, codegen_worker_receive) = channel();
@@ -495,6 +500,7 @@ pub(crate) fn start_async_codegen<B: ExtraBackendMethods>(
495500
Arc::new(regular_config),
496501
Arc::new(metadata_config),
497502
Arc::new(allocator_config),
503+
Arc::new(personality_stub_config),
498504
coordinator_send.clone(),
499505
);
500506

@@ -708,6 +714,11 @@ fn produce_final_output_artifacts(
708714
ensure_removed(sess.dcx(), path);
709715
}
710716
}
717+
if let Some(ref personality_stub_module) = compiled_modules.personality_stub_module {
718+
if let Some(ref path) = personality_stub_module.bytecode {
719+
ensure_removed(sess.dcx(), path);
720+
}
721+
}
711722
}
712723
}
713724

@@ -1113,6 +1124,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
11131124
regular_config: Arc<ModuleConfig>,
11141125
metadata_config: Arc<ModuleConfig>,
11151126
allocator_config: Arc<ModuleConfig>,
1127+
personality_stub_config: Arc<ModuleConfig>,
11161128
tx_to_llvm_workers: Sender<Box<dyn Any + Send>>,
11171129
) -> thread::JoinHandle<Result<CompiledModules, ()>> {
11181130
let coordinator_send = tx_to_llvm_workers;
@@ -1206,6 +1218,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
12061218
regular_module_config: regular_config,
12071219
metadata_module_config: metadata_config,
12081220
allocator_module_config: allocator_config,
1221+
personality_stub_module_config: personality_stub_config,
12091222
tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features),
12101223
msvc_imps_needed: msvc_imps_needed(tcx),
12111224
is_pe_coff: tcx.sess.target.is_like_windows,
@@ -1371,6 +1384,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
13711384
let mut autodiff_items = Vec::new();
13721385
let mut compiled_modules = vec![];
13731386
let mut compiled_allocator_module = None;
1387+
let mut compiled_personality_stub_module = None;
13741388
let mut needs_link = Vec::new();
13751389
let mut needs_fat_lto = Vec::new();
13761390
let mut needs_thin_lto = Vec::new();
@@ -1659,6 +1673,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
16591673
assert!(compiled_allocator_module.is_none());
16601674
compiled_allocator_module = Some(compiled_module);
16611675
}
1676+
ModuleKind::PersonalityStub => {
1677+
assert!(compiled_personality_stub_module.is_none());
1678+
compiled_personality_stub_module = Some(compiled_module);
1679+
}
16621680
ModuleKind::Metadata => bug!("Should be handled separately"),
16631681
}
16641682
}
@@ -1725,6 +1743,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
17251743
Ok(CompiledModules {
17261744
modules: compiled_modules,
17271745
allocator_module: compiled_allocator_module,
1746+
personality_stub_module: compiled_personality_stub_module,
17281747
})
17291748
})
17301749
.expect("failed to spawn coordinator thread");
@@ -2088,6 +2107,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
20882107

20892108
modules: compiled_modules.modules,
20902109
allocator_module: compiled_modules.allocator_module,
2110+
personality_stub_module: compiled_modules.personality_stub_module,
20912111
metadata_module: self.metadata_module,
20922112
},
20932113
work_products,

compiler/rustc_codegen_ssa/src/base.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
2828
use rustc_session::Session;
2929
use rustc_session::config::{self, CrateType, EntryFnType, OutputType};
3030
use rustc_span::{DUMMY_SP, Symbol, sym};
31+
use rustc_target::spec::PanicStrategy;
3132
use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
3233
use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
3334
use tracing::{debug, info};
@@ -592,6 +593,18 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option<AllocatorKind> {
592593
if any_dynamic_crate { None } else { tcx.allocator_kind(()) }
593594
}
594595

596+
/// Decide whether to inject an aborting stub personality function into this crate.
597+
pub fn needs_stub_personality_function(tcx: TyCtxt<'_>) -> bool {
598+
let missing_personality = tcx.lang_items().eh_personality().is_none();
599+
let is_abort = tcx.sess.panic_strategy() == PanicStrategy::Abort;
600+
let is_not_panic_abort = tcx
601+
.crates(())
602+
.iter()
603+
.any(|cnum| tcx.required_panic_strategy(*cnum) != Some(PanicStrategy::Abort));
604+
let any_non_rlib = tcx.crate_types().iter().any(|ctype| *ctype != CrateType::Rlib);
605+
missing_personality && is_abort && is_not_panic_abort && any_non_rlib
606+
}
607+
595608
pub fn codegen_crate<B: ExtraBackendMethods>(
596609
backend: B,
597610
tcx: TyCtxt<'_>,
@@ -692,6 +705,27 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
692705
);
693706
}
694707

708+
if needs_stub_personality_function(tcx) {
709+
let llmod_id = cgu_name_builder
710+
.build_cgu_name(LOCAL_CRATE, &["crate"], Some("personality_stub"))
711+
.to_string();
712+
let module_llvm = tcx.sess.time("write_personality_stub_module", || {
713+
backend.codegen_personality_stub(tcx, &llmod_id)
714+
});
715+
716+
ongoing_codegen.wait_for_signal_to_codegen_item();
717+
ongoing_codegen.check_for_errors(tcx.sess);
718+
719+
// These modules are generally cheap and won't throw off scheduling.
720+
let cost = 0;
721+
submit_codegened_module_to_llvm(
722+
&backend,
723+
&ongoing_codegen.coordinator.sender,
724+
ModuleCodegen::new_personality_stub(llmod_id, module_llvm),
725+
cost,
726+
);
727+
}
728+
695729
if !autodiff_fncs.is_empty() {
696730
ongoing_codegen.submit_autodiff_items(autodiff_fncs);
697731
}

compiler/rustc_codegen_ssa/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ impl<M> ModuleCodegen<M> {
9898
}
9999
}
100100

101+
pub fn new_personality_stub(name: impl Into<String>, module: M) -> Self {
102+
Self {
103+
name: name.into(),
104+
module_llvm: module,
105+
kind: ModuleKind::PersonalityStub,
106+
thin_lto_buffer: None,
107+
}
108+
}
109+
101110
pub fn into_compiled_module(
102111
self,
103112
emit_obj: bool,
@@ -165,6 +174,7 @@ pub enum ModuleKind {
165174
Regular,
166175
Metadata,
167176
Allocator,
177+
PersonalityStub,
168178
}
169179

170180
bitflags::bitflags! {
@@ -233,6 +243,7 @@ pub struct CrateInfo {
233243
pub struct CodegenResults {
234244
pub modules: Vec<CompiledModule>,
235245
pub allocator_module: Option<CompiledModule>,
246+
pub personality_stub_module: Option<CompiledModule>,
236247
pub metadata_module: Option<CompiledModule>,
237248
pub metadata: rustc_metadata::EncodedMetadata,
238249
pub crate_info: CrateInfo,

compiler/rustc_codegen_ssa/src/traits/backend.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ pub trait ExtraBackendMethods:
108108
alloc_error_handler_kind: AllocatorKind,
109109
) -> Self::Module;
110110

111+
fn codegen_personality_stub<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str) -> Self::Module;
112+
111113
/// This generates the codegen unit and returns it along with
112114
/// a `u64` giving an estimate of the unit's processing cost.
113115
fn compile_codegen_unit(

0 commit comments

Comments
 (0)