Skip to content

Commit ecb935e

Browse files
authored
Merge pull request #5 from swananan/fix_gimli_unsafe_use
refactor: eliminate unsafe lifetime transmutes in DWARF parser
2 parents d406825 + 802c9fb commit ecb935e

9 files changed

Lines changed: 457 additions & 345 deletions

File tree

ghostscope-dwarf/src/data/block_index.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//! - Provides fast lookup of in-scope variables at a given PC
77
88
use crate::parser::RangeExtractor;
9-
use gimli::{EndianSlice, LittleEndian};
9+
use gimli::{EndianArcSlice, LittleEndian, Reader};
1010
use std::collections::BTreeMap;
1111

1212
/// Reference to a variable DIE within a unit
@@ -190,11 +190,11 @@ impl BlockIndex {
190190

191191
/// Builder for block index using DWARF data
192192
pub struct BlockIndexBuilder<'a> {
193-
dwarf: &'a gimli::Dwarf<EndianSlice<'static, LittleEndian>>,
193+
dwarf: &'a gimli::Dwarf<EndianArcSlice<LittleEndian>>,
194194
}
195195

196196
impl<'a> BlockIndexBuilder<'a> {
197-
pub fn new(dwarf: &'a gimli::Dwarf<EndianSlice<'static, LittleEndian>>) -> Self {
197+
pub fn new(dwarf: &'a gimli::Dwarf<EndianArcSlice<LittleEndian>>) -> Self {
198198
Self { dwarf }
199199
}
200200

@@ -213,7 +213,7 @@ impl<'a> BlockIndexBuilder<'a> {
213213
.ok()
214214
.and_then(|a| a)
215215
.and_then(|a| self.dwarf.attr_string(&unit, a.value()).ok())
216-
.map(|s| s.to_string_lossy().into_owned());
216+
.and_then(|s| s.to_string_lossy().ok().map(|cow| cow.into_owned()));
217217
let mut fb = FunctionBlocks::new(name, cu_offset, entry.offset());
218218
if let Ok(ranges) = RangeExtractor::extract_all_ranges(entry, &unit, self.dwarf) {
219219
fb.ranges = ranges;
@@ -250,7 +250,7 @@ impl<'a> BlockIndexBuilder<'a> {
250250
.ok()
251251
.and_then(|a| a)
252252
.and_then(|a| self.dwarf.attr_string(&unit, a.value()).ok())
253-
.map(|s| s.to_string_lossy().into_owned());
253+
.and_then(|s| s.to_string_lossy().ok().map(|cow| cow.into_owned()));
254254
let mut fb = FunctionBlocks::new(name, cu_offset, die_offset);
255255
if let Ok(ranges) = RangeExtractor::extract_all_ranges(&entry, &unit, self.dwarf) {
256256
fb.ranges = ranges;
@@ -269,8 +269,8 @@ impl<'a> BlockIndexBuilder<'a> {
269269

270270
fn build_blocks_for_function(
271271
&self,
272-
unit: &gimli::Unit<EndianSlice<'static, LittleEndian>>,
273-
func_entry: &gimli::DebuggingInformationEntry<EndianSlice<'static, LittleEndian>>,
272+
unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
273+
func_entry: &gimli::DebuggingInformationEntry<EndianArcSlice<LittleEndian>>,
274274
fb: &mut FunctionBlocks,
275275
) {
276276
// DFS but only within this function's subtree
@@ -283,8 +283,8 @@ impl<'a> BlockIndexBuilder<'a> {
283283

284284
fn walk_children(
285285
&self,
286-
unit: &gimli::Unit<EndianSlice<'static, LittleEndian>>,
287-
node: gimli::EntriesTreeNode<EndianSlice<'static, LittleEndian>>,
286+
unit: &gimli::Unit<EndianArcSlice<LittleEndian>>,
287+
node: gimli::EntriesTreeNode<EndianArcSlice<LittleEndian>>,
288288
parent_idx: usize,
289289
fb: &mut FunctionBlocks,
290290
) {

ghostscope-dwarf/src/data/cfi_index.rs

Lines changed: 72 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,27 @@
66
use crate::core::{CfaResult, ComputeStep, Result};
77
use anyhow::{anyhow, Context};
88
use gimli::{
9-
BaseAddresses, CfaRule, CieOrFde, EhFrame, EhFrameHdr, EndianSlice, FrameDescriptionEntry,
10-
LittleEndian, ParsedEhFrameHdr, UnwindContext, UnwindSection,
9+
BaseAddresses, CfaRule, CieOrFde, EhFrame, EhFrameHdr, FrameDescriptionEntry, LittleEndian,
10+
ParsedEhFrameHdr, UnwindContext, UnwindSection,
1111
};
1212
use object::{Object, ObjectSection};
13+
use std::sync::Arc;
1314
use tracing::{debug, info, warn};
1415

16+
use gimli::{EndianSlice, LittleEndian as LE};
17+
1518
/// CFI index for fast CFA rule lookup
1619
pub struct CfiIndex {
17-
/// Parsed eh_frame section
18-
eh_frame: EhFrame<EndianSlice<'static, LittleEndian>>,
20+
/// Keep file data alive
21+
_file_data: Arc<[u8]>,
22+
/// Keep eh_frame section data alive
23+
_eh_frame_data: Arc<[u8]>,
24+
/// Keep eh_frame_hdr section data alive (if present)
25+
_eh_frame_hdr_data: Option<Arc<[u8]>>,
26+
/// Parsed eh_frame section (using 'static via Box::leak)
27+
eh_frame: EhFrame<EndianSlice<'static, LE>>,
1928
/// Parsed eh_frame_hdr for fast lookup (if available)
20-
eh_frame_hdr: Option<ParsedEhFrameHdr<EndianSlice<'static, LittleEndian>>>,
29+
eh_frame_hdr: Option<ParsedEhFrameHdr<EndianSlice<'static, LE>>>,
2130
/// Base addresses for DWARF sections
2231
bases: BaseAddresses,
2332
/// Whether we have eh_frame_hdr for fast lookup
@@ -34,48 +43,63 @@ impl std::fmt::Debug for CfiIndex {
3443
}
3544

3645
impl CfiIndex {
37-
/// Create a new CFI index from an object file
38-
pub fn from_static_data(file_data: &'static [u8]) -> Result<Self> {
39-
let object = object::File::parse(file_data).context("Failed to parse object file")?;
46+
/// Create a new CFI index from an object file data
47+
pub fn from_arc_data(file_data: Arc<[u8]>) -> Result<Self> {
48+
let object = object::File::parse(&file_data[..]).context("Failed to parse object file")?;
4049

4150
// Load eh_frame section (required)
42-
let eh_frame_data = object
51+
let eh_frame_section = object
4352
.section_by_name(".eh_frame")
44-
.ok_or_else(|| anyhow!(".eh_frame section not found"))?
45-
.data()
46-
.context("Failed to read .eh_frame section data")?;
47-
48-
let eh_frame = EhFrame::new(eh_frame_data, LittleEndian);
53+
.ok_or_else(|| anyhow!(".eh_frame section not found"))?;
54+
55+
// Get section data range
56+
let (eh_frame_start, eh_frame_size) = eh_frame_section
57+
.file_range()
58+
.ok_or_else(|| anyhow!(".eh_frame section has no file range"))?;
59+
let eh_frame_start = eh_frame_start as usize;
60+
let eh_frame_end = eh_frame_start + eh_frame_size as usize;
61+
62+
// Create Arc slice for eh_frame and leak to 'static
63+
// Note: We must copy once for Box::leak (gimli's EhFrame requires 'static lifetime)
64+
let eh_frame_data = file_data[eh_frame_start..eh_frame_end].to_vec();
65+
let eh_frame_static: &'static [u8] = Box::leak(eh_frame_data.into_boxed_slice());
66+
let eh_frame_arc: Arc<[u8]> = Arc::from(eh_frame_static); // Zero-copy: reference leaked data
67+
let eh_frame = EhFrame::new(eh_frame_static, LittleEndian);
4968

5069
// Try to load eh_frame_hdr for fast lookup (optional)
70+
let mut hdr_arc_opt: Option<Arc<[u8]>> = None;
5171
let (eh_frame_hdr, has_fast_lookup) = match object.section_by_name(".eh_frame_hdr") {
52-
Some(section) => {
53-
match section.data() {
54-
Ok(data) => {
55-
let hdr_section = EhFrameHdr::new(data, LittleEndian);
56-
57-
// Parse with proper address_size
58-
let address_size = if object.is_64() { 8 } else { 4 };
59-
let mut bases = BaseAddresses::default();
60-
61-
// Set eh_frame_hdr section base
62-
if let Some(hdr_section_obj) = object.section_by_name(".eh_frame_hdr") {
63-
bases = bases.set_eh_frame_hdr(hdr_section_obj.address());
64-
}
65-
66-
match hdr_section.parse(&bases, address_size) {
67-
Ok(parsed) => {
68-
info!("Successfully parsed .eh_frame_hdr for fast FDE lookup");
69-
(Some(parsed), true)
70-
}
71-
Err(e) => {
72-
warn!("Failed to parse .eh_frame_hdr: {:?}, falling back to linear search", e);
73-
(None, false)
74-
}
75-
}
72+
Some(hdr_section_obj) => {
73+
let (hdr_start, hdr_size) = hdr_section_obj
74+
.file_range()
75+
.ok_or_else(|| anyhow!(".eh_frame_hdr section has no file range"))?;
76+
let hdr_start = hdr_start as usize;
77+
let hdr_end = hdr_start + hdr_size as usize;
78+
79+
// Create Arc slice for eh_frame_hdr and leak to 'static
80+
let hdr_data = file_data[hdr_start..hdr_end].to_vec();
81+
let hdr_static: &'static [u8] = Box::leak(hdr_data.into_boxed_slice());
82+
let hdr_arc: Arc<[u8]> = Arc::from(hdr_static); // Zero-copy: reference leaked data
83+
hdr_arc_opt = Some(hdr_arc);
84+
let hdr_section = EhFrameHdr::new(hdr_static, LittleEndian);
85+
86+
// Parse with proper address_size
87+
let address_size = if object.is_64() { 8 } else { 4 };
88+
let mut bases = BaseAddresses::default();
89+
90+
// Set eh_frame_hdr section base
91+
bases = bases.set_eh_frame_hdr(hdr_section_obj.address());
92+
93+
match hdr_section.parse(&bases, address_size) {
94+
Ok(parsed) => {
95+
info!("Successfully parsed .eh_frame_hdr for fast FDE lookup");
96+
(Some(parsed), true)
7697
}
7798
Err(e) => {
78-
warn!("Failed to read .eh_frame_hdr data: {:?}", e);
99+
warn!(
100+
"Failed to parse .eh_frame_hdr: {:?}, falling back to linear search",
101+
e
102+
);
79103
(None, false)
80104
}
81105
}
@@ -105,6 +129,9 @@ impl CfiIndex {
105129
}
106130

107131
Ok(Self {
132+
_file_data: file_data.clone(),
133+
_eh_frame_data: eh_frame_arc,
134+
_eh_frame_hdr_data: hdr_arc_opt,
108135
eh_frame,
109136
eh_frame_hdr,
110137
bases,
@@ -142,7 +169,11 @@ impl CfiIndex {
142169
// Get the expression bytes from the section
143170
let expression = expr.get(&self.eh_frame)?;
144171
// Parse DWARF expression to compute steps
145-
let steps = self.parse_dwarf_expression(expression.0.slice())?;
172+
// expression.0 is EndianSlice, get the underlying bytes
173+
use gimli::Reader;
174+
let temp = expression.0.to_slice().ok();
175+
let expr_bytes = temp.as_deref().unwrap_or(&[]);
176+
let steps = self.parse_dwarf_expression(expr_bytes)?;
146177
CfaResult::Expression { steps }
147178
}
148179
};
@@ -156,7 +187,7 @@ impl CfiIndex {
156187
fn find_fde_for_address(
157188
&self,
158189
address: u64,
159-
) -> Result<FrameDescriptionEntry<EndianSlice<'static, LittleEndian>, usize>> {
190+
) -> Result<FrameDescriptionEntry<EndianSlice<'static, LE>, usize>> {
160191
if let Some(hdr) = &self.eh_frame_hdr {
161192
// Fast path: O(log n) binary search using eh_frame_hdr
162193
debug!(

ghostscope-dwarf/src/data/on_demand_resolver.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use crate::{
55
core::Result, data::TypeNameIndex, parser::DetailedParser, parser::VariableWithEvaluation,
66
};
7-
use gimli::{EndianSlice, LittleEndian};
7+
use gimli::{EndianArcSlice, LittleEndian};
88
// Use upper-case aliases to satisfy non_upper_case_globals lint on pattern constants
99
// No direct constant tag imports needed here
1010
// tracing::warn no longer used after removing legacy traversal
@@ -18,7 +18,7 @@ pub struct ChainSpec<'a> {
1818
/// Real on-demand DWARF resolver
1919
#[derive(Debug)]
2020
pub struct OnDemandResolver {
21-
dwarf: gimli::Dwarf<EndianSlice<'static, LittleEndian>>,
21+
dwarf: gimli::Dwarf<EndianArcSlice<LittleEndian>>,
2222
detailed_parser: DetailedParser,
2323
/// Optional cross-CU type name index (built from lightweight index)
2424
type_name_index: Option<std::sync::Arc<TypeNameIndex>>,
@@ -29,7 +29,7 @@ pub struct OnDemandResolver {
2929
impl OnDemandResolver {
3030
/// Create resolver with a type name index for faster cross-CU completion
3131
pub fn new_with_type_index(
32-
dwarf: gimli::Dwarf<EndianSlice<'static, LittleEndian>>,
32+
dwarf: gimli::Dwarf<EndianArcSlice<LittleEndian>>,
3333
type_index: std::sync::Arc<TypeNameIndex>,
3434
) -> Self {
3535
let mut detailed_parser = DetailedParser::new();
@@ -43,7 +43,7 @@ impl OnDemandResolver {
4343
}
4444

4545
/// Borrow DWARF reference for building auxiliary indexes lazily
46-
pub fn dwarf_ref(&self) -> &gimli::Dwarf<EndianSlice<'static, LittleEndian>> {
46+
pub fn dwarf_ref(&self) -> &gimli::Dwarf<EndianArcSlice<LittleEndian>> {
4747
&self.dwarf
4848
}
4949

0 commit comments

Comments
 (0)