Change how wasm DWARF is inserted into artifacts (#5358)
This commit fixes a bug with components by changing how DWARF information from a wasm binary is copied over to the final compiled artifact. Note that this is not the Wasmtime-generated DWARF but rather the native wasm DWARF itself used in backtraces. Previously the wasm dwarf was inserted into sections `.*.wasm` where `*` was `debug_info`, `debug_str`, etc -- one per `gimli::SectionId` as found in the original wasm module. This does not work with components, however, where modules did not correctly separate their debug information into separate sections or otherwise disambiguate. The fix in this commit is to instead smash all the debug information together into one large section and store offsets into that giant section. This is similar to the `name`-section scraping or the trap metadata section where one section contains all the data for all the modules in a component. This simplifies the object file parsing by only looking for one section name and doesn't add all that much complexity to serializing and looking up dwarf information as well.
This commit is contained in:
@@ -71,6 +71,10 @@ struct Metadata {
|
||||
/// Note that even if this flag is `true` sections may be missing if they
|
||||
/// weren't found in the original wasm module itself.
|
||||
has_wasm_debuginfo: bool,
|
||||
|
||||
/// Dwarf sections and the offsets at which they're stored in the
|
||||
/// ELF_WASMTIME_DWARF
|
||||
dwarf: Vec<(u8, Range<u64>)>,
|
||||
}
|
||||
|
||||
/// Helper structure to create an ELF file as a compilation artifact.
|
||||
@@ -94,6 +98,12 @@ pub struct ObjectBuilder<'a> {
|
||||
///
|
||||
/// This is optional and lazily created on demand.
|
||||
names: Option<SectionId>,
|
||||
|
||||
/// The section identifier for dwarf information copied from the original
|
||||
/// wasm files.
|
||||
///
|
||||
/// This is optional and lazily created on demand.
|
||||
dwarf: Option<SectionId>,
|
||||
}
|
||||
|
||||
impl<'a> ObjectBuilder<'a> {
|
||||
@@ -109,6 +119,7 @@ impl<'a> ObjectBuilder<'a> {
|
||||
tunables,
|
||||
data,
|
||||
names: None,
|
||||
dwarf: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,18 +250,21 @@ impl<'a> ObjectBuilder<'a> {
|
||||
// requested. Note that this is distinct from the native debuginfo
|
||||
// possibly generated by the native compiler, hence these sections
|
||||
// getting wasm-specific names.
|
||||
let mut dwarf = Vec::new();
|
||||
if self.tunables.parse_wasm_debuginfo {
|
||||
self.push_debug(&debuginfo.dwarf.debug_abbrev);
|
||||
self.push_debug(&debuginfo.dwarf.debug_addr);
|
||||
self.push_debug(&debuginfo.dwarf.debug_aranges);
|
||||
self.push_debug(&debuginfo.dwarf.debug_info);
|
||||
self.push_debug(&debuginfo.dwarf.debug_line);
|
||||
self.push_debug(&debuginfo.dwarf.debug_line_str);
|
||||
self.push_debug(&debuginfo.dwarf.debug_str);
|
||||
self.push_debug(&debuginfo.dwarf.debug_str_offsets);
|
||||
self.push_debug(&debuginfo.debug_ranges);
|
||||
self.push_debug(&debuginfo.debug_rnglists);
|
||||
self.push_debug(&mut dwarf, &debuginfo.dwarf.debug_abbrev);
|
||||
self.push_debug(&mut dwarf, &debuginfo.dwarf.debug_addr);
|
||||
self.push_debug(&mut dwarf, &debuginfo.dwarf.debug_aranges);
|
||||
self.push_debug(&mut dwarf, &debuginfo.dwarf.debug_info);
|
||||
self.push_debug(&mut dwarf, &debuginfo.dwarf.debug_line);
|
||||
self.push_debug(&mut dwarf, &debuginfo.dwarf.debug_line_str);
|
||||
self.push_debug(&mut dwarf, &debuginfo.dwarf.debug_str);
|
||||
self.push_debug(&mut dwarf, &debuginfo.dwarf.debug_str_offsets);
|
||||
self.push_debug(&mut dwarf, &debuginfo.debug_ranges);
|
||||
self.push_debug(&mut dwarf, &debuginfo.debug_rnglists);
|
||||
}
|
||||
// Sort this for binary-search-lookup later in `symbolize_context`.
|
||||
dwarf.sort_by_key(|(id, _)| *id);
|
||||
|
||||
Ok(CompiledModuleInfo {
|
||||
module,
|
||||
@@ -262,11 +276,12 @@ impl<'a> ObjectBuilder<'a> {
|
||||
has_unparsed_debuginfo,
|
||||
code_section_offset: debuginfo.wasm_file.code_section_offset,
|
||||
has_wasm_debuginfo: self.tunables.parse_wasm_debuginfo,
|
||||
dwarf,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn push_debug<'b, T>(&mut self, section: &T)
|
||||
fn push_debug<'b, T>(&mut self, dwarf: &mut Vec<(u8, Range<u64>)>, section: &T)
|
||||
where
|
||||
T: gimli::Section<gimli::EndianSlice<'b, gimli::LittleEndian>>,
|
||||
{
|
||||
@@ -274,12 +289,15 @@ impl<'a> ObjectBuilder<'a> {
|
||||
if data.is_empty() {
|
||||
return;
|
||||
}
|
||||
let section_id = self.obj.add_section(
|
||||
self.obj.segment_name(StandardSegment::Debug).to_vec(),
|
||||
format!("{}.wasm", T::id().name()).into_bytes(),
|
||||
SectionKind::Debug,
|
||||
);
|
||||
self.obj.append_section_data(section_id, data, 1);
|
||||
let section_id = *self.dwarf.get_or_insert_with(|| {
|
||||
self.obj.add_section(
|
||||
self.obj.segment_name(StandardSegment::Debug).to_vec(),
|
||||
obj::ELF_WASMTIME_DWARF.as_bytes().to_vec(),
|
||||
SectionKind::Debug,
|
||||
)
|
||||
});
|
||||
let offset = self.obj.append_section_data(section_id, data, 1);
|
||||
dwarf.push((T::id() as u8, offset..offset + data.len() as u64));
|
||||
}
|
||||
|
||||
/// Creates the `ELF_WASMTIME_INFO` section from the given serializable data
|
||||
@@ -591,7 +609,20 @@ impl CompiledModule {
|
||||
return Ok(None);
|
||||
}
|
||||
let dwarf = gimli::Dwarf::load(|id| -> Result<_> {
|
||||
let data = self.code_memory().dwarf_section(id);
|
||||
// Lookup the `id` in the `dwarf` array prepared for this module
|
||||
// during module serialization where it's sorted by the `id` key. If
|
||||
// found this is a range within the general module's concatenated
|
||||
// dwarf section which is extracted here, otherwise it's just an
|
||||
// empty list to represent that it's not present.
|
||||
let data = self
|
||||
.meta
|
||||
.dwarf
|
||||
.binary_search_by_key(&(id as u8), |(id, _)| *id)
|
||||
.map(|i| {
|
||||
let (_, range) = &self.meta.dwarf[i];
|
||||
&self.code_memory().dwarf()[range.start as usize..range.end as usize]
|
||||
})
|
||||
.unwrap_or(&[]);
|
||||
Ok(EndianSlice::new(data, gimli::LittleEndian))
|
||||
})?;
|
||||
let cx = addr2line::Context::from_dwarf(dwarf)
|
||||
|
||||
Reference in New Issue
Block a user