Skip to content

cranelift-object: emit .eh_frame for System V unwind info#13383

Merged
fitzgen merged 2 commits into
bytecodealliance:mainfrom
HueCodes:feat/eh-frame-emission
May 20, 2026
Merged

cranelift-object: emit .eh_frame for System V unwind info#13383
fitzgen merged 2 commits into
bytecodealliance:mainfrom
HueCodes:feat/eh-frame-emission

Conversation

@HueCodes
Copy link
Copy Markdown
Contributor

Add ObjectBuilder::unwind_info(bool). When enabled, the emitted object gains a .eh_frame section with one shared CIE and one FDE per defined function, relocated against the function symbols.

The implementation routes cranelift-codegen's existing UnwindInfo::SystemV through gimli's RelocateWriter into an object-crate section. Off by default. Supported on ELF and COFF; Mach-O and Windows unwind formats are deferred and currently no-op or error rather than emit wrong data.

Closes #1282.

@HueCodes HueCodes requested review from a team as code owners May 15, 2026 05:02
@HueCodes HueCodes requested review from cfallin and fitzgen and removed request for a team May 15, 2026 05:02
@bjorn3
Copy link
Copy Markdown
Contributor

bjorn3 commented May 15, 2026

Maybe put this behind a feature flag? This wouldn't be usable for rustc_codegen_cranelift as it doesn't allow writing an LSDA.

On arm64 Mach-O it should be possible to emit __TEXT,__eh_frame by the way: rust-lang/rustc_codegen_cranelift#1634

@github-actions github-actions Bot added cranelift Issues related to the Cranelift code generator cranelift:module labels May 15, 2026
Add `ObjectBuilder::unwind_info(bool)`. When enabled, the emitted object
gains a `.eh_frame` section with one shared CIE and one FDE per defined
function, relocated against the function symbols.

The implementation routes cranelift-codegen's existing `UnwindInfo::SystemV`
through gimli's `RelocateWriter` into an `object`-crate section. Off by
default. Supported on ELF and COFF; Mach-O and Windows unwind formats are
deferred and currently no-op or error rather than emit wrong data.

Gated behind a new default-on `unwind` Cargo feature mirroring
cranelift-codegen's `unwind`. `gimli` is now optional and pulled in only by
that feature, so consumers writing their own `.eh_frame` (e.g.
rustc_codegen_cranelift, which needs LSDA support) drop it via
`default-features = false`.

Closes bytecodealliance#1282.
@HueCodes HueCodes force-pushed the feat/eh-frame-emission branch from 0b7cc7f to 99dadc9 Compare May 15, 2026 19:40
@HueCodes
Copy link
Copy Markdown
Contributor Author

@bjorn3 thanks. Done: default-on "unwind" Cargo feature mirroring cranelift-codegen's "unwind", with gimli and ObjectBuilder::unwind_info both gated behind it, so cg_clif drops the dep and API via default-features = false. Kept Mach-O scoped out for now (wrong-reloc risk); TODO left pointing at
cg_clif#1634.

Copy link
Copy Markdown
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I think we could clean up the boundary between cfg(feature = "unwind") vs cfg(not(feature = "unwind")) a bit.

Comment thread cranelift/object/src/backend.rs
Comment thread cranelift/object/src/backend.rs
Comment thread cranelift/object/src/backend.rs
Comment thread cranelift/object/src/backend.rs
#![deny(missing_docs)]

mod backend;
mod unwind;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we also don't want to build any of this module unless #[cfg(feature = "unwind")].

Comment thread cranelift/object/src/unwind.rs Outdated
Comment on lines +29 to +52
/// No-op stand-in used when the `unwind` feature is disabled. It carries no
/// state and emits nothing, so callers need no `#[cfg]` and pull in no
/// `gimli` dependency.
#[cfg(not(feature = "unwind"))]
pub(crate) struct UnwindBuilder;

#[cfg(not(feature = "unwind"))]
impl UnwindBuilder {
pub(crate) fn new(_endian: object::Endianness) -> Self {
UnwindBuilder
}

pub(crate) fn add_function(
&mut self,
_isa: &dyn TargetIsa,
_func_symbol: SymbolId,
_info: UnwindInfo,
) {
}

pub(crate) fn finish(self, _object: &mut Object<'static>, _isa: &dyn TargetIsa) -> Result<()> {
Ok(())
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah better to avoid this by moving the #[cfg(feature = "unwind")] "up" and avoiding this whole module or any calls into it when the feature is disabled.

Move `#[cfg(feature = "unwind")]` from inside the `unwind` module up to
the field declarations, struct initializers, and call sites in
`backend.rs`. The no-op `UnwindBuilder` stub is gone; the module itself
no longer compiles when the feature is off.
@HueCodes
Copy link
Copy Markdown
Contributor Author

@fitzgen thanks! Changes made: stub gone, #[cfg(feature = "unwind")] lifted to the field decls, struct init,
and call sites. No cfg(not(feature = "unwind")) branch left.

Copy link
Copy Markdown
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@fitzgen fitzgen added this pull request to the merge queue May 20, 2026
Merged via the queue into bytecodealliance:main with commit f77276f May 20, 2026
51 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cranelift:module cranelift Issues related to the Cranelift code generator

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add capability (to cranelift) to generate .eh_frame

3 participants