Repository: gimli-rs/addr2line
Pull request #263 (2023-02-03 05:50:53)

Refactor split DWARF support
See individual commits.

cc @khuey 


Patch:
diff --git a/benches/bench.rs b/benches/bench.rs
index 64ce5f4..a4080d0 100644
--- a/benches/bench.rs
+++ b/benches/bench.rs
@@ -10,7 +10,6 @@ use std::env;
 use std::fs::File;
 use std::path::{self, PathBuf};
 
-use addr2line::LookupResultExt;
 use object::{Object, ObjectSection, ObjectSymbol};
 
 fn release_fixture_path() -> PathBuf {
diff --git a/examples/addr2line.rs b/examples/addr2line.rs
index 2b1d83a..13c584b 100644
--- a/examples/addr2line.rs
+++ b/examples/addr2line.rs
@@ -16,8 +16,6 @@ use fallible_iterator::FallibleIterator;
 use object::{Object, ObjectSection, SymbolMap, SymbolMapName};
 use typed_arena::Arena;
 
-#[cfg(not(unix))]
-use addr2line::LookupResultExt;
 use addr2line::{Context, Location};
 
 fn parse_uint_from_hex_string(string: &str) -> Option<u64> {
diff --git a/src/builtin_split_dwarf_loader.rs b/src/builtin_split_dwarf_loader.rs
index d912afc..c0393c4 100644
--- a/src/builtin_split_dwarf_loader.rs
+++ b/src/builtin_split_dwarf_loader.rs
@@ -102,8 +102,8 @@ where
     {
         loop {
             let (load, continuation) = match l {
-                LookupResult::Break(output) => break output,
-                LookupResult::Continue(load) => load,
+                LookupResult::Output(output) => break output,
+                LookupResult::Load { load, continuation } => (load, continuation),
             };
 
             let mut r: Option<Arc<gimli::Dwarf<_>>> = None;
diff --git a/src/function.rs b/src/function.rs
index 5602fd3..d3136f1 100644
--- a/src/function.rs
+++ b/src/function.rs
@@ -5,7 +5,7 @@ use core::iter;
 
 use crate::lazy::LazyCell;
 use crate::maybe_small;
-use crate::{DebugFile, Error, ParsedDwarf, RangeAttributes};
+use crate::{Context, DebugFile, Error, RangeAttributes};
 
 pub(crate) struct Functions<R: gimli::Reader> {
     /// List of all `DW_TAG_subprogram` details in the unit.
@@ -150,14 +150,15 @@ impl<R: gimli::Reader> Functions<R> {
 
     pub(crate) fn parse_inlined_functions(
         &self,
+        file: DebugFile,
         unit: &gimli::Unit<R>,
-        parsed: &ParsedDwarf<R>,
+        ctx: &Context<R>,
         sections: &gimli::Dwarf<R>,
     ) -> Result<(), Error> {
         for function in &*self.functions {
             function
                 .1
-                .borrow_with(|| Function::parse(function.0, unit, parsed, sections))
+                .borrow_with(|| Function::parse(function.0, file, unit, ctx, sections))
                 .as_ref()
                 .map_err(Error::clone)?;
         }
@@ -168,8 +169,9 @@ impl<R: gimli::Reader> Functions<R> {
 impl<R: gimli::Reader> Function<R> {
     pub(crate) fn parse(
         dw_die_offset: gimli::UnitOffset<R::Offset>,
+        file: DebugFile,
         unit: &gimli::Unit<R>,
-        parsed: &ParsedDwarf<R>,
+        ctx: &Context<R>,
         sections: &gimli::Dwarf<R>,
     ) -> Result<Self, Error> {
         let mut entries = unit.entries_raw(Some(dw_die_offset))?;
@@ -194,14 +196,7 @@ impl<R: gimli::Reader> Function<R> {
                         }
                         gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => {
                             if name.is_none() {
-                                name = name_attr(
-                                    attr.value(),
-                                    DebugFile::Primary,
-                                    unit,
-                                    parsed,
-                                    sections,
-                                    16,
-                                )?;
+                                name = name_attr(attr.value(), file, unit, ctx, sections, 16)?;
                             }
                         }
                         _ => {}
@@ -216,8 +211,9 @@ impl<R: gimli::Reader> Function<R> {
         Function::parse_children(
             &mut entries,
             depth,
+            file,
             unit,
-            parsed,
+            ctx,
             sections,
             &mut inlined_functions,
             &mut inlined_addresses,
@@ -259,8 +255,9 @@ impl<R: gimli::Reader> Function<R> {
     fn parse_children(
         entries: &mut gimli::EntriesRaw<R>,
         depth: isize,
+        file: DebugFile,
         unit: &gimli::Unit<R>,
-        parsed: &ParsedDwarf<R>,
+        ctx: &Context<R>,
         sections: &gimli::Dwarf<R>,
         inlined_functions: &mut Vec<InlinedFunction<R>>,
         inlined_addresses: &mut Vec<InlinedFunctionAddress>,
@@ -283,8 +280,9 @@ impl<R: gimli::Reader> Function<R> {
                             entries,
                             abbrev,
                             next_depth,
+                            file,
                             unit,
-                            parsed,
+                            ctx,
                             sections,
                             inlined_functions,
                             inlined_addresses,
@@ -359,8 +357,9 @@ impl<R: gimli::Reader> InlinedFunction<R> {
         entries: &mut gimli::EntriesRaw<R>,
         abbrev: &gimli::Abbreviation,
         depth: isize,
+        file: DebugFile,
         unit: &gimli::Unit<R>,
-        parsed: &ParsedDwarf<R>,
+        ctx: &Context<R>,
         sections: &gimli::Dwarf<R>,
         inlined_functions: &mut Vec<InlinedFunction<R>>,
         inlined_addresses: &mut Vec<InlinedFunctionAddress>,
@@ -404,14 +403,7 @@ impl<R: gimli::Reader> InlinedFunction<R> {
                     }
                     gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => {
                         if name.is_none() {
-                            name = name_attr(
-                                attr.value(),
-                                DebugFile::Primary,
-                                unit,
-                                parsed,
-                                sections,
-                                16,
-                            )?;
+                            name = name_attr(attr.value(), file, unit, ctx, sections, 16)?;
                         }
                     }
                     gimli::DW_AT_call_file => {
@@ -451,8 +443,9 @@ impl<R: gimli::Reader> InlinedFunction<R> {
         Function::parse_children(
             entries,
             depth,
+            file,
             unit,
-            parsed,
+            ctx,
             sections,
             inlined_functions,
             inlined_addresses,
@@ -465,7 +458,7 @@ fn name_attr<R>(
     attr: gimli::AttributeValue<R>,
     mut file: DebugFile,
     unit: &gimli::Unit<R>,
-    parsed: &ParsedDwarf<R>,
+    ctx: &Context<R>,
     sections: &gimli::Dwarf<R>,
     recursion_limit: usize,
 ) -> Result<Option<R>, Error>
@@ -478,31 +471,17 @@ where
 
     match attr {
         gimli::AttributeValue::UnitRef(offset) => {
-            name_entry(file, unit, offset, parsed, sections, recursion_limit)
+            name_entry(file, unit, offset, ctx, sections, recursion_limit)
         }
         gimli::AttributeValue::DebugInfoRef(dr) => {
-            let res_unit = parsed.find_unit(dr, file)?;
-            name_entry(
-                file,
-                &res_unit.dw_unit,
-                gimli::UnitOffset(dr.0 - res_unit.offset.0),
-                parsed,
-                sections,
-                recursion_limit,
-            )
+            let (unit, offset) = ctx.find_unit(dr, file)?;
+            name_entry(file, unit, offset, ctx, sections, recursion_limit)
         }
         gimli::AttributeValue::DebugInfoRefSup(dr) => {
             if let Some(sup_sections) = sections.sup.as_ref() {
                 file = DebugFile::Supplementary;
-                let res_unit = parsed.find_unit(dr, file)?;
-                name_entry(
-                    file,
-                    &res_unit.dw_unit,
-                    gimli::UnitOffset(dr.0 - res_unit.offset.0),
-                    parsed,
-                    sup_sections,
-                    recursion_limit,
-                )
+                let (unit, offset) = ctx.find_unit(dr, file)?;
+                name_entry(file, unit, offset, ctx, sup_sections, recursion_limit)
             } else {
                 Ok(None)
             }
@@ -515,7 +494,7 @@ fn name_entry<R>(
     file: DebugFile,
     unit: &gimli::Unit<R>,
     offset: gimli::UnitOffset<R::Offset>,
-    parsed: &ParsedDwarf<R>,
+    ctx: &Context<R>,
     sections: &gimli::Dwarf<R>,
     recursion_limit: usize,
 ) -> Result<Option<R>, Error>
@@ -558,7 +537,7 @@ where
     }
 
     if let Some(next) = next {
-        return name_attr(next, file, unit, parsed, sections, recursion_limit - 1);
+        return name_attr(next, file, unit, ctx, sections, recursion_limit - 1);
     }
 
     Ok(None)
diff --git a/src/lib.rs b/src/lib.rs
index 2194288..231da3e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -84,6 +84,7 @@ type Error = gimli::Error;
 enum DebugFile {
     Primary,
     Supplementary,
+    Dwo,
 }
 
 /// Operations that consult debug information may require additional files
@@ -102,18 +103,25 @@ enum DebugFile {
 ///   let mut r = ctx.find_frames(ADDRESS);
 ///   let result = loop {
 ///     match r {
-///       LookupResult::Break(result) => break result,
-///       LookupResult::Continue((load, continuation)) => {
+///       LookupResult::Output(result) => break result,
+///       LookupResult::Load { load, continuation } => {
 ///         let dwo = do_split_dwarf_load(load);
 ///         r = continuation.resume(dwo);
 ///       }
 ///     }
 ///   };
 /// ```
-pub type LookupResult<L> = ControlFlow<
-    <L as LookupContinuation>::Output,
-    (SplitDwarfLoad<<L as LookupContinuation>::Buf>, L),
->;
+pub enum LookupResult<L: LookupContinuation> {
+    /// The lookup requires split DWARF data to be loaded.
+    Load {
+        /// The information needed to find the split DWARF data.
+        load: SplitDwarfLoad<<L as LookupContinuation>::Buf>,
+        /// The continuation to resume with the loaded split DWARF data.
+        continuation: L,
+    },
+    /// The lookup has completed and produced an output.
+    Output(<L as LookupContinuation>::Output),
+}
 
 /// This trait represents a partially complete operation that can be resumed
 /// once a load of needed split DWARF data is completed or abandoned by the
@@ -137,50 +145,38 @@ pub trait LookupContinuation: Sized {
     fn resume(self, input: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self>;
 }
 
-/// This trait is used to extend `LookupResult`.
-pub trait LookupResultExt<L: LookupContinuation> {
+impl<L: LookupContinuation> LookupResult<L> {
     /// Callers that do not handle split DWARF can call `skip_all_loads`
     /// to fast-forward to the end result. This result is produced with
     /// the data that is available and may be less accurate than the
     /// the results that would be produced if the caller did properly
     /// support split DWARF.
-    fn skip_all_loads(self) -> L::Output;
-}
-
-impl<L: LookupContinuation> LookupResultExt<L> for LookupResult<L> {
-    fn skip_all_loads(mut self) -> L::Output {
+    pub fn skip_all_loads(mut self) -> L::Output {
         loop {
             self = match self {
-                ControlFlow::Break(t) => return t,
-                ControlFlow::Continue((_, continuation)) => continuation.resume(None),
+                LookupResult::Output(t) => return t,
+                LookupResult::Load { continuation, .. } => continuation.resume(None),
             };
         }
     }
-}
-
-trait LookupResultExtInternal<L: LookupContinuation> {
-    fn map<T, F: FnOnce(L::Output) -> T>(self, f: F) -> LookupResult<MappedLookup<T, L, F>>;
-    fn unwrap(self) -> L::Output;
-}
 
-impl<L: LookupContinuation> LookupResultExtInternal<L> for LookupResult<L> {
     fn map<T, F: FnOnce(L::Output) -> T>(self, f: F) -> LookupResult<MappedLookup<T, L, F>> {
         match self {
-            ControlFlow::Break(t) => ControlFlow::Break(f(t)),
-            ControlFlow::Continue((load, continuation)) => ControlFlow::Continue((
+            LookupResult::Output(t) => LookupResult::Output(f(t)),
+            LookupResult::Load { load, continuation } => LookupResult::Load {
                 load,
-                MappedLookup {
+                continuation: MappedLookup {
                     original: continuation,
                     mutator: f,
                 },
-            )),
+            },
         }
     }
 
     fn unwrap(self) -> L::Output {
         match self {
-            ControlFlow::Break(t) => t,
-            ControlFlow::Continue(_) => unreachable!("Internal API misuse"),
+            LookupResult::Output(t) => t,
+            LookupResult::Load { .. } => unreachable!("Internal API misuse"),
         }
     }
 }
@@ -190,8 +186,10 @@ impl<L: LookupContinuation> LookupResultExtInternal<L> for LookupResult<L> {
 /// Constructing a `Context` is somewhat costly, so users should aim to reuse `Context`s
 /// when performing lookups for many addresses in the same executable.
 pub struct Context<R: gimli::Reader> {
-    parsed_dwarf: ParsedDwarf<R>,
     sections: Arc<gimli::Dwarf<R>>,
+    unit_ranges: Vec<UnitRange>,
+    units: Vec<ResUnit<R>>,
+    sup_units: Vec<SupUnit<R>>,
 }
 
 /// The type of `Context` that supports the `new` method.
@@ -303,14 +301,17 @@ impl<R: gimli::Reader> Context<R> {
     #[inline]
     pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Context<R>, Error> {
         let sections = Arc::new(sections);
-        let mut parsed_dwarf = ParsedDwarf::parse(&sections)?;
-        if let Some(sup) = sections.sup.as_ref() {
-            let sup_dwarf = ParsedDwarf::parse(sup)?;
-            parsed_dwarf.sup_units = Some(sup_dwarf.units);
-        }
+        let (unit_ranges, units) = Context::parse_units(&sections)?;
+        let sup_units = if let Some(sup) = sections.sup.as_ref() {
+            Context::parse_sup(sup)?
+        } else {
+            Vec::new()
+        };
         Ok(Context {
-            parsed_dwarf,
             sections,
+            unit_ranges,
+            units,
+            sup_units,
         })
     }
 
@@ -342,7 +343,6 @@ impl<R: gimli::Reader> Context<R> {
         // First up find the position in the array which could have our function
         // address.
         let pos = match self
-            .parsed_dwarf
             .unit_ranges
             .binary_search_by_key(&probe_high, |i| i.range.begin)
         {
@@ -357,7 +357,7 @@ impl<R: gimli::Reader> Context<R> {
 
         // Once we have our index we iterate backwards from that position
         // looking for a matching CU.
-        self.parsed_dwarf.unit_ranges[..pos]
+        self.unit_ranges[..pos]
             .iter()
             .rev()
             .take_while(move |i| {
@@ -378,7 +378,7 @@ impl<R: gimli::Reader> Context<R> {
                 if probe_low >= i.range.end || probe_high <= i.range.begin {
                     return None;
                 }
-                Some((&self.parsed_dwarf.units[i.unit_id], &i.range))
+                Some((&self.units[i.unit_id], &i.range))
             })
     }
 
@@ -396,7 +396,7 @@ impl<R: gimli::Reader> Context<R> {
                 move |r| {
                     ControlFlow::Break(match r {
                         Ok((Some(_), _)) | Ok((_, Some(_))) => {
-                            let (sections, unit) = unit
+                            let (_file, sections, unit) = unit
                                 .dwarf_and_unit_dwo(self)
                                 // We've already been through both error cases here to get to this point.
                                 .unwrap()
@@ -507,8 +507,8 @@ impl<R: gimli::Reader> Context<R> {
     ///   });
     ///
     ///   let frames_iter = match ctx.find_frames(ADDRESS) {
-    ///     LookupResult::Break(result) => result,
-    ///     LookupResult::Continue(_) => unreachable!("addr2line promised we wouldn't get here"),
+    ///     LookupResult::Output(result) => result,
+    ///     LookupResult::Load { .. } => unreachable!("addr2line promised we wouldn't get here"),
     ///   };
     ///
     ///   // ...
@@ -524,8 +524,8 @@ impl<R: gimli::Reader> Context<R> {
     > {
         self.find_units(probe)
             .filter_map(move |unit| match unit.dwarf_and_unit_dwo(self) {
-                LookupResult::Break(_) => None,
-                LookupResult::Continue((load, continuation)) => Some((load, |result| {
+                LookupResult::Output(_) => None,
+                LookupResult::Load { load, continuation } => Some((load, |result| {
                     continuation.resume(result).unwrap().map(|_| ())
                 })),
             })
@@ -534,7 +534,7 @@ impl<R: gimli::Reader> Context<R> {
     /// Initialize all line data structures. This is used for benchmarks.
     #[doc(hidden)]
     pub fn parse_lines(&self) -> Result<(), Error> {
-        for unit in &self.parsed_dwarf.units {
+        for unit in &self.units {
             unit.parse_lines(&self.sections)?;
         }
         Ok(())
@@ -543,7 +543,7 @@ impl<R: gimli::Reader> Context<R> {
     /// Initialize all function data structures. This is used for benchmarks.
     #[doc(hidden)]
     pub fn parse_functions(&self) -> Result<(), Error> {
-        for unit in &self.parsed_dwarf.units {
+        for unit in &self.units {
             unit.parse_functions(self).skip_all_loads()?;
         }
         Ok(())
@@ -552,7 +552,7 @@ impl<R: gimli::Reader> Context<R> {
     /// Initialize all inlined function data structures. This is used for benchmarks.
     #[doc(hidden)]
     pub fn parse_inlined_functions(&self) -> Result<(), Error> {
-        for unit in &self.parsed_dwarf.units {
+        for unit in &self.units {
             unit.parse_inlined_functions(self).skip_all_loads()?;
         }
         Ok(())
@@ -565,14 +565,22 @@ struct UnitRange {
     range: gimli::Range,
 }
 
-struct ParsedDwarf<R: gimli::Reader> {
-    unit_ranges: Vec<UnitRange>,
-    units: Vec<ResUnit<R>>,
-    sup_units: Option<Vec<ResUnit<R>>>,
+struct ResUnit<R: gimli::Reader> {
+    offset: gimli::DebugInfoOffset<R::Offset>,
+    dw_unit: gimli::Unit<R>,
+    lang: Option<gimli::DwLang>,
+    lines: LazyCell<Result<Lines, Error>>,
+    funcs: LazyCell<Result<Functions<R>, Error>>,
+    dwo: LazyCell<Result<Option<Box<(Arc<gimli::Dwarf<R>>, gimli::Unit<R>)>>, Error>>,
+}
+
+struct SupUnit<R: gimli::Reader> {
+    offset: gimli::DebugInfoOffset<R::Offset>,
+    dw_unit: gimli::Unit<R>,
 }
 
-impl<R: gimli::Reader> ParsedDwarf<R> {
-    fn parse(sections: &gimli::Dwarf<R>) -> Result<Self, Error> {
+impl<R: gimli::Reader> Context<R> {
+    fn parse_units(sections: &gimli::Dwarf<R>) -> Result<(Vec<UnitRange>, Vec<ResUnit<R>>), Error> {
         // Find all the references to compilation units in .debug_aranges.
         // Note that we always also iterate through all of .debug_info to
         // find compilation units, because .debug_aranges may be missing some.
@@ -737,31 +745,60 @@ impl<R: gimli::Reader> ParsedDwarf<R> {
             i.max_end = max;
         }
 
-        Ok(ParsedDwarf {
-            units: res_units,
-            unit_ranges,
-            sup_units: None,
-        })
+        Ok((unit_ranges, res_units))
     }
 
+    fn parse_sup(sections: &gimli::Dwarf<R>) -> Result<Vec<SupUnit<R>>, Error> {
+        let mut sup_units = Vec::new();
+        let mut units = sections.units();
+        while let Some(header) = units.next()? {
+            let offset = match header.offset().as_debug_info_offset() {
+                Some(offset) => offset,
+                None => continue,
+            };
+            let dw_unit = match sections.unit(header) {
+                Ok(dw_unit) => dw_unit,
+                Err(_) => continue,
+            };
+            sup_units.push(SupUnit { dw_unit, offset });
+        }
+        Ok(sup_units)
+    }
+
+    // Find the unit containing the given offset, and convert the offset into a unit offset.
     fn find_unit(
         &self,
         offset: gimli::DebugInfoOffset<R::Offset>,
         file: DebugFile,
-    ) -> Result<&ResUnit<R>, Error> {
-        let units = match file {
-            DebugFile::Primary => &self.units,
-            DebugFile::Supplementary => self
-                .sup_units
-                .as_ref()
-                .ok_or(gimli::Error::NoEntryAtGivenOffset)?,
+    ) -> Result<(&gimli::Unit<R>, gimli::UnitOffset<R::Offset>), Error> {
+        let unit = match file {
+            DebugFile::Primary => {
+                match self
+                    .units
+                    .binary_search_by_key(&offset.0, |unit| unit.offset.0)
+                {
+                    // There is never a DIE at the unit offset or before the first unit.
+                    Ok(_) | Err(0) => return Err(gimli::Error::NoEntryAtGivenOffset),
+                    Err(i) => &self.units[i - 1].dw_unit,
+                }
+            }
+            DebugFile::Supplementary => {
+                match self
+                    .sup_units
+                    .binary_search_by_key(&offset.0, |unit| unit.offset.0)
+                {
+                    // There is never a DIE at the unit offset or before the first unit.
+                    Ok(_) | Err(0) => return Err(gimli::Error::NoEntryAtGivenOffset),
+                    Err(i) => &self.sup_units[i - 1].dw_unit,
+                }
+            }
+            DebugFile::Dwo => return Err(gimli::Error::NoEntryAtGivenOffset),
         };
 
-        match units.binary_search_by_key(&offset.0, |unit| unit.offset.0) {
-            // There is never a DIE at the unit offset or before the first unit.
-            Ok(_) | Err(0) => Err(gimli::Error::NoEntryAtGivenOffset),
-            Err(i) => Ok(&units[i - 1]),
-        }
+        let unit_offset = offset
+            .to_unit_offset(&unit.header)
+            .ok_or(gimli::Error::NoEntryAtGivenOffset)?;
+        Ok((unit, unit_offset))
     }
 }
 
@@ -888,15 +925,6 @@ struct LineRow {
     column: u32,
 }
 
-struct ResUnit<R: gimli::Reader> {
-    offset: gimli::DebugInfoOffset<R::Offset>,
-    dw_unit: gimli::Unit<R>,
-    lang: Option<gimli::DwLang>,
-    lines: LazyCell<Result<Lines, Error>>,
-    funcs: LazyCell<Result<Functions<R>, Error>>,
-    dwo: LazyCell<Result<Option<Box<(Arc<gimli::Dwarf<R>>, gimli::Unit<R>)>>, Error>>,
-}
-
 /// This struct contains the information needed to find split DWARF data
 /// and to produce a `gimli::Dwarf<R>` for it.
 pub struct SplitDwarfLoad<R> {
@@ -928,17 +956,17 @@ where
     R: gimli::Reader,
 {
     fn new_complete(t: F::Output) -> LookupResult<SimpleLookup<T, R, F>> {
-        ControlFlow::Break(t)
+        LookupResult::Output(t)
     }
 
-    fn new_needs_load(lookup: SplitDwarfLoad<R>, f: F) -> LookupResult<SimpleLookup<T, R, F>> {
-        ControlFlow::Continue((
-            lookup,
-            SimpleLookup {
+    fn new_needs_load(load: SplitDwarfLoad<R>, f: F) -> LookupResult<SimpleLookup<T, R, F>> {
+        LookupResult::Load {
+            load,
+            continuation: SimpleLookup {
                 f,
                 phantom: PhantomData,
             },
-        ))
+        }
     }
 }
 
@@ -951,7 +979,7 @@ where
     type Buf = R;
 
     fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
-        ControlFlow::Break((self.f)(v))
+        LookupResult::Output((self.f)(v))
     }
 }
 
@@ -974,14 +1002,14 @@ where
 
     fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
         match self.original.resume(v) {
-            ControlFlow::Break(t) => ControlFlow::Break((self.mutator)(t)),
-            ControlFlow::Continue((load, continuation)) => ControlFlow::Continue((
+            LookupResult::Output(t) => LookupResult::Output((self.mutator)(t)),
+            LookupResult::Load { load, continuation } => LookupResult::Load {
                 load,
-                MappedLookup {
+                continuation: MappedLookup {
                     original: continuation,
                     mutator: self.mutator,
                 },
-            )),
+            },
         }
     }
 }
@@ -1013,31 +1041,31 @@ where
     F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
 {
     fn new_complete(t: T) -> LookupResult<Self> {
-        ControlFlow::Break(t)
+        LookupResult::Output(t)
     }
 
     fn new_lookup(mut r: LookupResult<L>, mut mutator: F) -> LookupResult<Self> {
         // Drive the loop eagerly so that we only ever have to represent one state
         // (the r == ControlFlow::Continue state) in LoopingLookup.
-        let (load, continuation) = loop {
+        loop {
             match r {
-                ControlFlow::Break(l) => match mutator(l) {
-                    ControlFlow::Break(t) => return ControlFlow::Break(t),
+                LookupResult::Output(l) => match mutator(l) {
+                    ControlFlow::Break(t) => return LookupResult::Output(t),
                     ControlFlow::Continue(r2) => {
                         r = r2;
                     }
                 },
-                ControlFlow::Continue((load, continuation)) => break (load, continuation),
+                LookupResult::Load { load, continuation } => {
+                    return LookupResult::Load {
+                        load,
+                        continuation: LoopingLookup {
+                            continuation,
+                            mutator,
+                        },
+                    };
+                }
             }
-        };
-
-        ControlFlow::Continue((
-            load,
-            LoopingLookup {
-                continuation,
-                mutator,
-            },
-        ))
+        }
     }
 }
 
@@ -1061,17 +1089,18 @@ impl<R: gimli::Reader> ResUnit<R> {
         ctx: &'ctx Context<R>,
     ) -> LookupResult<
         SimpleLookup<
-            Result<(&'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>,
+            Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>,
             R,
             impl FnOnce(
                 Option<Arc<gimli::Dwarf<R>>>,
-            ) -> Result<(&'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>,
+            )
+                -> Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>,
         >,
     > {
         loop {
             break SimpleLookup::new_complete(match self.dwo.borrow() {
-                Some(Ok(Some(v))) => Ok((&*v.0, &v.1)),
-                Some(Ok(None)) => Ok((&*ctx.sections, &self.dw_unit)),
+                Some(Ok(Some(v))) => Ok((DebugFile::Dwo, &*v.0, &v.1)),
+                Some(Ok(None)) => Ok((DebugFile::Primary, &*ctx.sections, &self.dw_unit)),
                 Some(Err(e)) => Err(*e),
                 None => {
                     let dwo_id = match self.dw_unit.dwo_id {
@@ -1082,20 +1111,17 @@ impl<R: gimli::Reader> ResUnit<R> {
                         Some(dwo_id) => dwo_id,
                     };
 
-                    let dwo_path_f = || {
-                        let dwo_comp_dir = self.dw_unit.comp_dir.clone();
+                    let comp_dir = self.dw_unit.comp_dir.clone();
 
-                        let dwo_path = self.dw_unit.dwo_name().and_then(|s| {
-                            if let Some(s) = s {
-                                Ok(Some(ctx.sections.attr_string(&self.dw_unit, s)?))
-                            } else {
-                                Ok(None)
-                            }
-                        })?;
-                        Ok((dwo_comp_dir, dwo_path))
-                    };
+                    let dwo_name = self.dw_unit.dwo_name().and_then(|s| {
+                        if let Some(s) = s {
+                            Ok(Some(ctx.sections.attr_string(&self.dw_unit, s)?))
+                        } else {
+                            Ok(None)
+                        }
+                    });
 
-                    let (comp_dir, path) = match dwo_path_f() {
+                    let path = match dwo_name {
                         Ok(v) => v,
                         Err(e) => {
                             self.dwo.borrow_with(|| Err(e));
@@ -1127,8 +1153,8 @@ impl<R: gimli::Reader> ResUnit<R> {
                             parent: ctx.sections.clone(),
                         },
                         move |dwo_dwarf| match self.dwo.borrow_with(|| process_dwo(dwo_dwarf)) {
-                            Ok(Some(v)) => Ok((&*v.0, &v.1)),
-                            Ok(None) => Ok((&*ctx.sections, &self.dw_unit)),
+                            Ok(Some(v)) => Ok((DebugFile::Dwo, &*v.0, &v.1)),
+                            Ok(None) => Ok((DebugFile::Primary, &*ctx.sections, &self.dw_unit)),
                             Err(e) => Err(*e),
                         },
                     );
@@ -1168,7 +1194,7 @@ impl<R: gimli::Reader> ResUnit<R> {
     ) -> LookupResult<impl LookupContinuation<Output = Result<&'unit Functions<R>, Error>, Buf = R>>
     {
         self.dwarf_and_unit_dwo(ctx).map(move |r| {
-            let (sections, unit) = r?;
+            let (_file, sections, unit) = r?;
             self.parse_functions_dwarf_and_unit(unit, sections)
         })
     }
@@ -1177,12 +1203,12 @@ impl<R: gimli::Reader> ResUnit<R> {
         ctx: &'ctx Context<R>,
     ) -> LookupResult<impl LookupContinuation<Output = Result<(), Error>, Buf = R> + 'unit> {
         self.dwarf_and_unit_dwo(ctx).map(move |r| {
-            let (sections, unit) = r?;
+            let (file, sections, unit) = r?;
             self.funcs
                 .borrow_with(|| Functions::parse(unit, sections))
                 .as_ref()
                 .map_err(Error::clone)?
-                .parse_inlined_functions(unit, &ctx.parsed_dwarf, sections)
+                .parse_inlined_functions(file, unit, ctx, sections)
         })
     }
 
@@ -1222,7 +1248,7 @@ impl<R: gimli::Reader> ResUnit<R> {
         >,
     > {
         self.dwarf_and_unit_dwo(ctx).map(move |r| {
-            let (sections, unit) = r?;
+            let (file, sections, unit) = r?;
             let functions = self.parse_functions_dwarf_and_unit(unit, sections)?;
             let function = match functions.find_address(probe) {
                 Some(address) => {
@@ -1230,9 +1256,7 @@ impl<R: gimli::Reader> ResUnit<R> {
                     let (offset, ref function) = functions.functions[function_index];
                     Some(
                         function
-                            .borrow_with(|| {
-                                Function::parse(offset, unit, &ctx.parsed_dwarf, sections)
-                            })
+                            .borrow_with(|| Function::parse(offset, file, unit, ctx, sections))
                             .as_ref()
                             .map_err(Error::clone)?,
                     )


Test patch:
diff --git a/tests/correctness.rs b/tests/correctness.rs
index 66caf39..9b53cd5 100644
--- a/tests/correctness.rs
+++ b/tests/correctness.rs
@@ -5,7 +5,7 @@ extern crate gimli;
 extern crate memmap2;
 extern crate object;
 
-use addr2line::{Context, LookupResultExt};
+use addr2line::Context;
 use fallible_iterator::FallibleIterator;
 use findshlibs::{IterationControl, SharedLibrary, TargetSharedLibrary};
 use object::Object;