| //! Write the debuginfo into an object file. |
| |
| use cranelift_module::{DataId, FuncId}; |
| use cranelift_object::ObjectProduct; |
| use gimli::write::{Address, AttributeValue, EndianVec, Result, Sections, Writer}; |
| use gimli::{RunTimeEndian, SectionId}; |
| use rustc_data_structures::fx::FxHashMap; |
| |
| use super::DebugContext; |
| use super::object::WriteDebugInfo; |
| |
| pub(super) fn address_for_func(func_id: FuncId) -> Address { |
| let symbol = func_id.as_u32(); |
| assert!(symbol & 1 << 31 == 0); |
| Address::Symbol { symbol: symbol as usize, addend: 0 } |
| } |
| |
| pub(super) fn address_for_data(data_id: DataId) -> Address { |
| let symbol = data_id.as_u32(); |
| assert!(symbol & 1 << 31 == 0); |
| Address::Symbol { symbol: (symbol | 1 << 31) as usize, addend: 0 } |
| } |
| |
| impl DebugContext { |
| pub(crate) fn emit(&mut self, product: &mut ObjectProduct) { |
| let unit_range_list_id = self.dwarf.unit.ranges.add(self.unit_range_list.clone()); |
| let root = self.dwarf.unit.root(); |
| let root = self.dwarf.unit.get_mut(root); |
| root.set(gimli::DW_AT_ranges, AttributeValue::RangeListRef(unit_range_list_id)); |
| |
| let mut sections = Sections::new(WriterRelocate::new(self.endian)); |
| self.dwarf.write(&mut sections).unwrap(); |
| |
| let mut section_map = FxHashMap::default(); |
| let _: Result<()> = sections.for_each_mut(|id, section| { |
| if !section.writer.slice().is_empty() { |
| let section_id = product.add_debug_section(id, section.writer.take()); |
| section_map.insert(id, section_id); |
| } |
| Ok(()) |
| }); |
| |
| let _: Result<()> = sections.for_each(|id, section| { |
| if let Some(section_id) = section_map.get(&id) { |
| for reloc in §ion.relocs { |
| product.add_debug_reloc(§ion_map, section_id, reloc); |
| } |
| } |
| Ok(()) |
| }); |
| } |
| } |
| |
| #[derive(Clone)] |
| pub(crate) struct DebugReloc { |
| pub(crate) offset: u32, |
| pub(crate) size: u8, |
| pub(crate) name: DebugRelocName, |
| pub(crate) addend: i64, |
| pub(crate) kind: object::RelocationKind, |
| } |
| |
| #[derive(Clone)] |
| pub(crate) enum DebugRelocName { |
| Section(SectionId), |
| Symbol(usize), |
| } |
| |
| /// A [`Writer`] that collects all necessary relocations. |
| #[derive(Clone)] |
| pub(super) struct WriterRelocate { |
| pub(super) relocs: Vec<DebugReloc>, |
| pub(super) writer: EndianVec<RunTimeEndian>, |
| } |
| |
| impl WriterRelocate { |
| pub(super) fn new(endian: RunTimeEndian) -> Self { |
| WriterRelocate { relocs: Vec::new(), writer: EndianVec::new(endian) } |
| } |
| |
| /// Perform the collected relocations to be usable for JIT usage. |
| #[cfg(all(feature = "jit", not(windows)))] |
| pub(super) fn relocate_for_jit(mut self, jit_module: &cranelift_jit::JITModule) -> Vec<u8> { |
| use cranelift_module::Module; |
| |
| for reloc in self.relocs.drain(..) { |
| match reloc.name { |
| super::DebugRelocName::Section(_) => unreachable!(), |
| super::DebugRelocName::Symbol(sym) => { |
| let addr = if sym & 1 << 31 == 0 { |
| let func_id = FuncId::from_u32(sym.try_into().unwrap()); |
| // FIXME make JITModule::get_address public and use it here instead. |
| // HACK rust_eh_personality is likely not defined in the same crate, |
| // so get_finalized_function won't work. Use the rust_eh_personality |
| // of cg_clif itself, which is likely ABI compatible. |
| if jit_module.declarations().get_function_decl(func_id).name.as_deref() |
| == Some("rust_eh_personality") |
| { |
| extern "C" { |
| fn rust_eh_personality() -> !; |
| } |
| rust_eh_personality as *const u8 |
| } else { |
| jit_module.get_finalized_function(func_id) |
| } |
| } else { |
| jit_module |
| .get_finalized_data(DataId::from_u32( |
| u32::try_from(sym).unwrap() & !(1 << 31), |
| )) |
| .0 |
| }; |
| |
| let val = (addr as u64 as i64 + reloc.addend) as u64; |
| self.writer.write_udata_at(reloc.offset as usize, val, reloc.size).unwrap(); |
| } |
| } |
| } |
| self.writer.into_vec() |
| } |
| } |
| |
| impl Writer for WriterRelocate { |
| type Endian = RunTimeEndian; |
| |
| fn endian(&self) -> Self::Endian { |
| self.writer.endian() |
| } |
| |
| fn len(&self) -> usize { |
| self.writer.len() |
| } |
| |
| fn write(&mut self, bytes: &[u8]) -> Result<()> { |
| self.writer.write(bytes) |
| } |
| |
| fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { |
| self.writer.write_at(offset, bytes) |
| } |
| |
| fn write_address(&mut self, address: Address, size: u8) -> Result<()> { |
| match address { |
| Address::Constant(val) => self.write_udata(val, size), |
| Address::Symbol { symbol, addend } => { |
| let offset = self.len() as u64; |
| self.relocs.push(DebugReloc { |
| offset: offset as u32, |
| size, |
| name: DebugRelocName::Symbol(symbol), |
| addend, |
| kind: object::RelocationKind::Absolute, |
| }); |
| self.write_udata(0, size) |
| } |
| } |
| } |
| |
| fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { |
| let offset = self.len() as u32; |
| self.relocs.push(DebugReloc { |
| offset, |
| size, |
| name: DebugRelocName::Section(section), |
| addend: val as i64, |
| kind: object::RelocationKind::Absolute, |
| }); |
| self.write_udata(0, size) |
| } |
| |
| fn write_offset_at( |
| &mut self, |
| offset: usize, |
| val: usize, |
| section: SectionId, |
| size: u8, |
| ) -> Result<()> { |
| self.relocs.push(DebugReloc { |
| offset: offset as u32, |
| size, |
| name: DebugRelocName::Section(section), |
| addend: val as i64, |
| kind: object::RelocationKind::Absolute, |
| }); |
| self.write_udata_at(offset, 0, size) |
| } |
| |
| fn write_eh_pointer(&mut self, address: Address, eh_pe: gimli::DwEhPe, size: u8) -> Result<()> { |
| match address { |
| // Address::Constant arm copied from gimli |
| Address::Constant(val) => { |
| // Indirect doesn't matter here. |
| let val = match eh_pe.application() { |
| gimli::DW_EH_PE_absptr => val, |
| gimli::DW_EH_PE_pcrel => { |
| // FIXME better handling of sign |
| let offset = self.len() as u64; |
| offset.wrapping_sub(val) |
| } |
| _ => { |
| return Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)); |
| } |
| }; |
| self.write_eh_pointer_data(val, eh_pe.format(), size) |
| } |
| Address::Symbol { symbol, addend } => match eh_pe.application() { |
| gimli::DW_EH_PE_pcrel => { |
| let size = match eh_pe.format() { |
| gimli::DW_EH_PE_sdata4 => 4, |
| gimli::DW_EH_PE_sdata8 => 8, |
| _ => return Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)), |
| }; |
| self.relocs.push(DebugReloc { |
| offset: self.len() as u32, |
| size, |
| name: DebugRelocName::Symbol(symbol), |
| addend, |
| kind: object::RelocationKind::Relative, |
| }); |
| self.write_udata(0, size) |
| } |
| gimli::DW_EH_PE_absptr => { |
| self.relocs.push(DebugReloc { |
| offset: self.len() as u32, |
| size: size.into(), |
| name: DebugRelocName::Symbol(symbol), |
| addend, |
| kind: object::RelocationKind::Absolute, |
| }); |
| self.write_udata(0, size.into()) |
| } |
| _ => Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)), |
| }, |
| } |
| } |
| } |