diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 444f994a7d..a6b8ea74ca 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -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 @@ -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)) } diff --git a/crates/cranelift/src/obj.rs b/crates/cranelift/src/obj.rs index a5acdc29d3..0166347ff4 100644 --- a/crates/cranelift/src/obj.rs +++ b/crates/cranelift/src/obj.rs @@ -114,10 +114,14 @@ pub struct ObjectBuilder<'a> { /// a relocation against a libcall. libcalls: HashMap, + windows_unwind_info_id: Option, + /// Packed form of windows unwind tables which, if present, will get emitted /// to a windows-specific unwind info section. windows_unwind_info: Vec, + systemv_unwind_info_id: Option, + /// 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)>, @@ -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, + + /// 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 @@ -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() { @@ -199,6 +210,7 @@ impl<'a> ObjectBuilder<'a> { ), None => Box::new(DummyBuilder::default()), }, + added_unwind_info: false, } } @@ -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 { + 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); @@ -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 { @@ -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() @@ -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. @@ -438,6 +480,7 @@ impl<'a> ObjectBuilder<'a> { if self.systemv_unwind_info.len() > 0 { self.append_systemv_unwind_info(); } + Ok(()) } @@ -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()); @@ -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() diff --git a/crates/jit/src/unwind/systemv.rs b/crates/jit/src/unwind/systemv.rs index 4ec9d4efcf..6db1af5320 100644 --- a/crates/jit/src/unwind/systemv.rs +++ b/crates/jit/src/unwind/systemv.rs @@ -25,6 +25,12 @@ impl UnwindRegistration { unwind_info: *mut u8, unwind_len: usize, ) -> Result { + 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"),