Repository: bytecodealliance/wasmtime
Pull request #8456 (2024-04-24 06:03:08)

cranelift: Generalize type constraints on Writable
There's no reason to require that the type contained inside Writable must implement Clone, Copy, Debug, etc. If it doesn't implement some of those traits, then the missing traits will not be available on the Writable wrapper either, but that's okay.


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 7a9bb6fba..aa5140a06 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -635,11 +635,15 @@ jobs:
 
     # Test debug (DWARF) related functionality.
     - run: |
-        sudo apt-get update && sudo apt-get install -y gdb lldb llvm
+        sudo apt-get update && sudo apt-get install -y gdb lldb-15 llvm
+        # woraround for https://bugs.launchpad.net/ubuntu/+source/llvm-defaults/+bug/1972855
+        sudo mkdir -p /usr/lib/local/lib/python3.10/dist-packages/lldb
+        sudo ln -s /usr/lib/llvm-15/lib/python3.10/dist-packages/lldb/* /usr/lib/python3/dist-packages/lldb/
         cargo test test_debug_dwarf -- --ignored --test-threads 1
       if: matrix.os == 'ubuntu-latest' && matrix.target == ''&& needs.determine.outputs.run-full
       env:
         RUST_BACKTRACE: 1
+        LLDB: lldb-15 # override default version, 14
 
     # NB: the test job here is explicitly lacking in cancellation of this run if
     # something goes wrong. These take the longest anyway and otherwise if
diff --git a/cranelift/codegen/src/machinst/reg.rs b/cranelift/codegen/src/machinst/reg.rs
index cd5e5f11c..2352a3264 100644
--- a/cranelift/codegen/src/machinst/reg.rs
+++ b/cranelift/codegen/src/machinst/reg.rs
@@ -157,11 +157,11 @@ impl std::fmt::Debug for VirtualReg {
 /// temporary.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
-pub struct Writable<T: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash> {
+pub struct Writable<T> {
     reg: T,
 }
 
-impl<T: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash> Writable<T> {
+impl<T> Writable<T> {
     /// Explicitly construct a `Writable<T>` from a `T`. As noted in
     /// the documentation for `Writable`, this is not hidden or
     /// disallowed from the outside; anyone can perform the "cast";
@@ -176,11 +176,7 @@ impl<T: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash> Writabl
     }
 
     /// Map the underlying register to another value or type.
-    pub fn map<U, F>(self, f: F) -> Writable<U>
-    where
-        U: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash,
-        F: Fn(T) -> U,
-    {
+    pub fn map<U>(self, f: impl Fn(T) -> U) -> Writable<U> {
         Writable { reg: f(self.reg) }
     }
 }
diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs
index dc247b4eb..97f7b2410 100644
--- a/crates/cranelift/src/compiler.rs
+++ b/crates/cranelift/src/compiler.rs
@@ -536,6 +536,8 @@ impl wasmtime_environ::Compiler for Compiler {
         obj: &mut Object<'_>,
         translation: &ModuleTranslation<'_>,
         funcs: &PrimaryMap<DefinedFuncIndex, (SymbolId, &(dyn Any + Send))>,
+        dwarf_package_bytes: Option<&[u8]>,
+        tunables: &Tunables,
     ) -> Result<()> {
         let ofs = VMOffsets::new(
             self.isa
@@ -578,6 +580,8 @@ impl wasmtime_environ::Compiler for Compiler {
             &translation.debuginfo,
             &functions_info,
             &memory_offset,
+            dwarf_package_bytes,
+            tunables,
         )
         .with_context(|| "failed to emit DWARF debug information")?;
 
diff --git a/crates/cranelift/src/debug/gc.rs b/crates/cranelift/src/debug/gc.rs
index ad8c25d8b..c6398fb82 100644
--- a/crates/cranelift/src/debug/gc.rs
+++ b/crates/cranelift/src/debug/gc.rs
@@ -67,12 +67,13 @@ impl Dependencies {
 
 pub fn build_dependencies<R: Reader<Offset = usize>>(
     dwarf: &read::Dwarf<R>,
+    dwp: &Option<read::DwarfPackage<R>>,
     at: &AddressTransform,
 ) -> read::Result<Dependencies> {
     let mut deps = Dependencies::new();
     let mut units = dwarf.units();
     while let Some(unit) = units.next()? {
-        build_unit_dependencies(unit, dwarf, at, &mut deps)?;
+        build_unit_dependencies(unit, dwarf, dwp, at, &mut deps)?;
     }
     Ok(deps)
 }
@@ -80,6 +81,7 @@ pub fn build_dependencies<R: Reader<Offset = usize>>(
 fn build_unit_dependencies<R: Reader<Offset = usize>>(
     header: read::UnitHeader<R>,
     dwarf: &read::Dwarf<R>,
+    dwp: &Option<read::DwarfPackage<R>>,
     at: &AddressTransform,
     deps: &mut Dependencies,
 ) -> read::Result<()> {
@@ -87,6 +89,17 @@ fn build_unit_dependencies<R: Reader<Offset = usize>>(
     let mut tree = unit.entries_tree(None)?;
     let root = tree.root()?;
     build_die_dependencies(root, dwarf, &unit, at, deps)?;
+
+    if let Some(dwarf_package) = dwp {
+        if let Some(dwo_id) = unit.dwo_id {
+            if let Some(cu) = dwarf_package.find_cu(dwo_id, dwarf)? {
+                if let Some(unit_header) = cu.debug_info.units().next()? {
+                    build_unit_dependencies(unit_header, &cu, &None, at, deps)?;
+                }
+            }
+        }
+    }
+
     Ok(())
 }
 
diff --git a/crates/cranelift/src/debug/transform/attr.rs b/crates/cranelift/src/debug/transform/attr.rs
index 354bcdace..543b24a51 100644
--- a/crates/cranelift/src/debug/transform/attr.rs
+++ b/crates/cranelift/src/debug/transform/attr.rs
@@ -5,10 +5,7 @@ use super::refs::{PendingDebugInfoRefs, PendingUnitRefs};
 use super::{DebugInputContext, Reader, TransformError};
 use anyhow::{bail, Error};
 use cranelift_codegen::isa::TargetIsa;
-use gimli::{
-    write, AttributeValue, DebugLineOffset, DebugLineStr, DebugStr, DebugStrOffsets,
-    DebuggingInformationEntry, Unit,
-};
+use gimli::{write, AttributeValue, DebugLineOffset, DebuggingInformationEntry, Unit};
 
 #[derive(Debug)]
 pub(crate) enum FileAttributeContext<'a> {
@@ -124,17 +121,11 @@ where
                     return Err(TransformError("unexpected file index attribute").into());
                 }
             }
-            AttributeValue::DebugStrRef(str_offset) => {
-                let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
-                write::AttributeValue::StringRef(out_strings.add(s))
-            }
-            AttributeValue::DebugStrOffsetsIndex(i) => {
-                let str_offset = context.debug_str_offsets.get_str_offset(
-                    gimli::Format::Dwarf32,
-                    unit.str_offsets_base,
-                    i,
-                )?;
-                let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec();
+            AttributeValue::DebugStrRef(_) | AttributeValue::DebugStrOffsetsIndex(_) => {
+                let s = dwarf
+                    .attr_string(unit, attr.value().clone())?
+                    .to_string_lossy()?
+                    .into_owned();
                 write::AttributeValue::StringRef(out_strings.add(s))
             }
             AttributeValue::RangeListsRef(r) => {
@@ -308,38 +299,22 @@ pub(crate) fn clone_attr_string<R>(
     attr_value: &AttributeValue<R>,
     form: gimli::DwForm,
     unit: &Unit<R, R::Offset>,
-    debug_str: &DebugStr<R>,
-    debug_str_offsets: &DebugStrOffsets<R>,
-    debug_line_str: &DebugLineStr<R>,
+    dwarf: &gimli::Dwarf<R>,
     out_strings: &mut write::StringTable,
 ) -> Result<write::LineString, Error>
 where
     R: Reader,
 {
-    let content = match attr_value {
-        AttributeValue::DebugStrRef(str_offset) => {
-            debug_str.get_str(*str_offset)?.to_slice()?.to_vec()
-        }
-        AttributeValue::DebugStrOffsetsIndex(i) => {
-            let str_offset = debug_str_offsets.get_str_offset(
-                gimli::Format::Dwarf32,
-                unit.str_offsets_base,
-                *i,
-            )?;
-            debug_str.get_str(str_offset)?.to_slice()?.to_vec()
-        }
-        AttributeValue::DebugLineStrRef(str_offset) => {
-            debug_line_str.get_str(*str_offset)?.to_slice()?.to_vec()
-        }
-        AttributeValue::String(b) => b.to_slice()?.to_vec(),
-        v => bail!("Unexpected attribute value: {:?}", v),
-    };
+    let content = dwarf
+        .attr_string(unit, attr_value.clone())?
+        .to_string_lossy()?
+        .into_owned();
     Ok(match form {
         gimli::DW_FORM_strp => {
             let id = out_strings.add(content);
             write::LineString::StringRef(id)
         }
-        gimli::DW_FORM_string => write::LineString::String(content),
+        gimli::DW_FORM_string => write::LineString::String(content.into()),
         _ => bail!("DW_FORM_line_strp or other not supported"),
     })
 }
diff --git a/crates/cranelift/src/debug/transform/line_program.rs b/crates/cranelift/src/debug/transform/line_program.rs
index 8eb068e10..148fe80f3 100644
--- a/crates/cranelift/src/debug/transform/line_program.rs
+++ b/crates/cranelift/src/debug/transform/line_program.rs
@@ -3,7 +3,7 @@ use super::attr::clone_attr_string;
 use super::{Reader, TransformError};
 use anyhow::{Context, Error};
 use gimli::{
-    write, DebugLine, DebugLineOffset, DebugLineStr, DebugStr, DebugStrOffsets,
+    write, AttributeValue::DebugLineRef, DebugLine, DebugLineOffset, DebugStr,
     DebuggingInformationEntry, LineEncoding, Unit,
 };
 use wasmtime_environ::{DefinedFuncIndex, EntityRef};
@@ -40,48 +40,57 @@ enum ReadLineProgramState {
 }
 
 pub(crate) fn clone_line_program<R>(
+    dwarf: &gimli::Dwarf<R>,
+    skeleton_dwarf: &gimli::Dwarf<R>,
     unit: &Unit<R, R::Offset>,
     root: &DebuggingInformationEntry<R>,
+    skeleton_die: Option<&DebuggingInformationEntry<R>>,
     addr_tr: &AddressTransform,
     out_encoding: gimli::Encoding,
     debug_str: &DebugStr<R>,
-    debug_str_offsets: &DebugStrOffsets<R>,
-    debug_line_str: &DebugLineStr<R>,
     debug_line: &DebugLine<R>,
     out_strings: &mut write::StringTable,
 ) -> Result<(write::LineProgram, DebugLineOffset, Vec<write::FileId>, u64), Error>
 where
     R: Reader,
 {
-    let offset = match root.attr_value(gimli::DW_AT_stmt_list)? {
-        Some(gimli::AttributeValue::DebugLineRef(offset)) => offset,
+    // Where are the "location" attributes
+    let (location_die, location_dwarf) = match skeleton_die {
+        Some(die) => (die, skeleton_dwarf),
+        _ => (root, dwarf),
+    };
+
+    let offset = match location_die.attr_value(gimli::DW_AT_stmt_list)? {
+        Some(DebugLineRef(offset)) => offset,
+        Some(gimli::AttributeValue::SecOffset(offset)) => DebugLineOffset(offset),
         _ => {
             return Err(TransformError("Debug line offset is not found").into());
         }
     };
-    let comp_dir = root.attr_value(gimli::DW_AT_comp_dir)?;
+    let comp_dir = location_die.attr_value(gimli::DW_AT_comp_dir)?;
     let comp_name = root.attr_value(gimli::DW_AT_name)?;
     let out_comp_dir = match &comp_dir {
         Some(comp_dir) => Some(clone_attr_string(
             comp_dir,
             gimli::DW_FORM_strp,
             unit,
-            debug_str,
-            debug_str_offsets,
-            debug_line_str,
+            location_dwarf,
             out_strings,
         )?),
         None => None,
     };
-    let out_comp_name = clone_attr_string(
-        comp_name.as_ref().context("missing DW_AT_name attribute")?,
-        gimli::DW_FORM_strp,
-        unit,
-        debug_str,
-        debug_str_offsets,
-        debug_line_str,
-        out_strings,
-    )?;
+    let out_comp_name = match comp_name {
+        Some(_) => clone_attr_string(
+            comp_name
+                .as_ref()
+                .context("failed to read DW_AT_name attribute")?,
+            gimli::DW_FORM_strp,
+            unit,
+            dwarf,
+            out_strings,
+        )?,
+        _ => gimli::write::LineString::String("missing DW_AT_name attribute".into()),
+    };
 
     let program = debug_line.program(
         offset,
@@ -114,9 +123,7 @@ where
                 dir_attr,
                 gimli::DW_FORM_string,
                 unit,
-                debug_str,
-                debug_str_offsets,
-                debug_line_str,
+                location_dwarf,
                 out_strings,
             )?);
             dirs.push(dir_id);
@@ -132,9 +139,7 @@ where
                     &file_entry.path_name(),
                     gimli::DW_FORM_string,
                     unit,
-                    debug_str,
-                    debug_str_offsets,
-                    debug_line_str,
+                    location_dwarf,
                     out_strings,
                 )?,
                 dir_id,
diff --git a/crates/cranelift/src/debug/transform/mod.rs b/crates/cranelift/src/debug/transform/mod.rs
index 8a0b3e3cc..788550d0e 100644
--- a/crates/cranelift/src/debug/transform/mod.rs
+++ b/crates/cranelift/src/debug/transform/mod.rs
@@ -7,12 +7,12 @@ use crate::CompiledFunctionsMetadata;
 use anyhow::Error;
 use cranelift_codegen::isa::TargetIsa;
 use gimli::{
-    write, DebugAddr, DebugLine, DebugLineStr, DebugStr, DebugStrOffsets, LocationLists,
-    RangeLists, UnitSectionOffset,
+    write, DebugAddr, DebugLine, DebugStr, Dwarf, DwarfPackage, LittleEndian, LocationLists,
+    RangeLists, Section, Unit, UnitSectionOffset,
 };
-use std::collections::HashSet;
+use std::{collections::HashSet, fmt::Debug};
 use thiserror::Error;
-use wasmtime_environ::DebugInfoData;
+use wasmtime_environ::{DebugInfoData, ModuleTranslation, Tunables};
 
 pub use address_transform::AddressTransform;
 
@@ -26,9 +26,12 @@ mod simulate;
 mod unit;
 mod utils;
 
-pub(crate) trait Reader: gimli::Reader<Offset = usize> {}
+pub(crate) trait Reader: gimli::Reader<Offset = usize> + Send + Sync {}
 
-impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where Endian: gimli::Endianity {}
+impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where
+    Endian: gimli::Endianity + Send + Sync
+{
+}
 
 #[derive(Error, Debug)]
 #[error("Debug info transform error: {0}")]
@@ -39,8 +42,6 @@ where
     R: Reader,
 {
     debug_str: &'a DebugStr<R>,
-    debug_str_offsets: &'a DebugStrOffsets<R>,
-    debug_line_str: &'a DebugLineStr<R>,
     debug_line: &'a DebugLine<R>,
     debug_addr: &'a DebugAddr<R>,
     rnglists: &'a RangeLists<R>,
@@ -48,19 +49,113 @@ where
     reachable: &'a HashSet<UnitSectionOffset>,
 }
 
-pub fn transform_dwarf(
+fn load_dwp<'data>(
+    translation: ModuleTranslation<'data>,
+    buffer: &'data [u8],
+) -> anyhow::Result<DwarfPackage<gimli::EndianSlice<'data, gimli::LittleEndian>>> {
+    let endian_slice = gimli::EndianSlice::new(buffer, LittleEndian);
+
+    let dwarf_package = DwarfPackage::load(
+        |id| -> anyhow::Result<_> {
+            let slice = match id {
+                gimli::SectionId::DebugAbbrev => {
+                    translation.debuginfo.dwarf.debug_abbrev.reader().slice()
+                }
+                gimli::SectionId::DebugInfo => {
+                    translation.debuginfo.dwarf.debug_info.reader().slice()
+                }
+                gimli::SectionId::DebugLine => {
+                    translation.debuginfo.dwarf.debug_line.reader().slice()
+                }
+                gimli::SectionId::DebugStr => {
+                    translation.debuginfo.dwarf.debug_str.reader().slice()
+                }
+                gimli::SectionId::DebugStrOffsets => translation
+                    .debuginfo
+                    .dwarf
+                    .debug_str_offsets
+                    .reader()
+                    .slice(),
+                gimli::SectionId::DebugLoc => translation.debuginfo.debug_loc.reader().slice(),
+                gimli::SectionId::DebugLocLists => {
+                    translation.debuginfo.debug_loclists.reader().slice()
+                }
+                gimli::SectionId::DebugRngLists => {
+                    translation.debuginfo.debug_rnglists.reader().slice()
+                }
+                gimli::SectionId::DebugTypes => {
+                    translation.debuginfo.dwarf.debug_types.reader().slice()
+                }
+                gimli::SectionId::DebugCuIndex => {
+                    translation.debuginfo.debug_cu_index.reader().slice()
+                }
+                gimli::SectionId::DebugTuIndex => {
+                    translation.debuginfo.debug_tu_index.reader().slice()
+                }
+                _ => &buffer,
+            };
+
+            Ok(gimli::EndianSlice::new(slice, gimli::LittleEndian))
+        },
+        endian_slice,
+    )?;
+
+    Ok(dwarf_package)
+}
+
+/// Attempts to load a DWARF package using the passed bytes.
+fn read_dwarf_package_from_bytes<'data>(
+    dwp_bytes: &'data [u8],
+    buffer: &'data [u8],
+    tunables: &Tunables,
+) -> Option<DwarfPackage<gimli::EndianSlice<'data, gimli::LittleEndian>>> {
+    let mut validator = wasmparser::Validator::new();
+    let parser = wasmparser::Parser::new(0);
+    let mut types = wasmtime_environ::ModuleTypesBuilder::new(&validator);
+    let translation =
+        match wasmtime_environ::ModuleEnvironment::new(tunables, &mut validator, &mut types)
+            .translate(parser, dwp_bytes)
+        {
+            Ok(translation) => translation,
+            Err(e) => {
+                log::warn!("failed to parse wasm dwarf package: {e:?}");
+                return None;
+            }
+        };
+
+    match load_dwp(translation, buffer) {
+        Ok(package) => Some(package),
+        Err(err) => {
+            log::warn!("Failed to load Dwarf package {}", err);
+            None
+        }
+    }
+}
+
+pub fn transform_dwarf<'data>(
     isa: &dyn TargetIsa,
     di: &DebugInfoData,
     funcs: &CompiledFunctionsMetadata,
     memory_offset: &ModuleMemoryOffset,
+    dwarf_package_bytes: Option<&[u8]>,
+    tunables: &Tunables,
 ) -> Result<write::Dwarf, Error> {
     let addr_tr = AddressTransform::new(funcs, &di.wasm_file);
-    let reachable = build_dependencies(&di.dwarf, &addr_tr)?.get_reachable();
+
+    let buffer = Vec::new();
+
+    let dwarf_package = dwarf_package_bytes
+        .map(
+            |bytes| -> Option<DwarfPackage<gimli::EndianSlice<'_, gimli::LittleEndian>>> {
+                read_dwarf_package_from_bytes(bytes, &buffer, tunables)
+            },
+        )
+        .flatten();
+
+    let reachable = build_dependencies(&di.dwarf, &dwarf_package, &addr_tr)?.get_reachable();
 
     let context = DebugInputContext {
         debug_str: &di.dwarf.debug_str,
-        debug_str_offsets: &di.dwarf.debug_str_offsets,
-        debug_line_str: &di.dwarf.debug_line_str,
         debug_line: &di.dwarf.debug_line,
         debug_addr: &di.dwarf.debug_addr,
         rnglists: &di.dwarf.ranges,
@@ -84,11 +179,29 @@ pub fn transform_dwarf(
 
     let mut translated = HashSet::new();
     let mut iter = di.dwarf.debug_info.units();
+
     while let Some(header) = iter.next().unwrap_or(None) {
         let unit = di.dwarf.unit(header)?;
+
+        let mut resolved_unit = None;
+        let mut split_dwarf = None;
+
+        if let gimli::UnitType::Skeleton(_dwo_id) = unit.header.type_() {
+            if let Some(dwarf_package) = &dwarf_package {
+                if let Some((fused, fused_dwarf)) =
+                    replace_unit_from_split_dwarf(&unit, dwarf_package, &di.dwarf)
+                {
+                    resolved_unit = Some(fused);
+                    split_dwarf = Some(fused_dwarf);
+                }
+            }
+        }
+
         if let Some((id, ref_map, pending_refs)) = clone_unit(
             &di.dwarf,
-            unit,
+            &unit,
+            resolved_unit.as_ref(),
+            split_dwarf.as_ref(),
             &context,
             &addr_tr,
             funcs,
@@ -124,3 +237,20 @@ pub fn transform_dwarf(
         strings: out_strings,
     })
 }
+
+fn replace_unit_from_split_dwarf<'a>(
+    unit: &'a Unit<gimli::EndianSlice<'a, gimli::LittleEndian>, usize>,
+    dwp: &DwarfPackage<gimli::EndianSlice<'a, gimli::LittleEndian>>,
+    parent: &Dwarf<gimli::EndianSlice<'a, gimli::LittleEndian>>,
+) -> Option<(
+    Unit<gimli::EndianSlice<'a, gimli::LittleEndian>, usize>,
+    Dwarf<gimli::EndianSlice<'a, gimli::LittleEndian>>,
+)> {
+    let dwo_id = unit.dwo_id?;
+    let split_unit_dwarf = dwp.find_cu(dwo_id, parent).ok()??;
+    let unit_header = split_unit_dwarf.debug_info.units().next().ok()??;
+    Some((
+        split_unit_dwarf.unit(unit_header).unwrap(),
+        split_unit_dwarf,
+    ))
+}
diff --git a/crates/cranelift/src/debug/transform/unit.rs b/crates/cranelift/src/debug/transform/unit.rs
index eefed56d0..5612dbdf9 100644
--- a/crates/cranelift/src/debug/transform/unit.rs
+++ b/crates/cranelift/src/debug/transform/unit.rs
@@ -5,7 +5,7 @@ use super::line_program::clone_line_program;
 use super::range_info_builder::RangeInfoBuilder;
 use super::refs::{PendingDebugInfoRefs, PendingUnitRefs, UnitRefsMap};
 use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info};
-use super::{DebugInputContext, Reader, TransformError};
+use super::{DebugInputContext, Reader};
 use crate::debug::ModuleMemoryOffset;
 use crate::CompiledFunctionsMetadata;
 use anyhow::{Context, Error};
@@ -256,7 +256,9 @@ fn is_dead_code<R: Reader>(entry: &DebuggingInformationEntry<R>) -> bool {
 
 pub(crate) fn clone_unit<'a, R>(
     dwarf: &gimli::Dwarf<R>,
-    unit: Unit<R, R::Offset>,
+    unit: &Unit<R, R::Offset>,
+    split_unit: Option<&Unit<R, R::Offset>>,
+    split_dwarf: Option<&gimli::Dwarf<R>>,
     context: &DebugInputContext<R>,
     addr_tr: &'a AddressTransform,
     funcs: &'a CompiledFunctionsMetadata,
@@ -275,20 +277,35 @@ where
     let mut pending_di_refs = PendingDebugInfoRefs::new();
     let mut stack = Vec::new();
 
+    let mut program_unit = unit;
+    let mut skeleton_die = None;
+
+    // Get entries in outer scope to avoid borrowing on short lived temporary.
+    let mut skeleton_entries = unit.entries();
+    if let Some(unit) = split_unit {
+        program_unit = unit;
+
+        // From the spec, a skeleton unit has no children so we can assume the first, and only, entry is the DW_TAG_skeleton_unit (https://dwarfstd.org/doc/DWARF5.pdf).
+        if let Some(die_tuple) = skeleton_entries.next_dfs()? {
+            skeleton_die = Some(die_tuple.1);
+        }
+    }
+
     // Iterate over all of this compilation unit's entries.
-    let mut entries = unit.entries();
+    let mut entries = program_unit.entries();
     let (mut comp_unit, unit_id, file_map, file_index_base, cu_low_pc, wp_die_id, vmctx_die_id) =
         if let Some((depth_delta, entry)) = entries.next_dfs()? {
             assert_eq!(depth_delta, 0);
             let (out_line_program, debug_line_offset, file_map, file_index_base) =
                 clone_line_program(
-                    &unit,
+                    split_dwarf.unwrap_or(dwarf),
+                    dwarf,
+                    program_unit,
                     entry,
+                    skeleton_die,
                     addr_tr,
                     out_encoding,
                     context.debug_str,
-                    context.debug_str_offsets,
-                    context.debug_line_str,
                     context.debug_line,
                     out_strings,
                 )?;
@@ -299,23 +316,11 @@ where
 
                 let root_id = comp_unit.root();
                 die_ref_map.insert(entry.offset(), root_id);
-
-                let cu_low_pc = if let Some(AttributeValue::Addr(addr)) =
-                    entry.attr_value(gimli::DW_AT_low_pc)?
-                {
-                    addr
-                } else if let Some(AttributeValue::DebugAddrIndex(i)) =
-                    entry.attr_value(gimli::DW_AT_low_pc)?
-                {
-                    context.debug_addr.get_address(4, unit.addr_base, i)?
-                } else {
-                    // FIXME? return Err(TransformError("No low_pc for unit header").into());
-                    0
-                };
+                let cu_low_pc = unit.low_pc;
 
                 clone_die_attributes(
-                    dwarf,
-                    &unit,
+                    split_dwarf.unwrap_or(dwarf),
+                    &program_unit,
                     entry,
                     context,
                     addr_tr,
@@ -346,7 +351,9 @@ where
                     vmctx_die_id,
                 )
             } else {
-                return Err(TransformError("Unexpected unit header").into());
+                // Can happen when the DWARF is split and we dont have the package/dwo files.
+                // This is a better user experience than errorring.
+                return Ok(None); // empty:
             }
         } else {
             return Ok(None); // empty
@@ -469,7 +476,7 @@ where
         die_ref_map.insert(entry.offset(), die_id);
 
         clone_die_attributes(
-            dwarf,
+            split_dwarf.unwrap_or(dwarf),
             &unit,
             entry,
             context,
diff --git a/crates/cranelift/src/debug/write_debuginfo.rs b/crates/cranelift/src/debug/write_debuginfo.rs
index e5e6269f8..35b9fed72 100644
--- a/crates/cranelift/src/debug/write_debuginfo.rs
+++ b/crates/cranelift/src/debug/write_debuginfo.rs
@@ -179,8 +179,17 @@ pub fn emit_dwarf<'a>(
     debuginfo_data: &DebugInfoData,
     funcs: &CompiledFunctionsMetadata,
     memory_offset: &ModuleMemoryOffset,
+    dwarf_package_bytes: Option<&[u8]>,
+    tunables: &wasmtime_environ::Tunables,
 ) -> anyhow::Result<Vec<DwarfSection>> {
-    let dwarf = transform_dwarf(isa, debuginfo_data, funcs, memory_offset)?;
+    let dwarf = transform_dwarf(
+        isa,
+        debuginfo_data,
+        funcs,
+        memory_offset,
+        dwarf_package_bytes,
+        tunables,
+    )?;
     let frame_table = create_frame_table(isa, funcs);
     let sections = emit_dwarf_sections(isa, dwarf, frame_table)?;
     Ok(sections)
diff --git a/crates/environ/src/compile/mod.rs b/crates/environ/src/compile/mod.rs
index 993a6c6cd..a88c0f32b 100644
--- a/crates/environ/src/compile/mod.rs
+++ b/crates/environ/src/compile/mod.rs
@@ -377,6 +377,8 @@ pub trait Compiler: Send + Sync {
         obj: &mut Object<'_>,
         translation: &ModuleTranslation<'_>,
         funcs: &PrimaryMap<DefinedFuncIndex, (SymbolId, &(dyn Any + Send))>,
+        dwarf_package_bytes: Option<&[u8]>,
+        tunables: &Tunables,
     ) -> Result<()>;
 
     /// Creates a new System V Common Information Entry for the ISA.
diff --git a/crates/environ/src/compile/module_artifacts.rs b/crates/environ/src/compile/module_artifacts.rs
index 7210f9139..68f19e377 100644
--- a/crates/environ/src/compile/module_artifacts.rs
+++ b/crates/environ/src/compile/module_artifacts.rs
@@ -2,8 +2,8 @@
 //! with `bincode` as part of a module's compilation process.
 
 use crate::{
-    obj, CompiledFunctionInfo, CompiledModuleInfo, DefinedFuncIndex, FunctionLoc, FunctionName,
-    MemoryInitialization, Metadata, ModuleTranslation, PrimaryMap, Tunables,
+    obj, CompiledFunctionInfo, CompiledModuleInfo, DebugInfoData, DefinedFuncIndex, FunctionLoc,
+    FunctionName, MemoryInitialization, Metadata, ModuleTranslation, PrimaryMap, Tunables,
 };
 use anyhow::{bail, Result};
 use object::write::{Object, SectionId, StandardSegment, WritableBuffer};
@@ -57,6 +57,31 @@ impl<'a> ObjectBuilder<'a> {
         }
     }
 
+    /// Insert the wasm raw wasm-based debuginfo into the output.
+    /// Note that this is distinct from the native debuginfo
+    /// possibly generated by the native compiler, hence these sections
+    /// getting wasm-specific names.
+    pub fn push_debuginfo(
+        &mut self,
+        dwarf: &mut Vec<(u8, Range<u64>)>,
+        debuginfo: &DebugInfoData<'_>,
+    ) {
+        self.push_debug(dwarf, &debuginfo.dwarf.debug_abbrev);
+        self.push_debug(dwarf, &debuginfo.dwarf.debug_addr);
+        self.push_debug(dwarf, &debuginfo.dwarf.debug_aranges);
+        self.push_debug(dwarf, &debuginfo.dwarf.debug_info);
+        self.push_debug(dwarf, &debuginfo.dwarf.debug_line);
+        self.push_debug(dwarf, &debuginfo.dwarf.debug_line_str);
+        self.push_debug(dwarf, &debuginfo.dwarf.debug_str);
+        self.push_debug(dwarf, &debuginfo.dwarf.debug_str_offsets);
+        self.push_debug(dwarf, &debuginfo.debug_ranges);
+        self.push_debug(dwarf, &debuginfo.debug_rnglists);
+        self.push_debug(dwarf, &debuginfo.debug_cu_index);
+
+        // Sort this for binary-search-lookup later in `symbolize_context`.
+        dwarf.sort_by_key(|(id, _)| *id);
+    }
+
     /// Completes compilation of the `translation` specified, inserting
     /// everything necessary into the `Object` being built.
     ///
@@ -188,19 +213,8 @@ impl<'a> ObjectBuilder<'a> {
         // getting wasm-specific names.
         let mut dwarf = Vec::new();
         if self.tunables.parse_wasm_debuginfo {
-            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);
+            self.push_debuginfo(&mut dwarf, &debuginfo);
         }
-        // Sort this for binary-search-lookup later in `symbolize_context`.
-        dwarf.sort_by_key(|(id, _)| *id);
 
         Ok(CompiledModuleInfo {
             module,
diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs
index 0c119ae74..fd86fbc48 100644
--- a/crates/environ/src/module_environ.rs
+++ b/crates/environ/src/module_environ.rs
@@ -122,10 +122,12 @@ pub struct DebugInfoData<'a> {
     pub dwarf: Dwarf<'a>,
     pub name_section: NameSection<'a>,
     pub wasm_file: WasmFileInfo,
-    debug_loc: gimli::DebugLoc<Reader<'a>>,
-    debug_loclists: gimli::DebugLocLists<Reader<'a>>,
+    pub debug_loc: gimli::DebugLoc<Reader<'a>>,
+    pub debug_loclists: gimli::DebugLocLists<Reader<'a>>,
     pub debug_ranges: gimli::DebugRanges<Reader<'a>>,
     pub debug_rnglists: gimli::DebugRngLists<Reader<'a>>,
+    pub debug_cu_index: gimli::DebugCuIndex<Reader<'a>>,
+    pub debug_tu_index: gimli::DebugTuIndex<Reader<'a>>,
 }
 
 #[allow(missing_docs)]
@@ -782,7 +784,7 @@ and for re-adding support for interface types you can see this issue:
     }
 
     fn register_dwarf_section(&mut self, section: &CustomSectionReader<'data>) {
-        let name = section.name();
+        let name = section.name().trim_end_matches(".dwo");
         if !name.starts_with(".debug_") {
             return;
         }
@@ -800,7 +802,9 @@ and for re-adding support for interface types you can see this issue:
             // `gimli::Dwarf` fields.
             ".debug_abbrev" => dwarf.debug_abbrev = gimli::DebugAbbrev::new(data, endian),
             ".debug_addr" => dwarf.debug_addr = gimli::DebugAddr::from(slice),
-            ".debug_info" => dwarf.debug_info = gimli::DebugInfo::new(data, endian),
+            ".debug_info" => {
+                dwarf.debug_info = gimli::DebugInfo::new(data, endian);
+            }
             ".debug_line" => dwarf.debug_line = gimli::DebugLine::new(data, endian),
             ".debug_line_str" => dwarf.debug_line_str = gimli::DebugLineStr::from(slice),
             ".debug_str" => dwarf.debug_str = gimli::DebugStr::new(data, endian),
@@ -818,9 +822,12 @@ and for re-adding support for interface types you can see this issue:
             ".debug_ranges" => info.debug_ranges = gimli::DebugRanges::new(data, endian),
             ".debug_rnglists" => info.debug_rnglists = gimli::DebugRngLists::new(data, endian),
 
+            // DWARF package fields
+            ".debug_cu_index" => info.debug_cu_index = gimli::DebugCuIndex::new(data, endian),
+            ".debug_tu_index" => info.debug_tu_index = gimli::DebugTuIndex::new(data, endian),
+
             // We don't use these at the moment.
             ".debug_aranges" | ".debug_pubnames" | ".debug_pubtypes" => return,
-
             other => {
                 log::warn!("unknown debug section `{}`", other);
                 return;
diff --git a/crates/wasmtime/src/compile.rs b/crates/wasmtime/src/compile.rs
index 15e316e31..5ea9e33bc 100644
--- a/crates/wasmtime/src/compile.rs
+++ b/crates/wasmtime/src/compile.rs
@@ -29,6 +29,7 @@ use std::{
     collections::{btree_map, BTreeMap, BTreeSet, HashMap, HashSet},
     mem,
 };
+
 #[cfg(feature = "component-model")]
 use wasmtime_environ::component::Translator;
 use wasmtime_environ::{
@@ -61,6 +62,7 @@ pub use self::runtime::finish_object;
 pub(crate) fn build_artifacts<T: FinishedObject>(
     engine: &Engine,
     wasm: &[u8],
+    dwarf_package: Option<&[u8]>,
 ) -> Result<(T, Option<(CompiledModuleInfo, ModuleTypes)>)> {
     let tunables = engine.tunables();
 
@@ -101,6 +103,7 @@ pub(crate) fn build_artifacts<T: FinishedObject>(
         engine,
         compiled_funcs,
         std::iter::once(translation).collect(),
+        dwarf_package,
     )?;
 
     let info = compilation_artifacts.unwrap_as_module_info();
@@ -118,9 +121,10 @@ pub(crate) fn build_artifacts<T: FinishedObject>(
 /// The output artifact here is the serialized object file contained within
 /// an owned mmap along with metadata about the compilation itself.
 #[cfg(feature = "component-model")]
-pub(crate) fn build_component_artifacts<T: FinishedObject>(
+pub(crate) fn build_component_artifacts<'a, T: FinishedObject>(
     engine: &Engine,
     binary: &[u8],
+    _dwarf_package: Option<&[u8]>,
 ) -> Result<(T, Option<wasmtime_environ::component::ComponentArtifacts>)> {
     use wasmtime_environ::component::{
         CompiledComponentInfo, ComponentArtifacts, ComponentTypesBuilder,
@@ -159,6 +163,7 @@ pub(crate) fn build_component_artifacts<T: FinishedObject>(
         engine,
         compiled_funcs,
         module_translations,
+        None, // TODO: Support dwarf packages for components.
     )?;
     let (types, ty) = types.finish(
         &compilation_artifacts.modules,
@@ -649,6 +654,7 @@ impl FunctionIndices {
         engine: &'a Engine,
         compiled_funcs: Vec<(String, Box<dyn Any + Send>)>,
         translations: PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
+        dwarf_package_bytes: Option<&[u8]>,
     ) -> Result<(wasmtime_environ::ObjectBuilder<'a>, Artifacts)> {
         // Append all the functions to the ELF file.
         //
@@ -713,7 +719,13 @@ impl FunctionIndices {
                     })
                     .collect();
                 if !funcs.is_empty() {
-                    compiler.append_dwarf(&mut obj, translation, &funcs)?;
+                    compiler.append_dwarf(
+                        &mut obj,
+                        translation,
+                        &funcs,
+                        dwarf_package_bytes,
+                        tunables,
+                    )?;
                 }
             }
         }
diff --git a/crates/wasmtime/src/compile/code_builder.rs b/crates/wasmtime/src/compile/code_builder.rs
index 00b456d53..58afd663f 100644
--- a/crates/wasmtime/src/compile/code_builder.rs
+++ b/crates/wasmtime/src/compile/code_builder.rs
@@ -40,6 +40,8 @@ pub struct CodeBuilder<'a> {
     pub(super) engine: &'a Engine,
     wasm: Option<Cow<'a, [u8]>>,
     wasm_path: Option<Cow<'a, Path>>,
+    dwarf_package: Option<Cow<'a, [u8]>>,
+    dwarf_package_path: Option<Cow<'a, Path>>,
     wat: bool,
 }
 
@@ -51,6 +53,8 @@ impl<'a> CodeBuilder<'a> {
             engine,
             wasm: None,
             wasm_path: None,
+            dwarf_package: None,
+            dwarf_package_path: None,
             wat: cfg!(feature = "wat"),
         }
     }
@@ -76,6 +80,11 @@ impl<'a> CodeBuilder<'a> {
         }
         self.wasm = Some(wasm_bytes.into());
         self.wasm_path = wasm_path.map(|p| p.into());
+
+        if self.wasm_path.is_some() {
+            self.dwarf_package_from_wasm_path()?;
+        }
+
         Ok(self)
     }
 
@@ -104,6 +113,10 @@ impl<'a> CodeBuilder<'a> {
     /// either as a WebAssembly binary or as a WebAssembly text file. The
     /// contents are inspected to do this, the file extension is not consulted.
     ///
+    /// A DWARF package file will be probed using the root of `file` and with a
+    /// `.dwp` extension.  If found, it will be loaded and DWARF fusion
+    /// performed.
+    ///
     /// # Errors
     ///
     /// If wasm bytes have already been configured via a call to this method or
@@ -111,6 +124,9 @@ impl<'a> CodeBuilder<'a> {
     ///
     /// If `file` can't be read or an error happens reading it then that will
     /// also be returned.
+    ///
+    /// If DWARF fusion is performed and the DWARF packaged file cannot be read
+    /// then an error will be returned.
     pub fn wasm_file(&mut self, file: &'a Path) -> Result<&mut Self> {
         if self.wasm.is_some() {
             bail!("cannot call `wasm` or `wasm_file` twice");
@@ -119,6 +135,8 @@ impl<'a> CodeBuilder<'a> {
             .with_context(|| format!("failed to read input file: {}", file.display()))?;
         self.wasm = Some(wasm.into());
         self.wasm_path = Some(file.into());
+        self.dwarf_package_from_wasm_path()?;
+
         Ok(self)
     }
 
@@ -139,6 +157,61 @@ impl<'a> CodeBuilder<'a> {
         Ok((&wasm[..]).into())
     }
 
+    /// Explicitly specify DWARF `.dwp` path.
+    ///
+    /// # Errors
+    ///
+    /// This method will return an error if the `.dwp` file has already been set
+    /// through [`CodeBuilder::dwarf_package`] or auto-detection in
+    /// [`CodeBuilder::wasm_file`].
+    ///
+    /// This method will also return an error if `file` cannot be read.
+    pub fn dwarf_package_file(&mut self, file: &Path) -> Result<&mut Self> {
+        if self.dwarf_package.is_some() {
+            bail!("cannot call `dwarf_package` or `dwarf_package_file` twice");
+        }
+
+        let dwarf_package = std::fs::read(file)
+            .with_context(|| format!("failed to read dwarf input file: {}", file.display()))?;
+        self.dwarf_package_path = Some(Cow::Owned(file.to_owned()));
+        self.dwarf_package = Some(dwarf_package.into());
+
+        Ok(self)
+    }
+
+    fn dwarf_package_from_wasm_path(&mut self) -> Result<&mut Self> {
+        let dwarf_package_path_buf = self.wasm_path.as_ref().unwrap().with_extension("dwp");
+        if dwarf_package_path_buf.exists() {
+            return self.dwarf_package_file(dwarf_package_path_buf.as_path());
+        }
+
+        Ok(self)
+    }
+
+    /// Gets the DWARF package.
+    pub(super) fn dwarf_package_binary(&self) -> Option<&[u8]> {
+        return self.dwarf_package.as_deref();
+    }
+
+    /// Set the DWARF package binary.
+    ///
+    /// Initializes `dwarf_package` from `dwp_bytes` in preparation for
+    /// DWARF fusion. Allows the DWARF package to be supplied as a byte array
+    /// when the file probing performed in `wasm_file` is not appropriate.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the `*.dwp` file is already set via auto-probing in
+    /// [`CodeBuilder::wasm_file`] or explicitly via
+    /// [`CodeBuilder::dwarf_package_file`].
+    pub fn dwarf_package(&mut self, dwp_bytes: &'a [u8]) -> Result<&mut Self> {
+        if self.dwarf_package.is_some() {
+            bail!("cannot call `dwarf_package` or `dwarf_package_file` twice");
+        }
+        self.dwarf_package = Some(dwp_bytes.into());
+        Ok(self)
+    }
+
     /// Finishes this compilation and produces a serialized list of bytes.
     ///
     /// This method requires that either [`CodeBuilder::wasm`] or
@@ -157,7 +230,8 @@ impl<'a> CodeBuilder<'a> {
     /// compilation-related error is encountered.
     pub fn compile_module_serialized(&self) -> Result<Vec<u8>> {
         let wasm = self.wasm_binary()?;
-        let (v, _) = super::build_artifacts(self.engine, &wasm)?;
+        let dwarf_package = self.dwarf_package_binary();
+        let (v, _) = super::build_artifacts(self.engine, &wasm, dwarf_package.as_deref())?;
         Ok(v)
     }
 
@@ -168,7 +242,7 @@ impl<'a> CodeBuilder<'a> {
     #[cfg_attr(docsrs, doc(cfg(feature = "component-model")))]
     pub fn compile_component_serialized(&self) -> Result<Vec<u8>> {
         let bytes = self.wasm_binary()?;
-        let (v, _) = super::build_component_artifacts(self.engine, &bytes)?;
+        let (v, _) = super::build_component_artifacts(self.engine, &bytes, None)?;
         Ok(v)
     }
 }
diff --git a/crates/wasmtime/src/compile/runtime.rs b/crates/wasmtime/src/compile/runtime.rs
index fde8dfcdf..0887cfb36 100644
--- a/crates/wasmtime/src/compile/runtime.rs
+++ b/crates/wasmtime/src/compile/runtime.rs
@@ -11,9 +11,10 @@ use wasmtime_runtime::MmapVec;
 impl<'a> CodeBuilder<'a> {
     fn compile_cached<T>(
         &self,
-        build_artifacts: fn(&Engine, &[u8]) -> Result<(MmapVecWrapper, Option<T>)>,
+        build_artifacts: fn(&Engine, &[u8], Option<&[u8]>) -> Result<(MmapVecWrapper, Option<T>)>,
     ) -> Result<(Arc<CodeMemory>, Option<T>)> {
         let wasm = self.wasm_binary()?;
+        let dwarf_package = self.dwarf_package_binary();
 
         self.engine
             .check_compatible_with_native_host()
@@ -24,6 +25,7 @@ impl<'a> CodeBuilder<'a> {
             let state = (
                 HashedEngineCompileEnv(self.engine),
                 &wasm,
+                &dwarf_package,
                 // Don't hash this as it's just its own "pure" function pointer.
                 NotHashed(build_artifacts),
             );
@@ -32,15 +34,18 @@ impl<'a> CodeBuilder<'a> {
                     .get_data_raw(
                         &state,
                         // Cache miss, compute the actual artifacts
-                        |(engine, wasm, build_artifacts)| -> Result<_> {
-                            let (mmap, info) = (build_artifacts.0)(engine.0, wasm)?;
+                        |(engine, wasm, dwarf_package, build_artifacts)| -> Result<_> {
+                            let (mmap, info) =
+                                (build_artifacts.0)(engine.0, wasm, dwarf_package.as_deref())?;
                             let code = publish_mmap(mmap.0)?;
                             Ok((code, info))
                         },
                         // Implementation of how to serialize artifacts
-                        |(_engine, _wasm, _), (code, _info_and_types)| Some(code.mmap().to_vec()),
+                        |(_engine, _wasm, _, _), (code, _info_and_types)| {
+                            Some(code.mmap().to_vec())
+                        },
                         // Cache hit, deserialize the provided artifacts
-                        |(engine, wasm, _), serialized_bytes| {
+                        |(engine, wasm, _, _), serialized_bytes| {
                             let kind = if wasmparser::Parser::is_component(&wasm) {
                                 ObjectKind::Component
                             } else {
@@ -55,7 +60,8 @@ impl<'a> CodeBuilder<'a> {
 
         #[cfg(not(feature = "cache"))]
         {
-            let (mmap, info_and_types) = build_artifacts(self.engine, &wasm)?;
+            let (mmap, info_and_types) =
+                build_artifacts(self.engine, &wasm, dwarf_package.as_deref())?;
             let code = publish_mmap(mmap.0)?;
             return Ok((code, info_and_types));
         }
diff --git a/crates/wasmtime/src/runtime/module.rs b/crates/wasmtime/src/runtime/module.rs
index a0217c768..04ecc47f1 100644
--- a/crates/wasmtime/src/runtime/module.rs
+++ b/crates/wasmtime/src/runtime/module.rs
@@ -23,7 +23,6 @@ use wasmtime_runtime::{
     CompiledModuleId, MemoryImage, MmapVec, ModuleMemoryImages, VMArrayCallFunction,
     VMNativeCallFunction, VMWasmCallFunction,
 };
-
 mod registry;
 
 pub use registry::{
diff --git a/crates/winch/src/compiler.rs b/crates/winch/src/compiler.rs
index d59c7e713..ecb93dacd 100644
--- a/crates/winch/src/compiler.rs
+++ b/crates/winch/src/compiler.rs
@@ -233,6 +233,8 @@ impl wasmtime_environ::Compiler for Compiler {
         _obj: &mut Object<'_>,
         _translation: &ModuleTranslation<'_>,
         _funcs: &PrimaryMap<DefinedFuncIndex, (SymbolId, &(dyn Any + Send))>,
+        _dwarf_package: Option<&[u8]>,
+        _tunables: &wasmtime_environ::Tunables,
     ) -> Result<()> {
         todo!()
     }


Test patch:
diff --git a/tests/all/debug/lldb.rs b/tests/all/debug/lldb.rs
index 14c093b31..b52a6f6aa 100644
--- a/tests/all/debug/lldb.rs
+++ b/tests/all/debug/lldb.rs
@@ -247,3 +247,51 @@ check: exited with status
     )?;
     Ok(())
 }
+
+/* C program used for this test, dwarf_fission.c, compiled with `emcc dwarf_fission.c -o dwarf_fission.wasm -gsplit-dwarf -gdwarf-5 -gpubnames -sWASM_BIGINT`:
+#include <stdio.h>
+
+int main()
+{
+    int i = 1;
+    i++;
+    return i - 2;
+}
+ */
+#[test]
+#[ignore]
+#[cfg(all(
+    any(target_os = "linux", target_os = "macos"),
+    target_pointer_width = "64"
+))]
+pub fn test_debug_dwarf5_fission_lldb() -> Result<()> {
+    let output = lldb_with_script(
+        &[
+            "-Ccache=n",
+            "-Ddebug-info",
+            "tests/all/debug/testsuite/dwarf_fission.wasm",
+        ],
+        r#"breakpoint set --file dwarf_fission.c --line 6
+r
+fr v
+s
+fr v
+c"#,
+    )?;
+
+    check_lldb_output(
+        &output,
+        r#"
+check: Breakpoint 1: no locations (pending)
+check: Unable to resolve breakpoint to any actual locations.
+check: 1 location added to breakpoint 1
+check: stop reason = breakpoint 1.1
+check: i = 1
+check: stop reason = step in
+check: i = 2
+check: resuming
+check: exited with status = 0
+"#,
+    )?;
+    Ok(())
+}
diff --git a/tests/all/debug/testsuite/dwarf_fission.c b/tests/all/debug/testsuite/dwarf_fission.c
new file mode 100644
index 000000000..4dc174f90
--- /dev/null
+++ b/tests/all/debug/testsuite/dwarf_fission.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main() {
+  int i = 1;
+  i++;
+  return i - 2;
+}