Skip to content

Commit

Permalink
Place unwind info directly after the text section, even when debug in…
Browse files Browse the repository at this point in the history
…fo is enabled

When debug info was enabled, we would put the debug info sections in between the
text section and the unwind info section. But the unwind info is encoded in a
position-independent manner (so that we don't need relocs for it) that relies on
it directly following the text section. The result of the misplacement was some
crashes inside the unwinder.
  • Loading branch information
fitzgen committed Sep 9, 2021
1 parent 0499cca commit 4b256ab
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 12 deletions.
3 changes: 3 additions & 0 deletions crates/cranelift/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ impl wasmtime_environ::Compiler for Compiler {
trampolines.push(builder.trampoline(*i, &func));
}

builder.unwind_info();

if emit_dwarf && funcs.len() > 0 {
let ofs = VMOffsets::new(
self.isa
Expand Down Expand Up @@ -330,6 +332,7 @@ impl wasmtime_environ::Compiler for Compiler {
let mut builder = ObjectBuilder::new(obj, &module, &*self.isa);
let a = builder.trampoline(SignatureIndex::new(0), &host_to_wasm);
let b = builder.trampoline(SignatureIndex::new(1), &wasm_to_host);
builder.unwind_info();
builder.finish()?;
Ok((a, b))
}
Expand Down
58 changes: 46 additions & 12 deletions crates/cranelift/src/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,14 @@ pub struct ObjectBuilder<'a> {
/// a relocation against a libcall.
libcalls: HashMap<LibCall, SymbolId>,

windows_unwind_info_id: Option<SectionId>,

/// Packed form of windows unwind tables which, if present, will get emitted
/// to a windows-specific unwind info section.
windows_unwind_info: Vec<RUNTIME_FUNCTION>,

systemv_unwind_info_id: Option<SectionId>,

/// Pending unwinding information for DWARF-based platforms. This is used to
/// build a `.eh_frame` lookalike at the very end of object building.
systemv_unwind_info: Vec<(u64, &'a systemv::UnwindInfo)>,
Expand All @@ -139,6 +143,11 @@ pub struct ObjectBuilder<'a> {
/// In-progress text section that we're using cranelift's `MachBuffer` to
/// build to resolve relocations (calls) between functions.
pub text: Box<dyn TextSectionBuilder>,

/// The unwind info _must_ come directly after the text section. Our FDE's
/// instructions are encoded to rely on this placement. We use this `bool`
/// for debug assertions to ensure that we get the ordering correct.
added_unwind_info: bool,
}

// This is a mirror of `RUNTIME_FUNCTION` in the Windows API, but defined here
Expand Down Expand Up @@ -190,7 +199,9 @@ impl<'a> ObjectBuilder<'a> {
text_section,
func_symbols,
libcalls,
windows_unwind_info_id: None,
windows_unwind_info: Vec::new(),
systemv_unwind_info_id: None,
systemv_unwind_info: Vec::new(),
relocations: Vec::new(),
text: match isa.get_mach_backend() {
Expand All @@ -199,6 +210,7 @@ impl<'a> ObjectBuilder<'a> {
),
None => Box::new(DummyBuilder::default()),
},
added_unwind_info: false,
}
}

Expand Down Expand Up @@ -332,6 +344,7 @@ impl<'a> ObjectBuilder<'a> {
///
/// This is expected to be called in-order for ascending `index` values.
pub fn func(&mut self, index: DefinedFuncIndex, func: &'a CompiledFunction) -> Range<u64> {
assert!(!self.added_unwind_info);
let index = self.module.func_index(index);
let name = obj::func_symbol_name(index);
let (symbol_id, range) = self.append_func(true, name.into_bytes(), func);
Expand All @@ -340,6 +353,7 @@ impl<'a> ObjectBuilder<'a> {
}

pub fn trampoline(&mut self, sig: SignatureIndex, func: &'a CompiledFunction) -> Trampoline {
assert!(!self.added_unwind_info);
let name = obj::trampoline_symbol_name(sig);
let (_, range) = self.append_func(false, name.into_bytes(), func);
Trampoline {
Expand All @@ -350,6 +364,11 @@ impl<'a> ObjectBuilder<'a> {
}

pub fn dwarf_sections(&mut self, sections: &[DwarfSection]) -> Result<()> {
assert!(
self.added_unwind_info,
"can't add dwarf yet; unwind info must directly follow the text section"
);

// If we have DWARF data, write it in the object file.
let (debug_bodies, debug_relocs): (Vec<_>, Vec<_>) = sections
.iter()
Expand Down Expand Up @@ -394,6 +413,29 @@ impl<'a> ObjectBuilder<'a> {
Ok(())
}

pub fn unwind_info(&mut self) {
assert!(!self.added_unwind_info);

if self.windows_unwind_info.len() > 0 {
let segment = self.obj.segment_name(StandardSegment::Data).to_vec();
self.windows_unwind_info_id = Some(self.obj.add_section(
segment,
b"_wasmtime_winx64_unwind".to_vec(),
SectionKind::ReadOnlyData,
));
}
if self.systemv_unwind_info.len() > 0 {
let segment = self.obj.segment_name(StandardSegment::Data).to_vec();
self.systemv_unwind_info_id = Some(self.obj.add_section(
segment,
b".eh_frame".to_vec(),
SectionKind::ReadOnlyData,
));
}

self.added_unwind_info = true;
}

pub fn finish(&mut self) -> Result<()> {
// Now that all function symbols are available register all final
// relocations between functions.
Expand Down Expand Up @@ -438,6 +480,7 @@ impl<'a> ObjectBuilder<'a> {
if self.systemv_unwind_info.len() > 0 {
self.append_systemv_unwind_info();
}

Ok(())
}

Expand All @@ -454,17 +497,13 @@ impl<'a> ObjectBuilder<'a> {
// This may need updates for other platforms.
assert_eq!(self.obj.architecture(), Architecture::X86_64);

let section_id = self.windows_unwind_info_id.unwrap();

// Page-align the text section so the unwind info can reside on a
// separate page that doesn't need executable permissions.
self.obj
.append_section_data(self.text_section, &[], self.isa.code_section_alignment());

let segment = self.obj.segment_name(StandardSegment::Data).to_vec();
let section_id = self.obj.add_section(
segment,
b"_wasmtime_winx64_unwind".to_vec(),
SectionKind::ReadOnlyData,
);
let mut unwind_info = Vec::with_capacity(self.windows_unwind_info.len() * 3 * 4);
for info in self.windows_unwind_info.iter() {
unwind_info.extend_from_slice(&info.begin.to_le_bytes());
Expand Down Expand Up @@ -513,12 +552,7 @@ impl<'a> ObjectBuilder<'a> {
/// such as being purely read-only instead of read/execute like the code
/// bits.
fn append_systemv_unwind_info(&mut self) {
let segment = self.obj.segment_name(StandardSegment::Data).to_vec();
let section_id = self.obj.add_section(
segment,
b".eh_frame".to_vec(),
SectionKind::ReadOnlyData,
);
let section_id = self.systemv_unwind_info_id.unwrap();
let mut cie = self
.isa
.create_systemv_cie()
Expand Down
6 changes: 6 additions & 0 deletions crates/jit/src/unwind/systemv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ impl UnwindRegistration {
unwind_info: *mut u8,
unwind_len: usize,
) -> Result<UnwindRegistration> {
debug_assert_eq!(
unwind_info as usize % region::page::size(),
0,
"The unwind info must always be aligned to a page"
);

let mut registrations = Vec::new();
if cfg!(any(
all(target_os = "linux", target_env = "gnu"),
Expand Down

0 comments on commit 4b256ab

Please sign in to comment.