Skip to content

Commit

Permalink
Use gimli::write::LineConvert
Browse files Browse the repository at this point in the history
  • Loading branch information
philipc committed Aug 22, 2024
1 parent 2c3ddf9 commit 05f9bac
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 233 deletions.
291 changes: 60 additions & 231 deletions src/module/debug/dwarf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ pub(crate) static DEAD_CODE: u64 = 0xFFFFFFFF;
/// DWARF convertion context
pub(crate) struct ConvertContext<'a, R: Reader<Offset = usize>> {
/// Source DWARF debug data
pub debug_str: &'a read::DebugStr<R>,
pub debug_line_str: &'a read::DebugLineStr<R>,
pub dwarf: &'a read::Dwarf<R>,

pub strings: &'a mut write::StringTable,
pub line_strings: &'a mut write::LineStringTable,
Expand All @@ -38,15 +37,13 @@ where
R: Reader<Offset = usize>,
{
pub(crate) fn new(
debug_str: &'a read::DebugStr<R>,
debug_line_str: &'a read::DebugLineStr<R>,
dwarf: &'a read::Dwarf<R>,
strings: &'a mut write::StringTable,
line_strings: &'a mut write::LineStringTable,
convert_address: &'a dyn Fn(u64, AddressSearchPreference) -> Option<write::Address>,
) -> Self {
ConvertContext {
debug_str,
debug_line_str,
dwarf,
strings,
line_strings,
convert_address,
Expand Down Expand Up @@ -95,224 +92,61 @@ where
&mut self,
from_unit: read::Unit<R>,
) -> Option<write::LineProgram> {
match from_unit.line_program {
Some(ref from_program) => {
let from_program = from_program.clone();
let line_program = self
.convert_line_program(from_program)
.expect("cannot convert line program");
Some(line_program)
}
None => None,
}
}

/// Perform conversion in DWARF line program header.
/// Almostly cloned from https://github.com/gimli-rs/gimli/blob/master/src/write/line.rs#L985
fn convert_line_program_header(
&mut self,
from_program: &read::IncompleteLineProgram<R>,
files: &mut Vec<write::FileId>,
) -> write::ConvertResult<write::LineProgram> {
let from_header = from_program.header();
let encoding = from_header.encoding();

let comp_dir = match from_header.directory(0) {
Some(comp_dir) => self.convert_line_string(comp_dir)?,
None => write::LineString::new(&[][..], encoding, self.line_strings),
};

let (comp_name, comp_file_info) = match from_header.file(0) {
Some(comp_file) => {
if comp_file.directory_index() != 0 {
return Err(write::ConvertError::InvalidDirectoryIndex);
}
(
self.convert_line_string(comp_file.path_name())?,
Some(write::FileInfo {
timestamp: comp_file.timestamp(),
size: comp_file.size(),
md5: *comp_file.md5(),
}),
)
}
None => (
write::LineString::new(&[][..], encoding, self.line_strings),
None,
),
};

if from_header.line_base() > 0 {
return Err(write::ConvertError::InvalidLineBase);
}
let mut program = write::LineProgram::new(
encoding,
from_header.line_encoding(),
comp_dir,
comp_name,
comp_file_info,
);

let mut dirs = Vec::new();
let file_skip = if from_header.version() <= 4 {
// The first directory is implicit.
dirs.push(program.default_directory());
// A file index of 0 is invalid for version <= 4, but putting
// something there makes the indexing easier.
0
} else {
// We don't add the first file to `files`, but still allow
// it to be referenced from converted instructions.
1
};

for from_dir in from_header.include_directories() {
let from_dir = self.convert_line_string(from_dir.clone())?;
dirs.push(program.add_directory(from_dir));
}

for from_file in from_header.file_names().iter().skip(file_skip) {
let from_name = self.convert_line_string(from_file.path_name())?;
let from_dir = from_file.directory_index();
if from_dir >= dirs.len() as u64 {
return Err(write::ConvertError::InvalidDirectoryIndex);
}
let from_dir = dirs[from_dir as usize];
let from_info = Some(write::FileInfo {
timestamp: from_file.timestamp(),
size: from_file.size(),
md5: *from_file.md5(),
});
files.push(program.add_file(from_name, from_dir, from_info));
}

Ok(program)
let from_program = from_unit.line_program.clone()?;
let line_program = self
.convert_line_program(from_program, from_unit.name.clone())
.expect("cannot convert line program");
Some(line_program)
}

/// Perform address conversion in DWARF line program entries.
/// Almostly cloned from https://github.com/gimli-rs/gimli/blob/master/src/write/line.rs#L1066
fn convert_line_program(
&mut self,
mut from_program: read::IncompleteLineProgram<R>,
from_program: read::IncompleteLineProgram<R>,
comp_name: Option<R>,
) -> write::ConvertResult<write::LineProgram> {
let mut files = Vec::new();
// Create mappings in case the source has duplicate files or directories.
let mut program = self
.convert_line_program_header(&from_program, &mut files)
.expect("line program header cannot be converted");

// We can't use the `from_program.rows()` because that wouldn't let
// us preserve address relocations.
let mut from_row = read::LineRow::new(from_program.header());
let mut instructions = from_program.header().instructions();
let mut current_sequence_base_address = None;
let mut from_base_address = 0;

while let Some(instruction) = instructions.next_instruction(from_program.header())? {
match instruction {
read::LineInstruction::SetAddress(val) => {
if program.in_sequence() {
return Err(write::ConvertError::UnsupportedLineInstruction);
}
from_base_address = val;

from_row.execute(read::LineInstruction::SetAddress(0), &mut from_program);
}
read::LineInstruction::DefineFile(_) => {
return Err(write::ConvertError::UnsupportedLineInstruction);
}
_ => {
if from_row.execute(instruction, &mut from_program) {
if !program.in_sequence() {
// begin new sequence if exists
current_sequence_base_address = (self.convert_address)(
from_base_address,
AddressSearchPreference::ExclusiveFunctionEnd,
);

if current_sequence_base_address.is_some() {
program.begin_sequence(current_sequence_base_address);
}
}

if let Some(write::Address::Constant(base_address)) =
current_sequence_base_address
{
// New offset from sequence base address in the transformed wasm binary
// can be different from one in the original wasm binary.
// Therefore, reculculating the new offset here.
let from_row_address = from_row.address() + from_base_address;
let row_address = (self.convert_address)(
from_row_address,
AddressSearchPreference::InclusiveFunctionEnd,
);

// either sequence_base_address or row_address is not resolved, ignore this entry.
if let Some(write::Address::Constant(address)) = row_address {
let address_offset = address.saturating_sub(base_address);

if from_row.end_sequence() {
program.end_sequence(address_offset);
from_base_address = from_row_address;
} else {
program.row().address_offset = address_offset;
program.row().op_index = from_row.op_index();
program.row().file = {
let file = from_row.file_index();
if file > files.len() as u64 {
return Err(write::ConvertError::InvalidFileIndex);
}
if file == 0 && program.version() <= 4 {
return Err(write::ConvertError::InvalidFileIndex);
}
files[(file - 1) as usize]
};
program.row().line = match from_row.line() {
Some(line) => line.get(),
None => 0,
};
program.row().column = match from_row.column() {
read::ColumnType::LeftEdge => 0,
read::ColumnType::Column(val) => val.get(),
};
program.row().discriminator = from_row.discriminator();
program.row().is_statement = from_row.is_stmt();
program.row().basic_block = from_row.basic_block();
program.row().prologue_end = from_row.prologue_end();
program.row().epilogue_begin = from_row.epilogue_begin();
program.row().isa = from_row.isa();
program.generate_row();
}
}
}

from_row.reset(from_program.header());
}
}
let encoding = from_program.header().encoding();
let line_encoding = from_program.header().line_encoding();
let mut convert = write::LineConvert::new(
self.dwarf,
from_program,
comp_name,
encoding,
line_encoding,
self.line_strings,
self.strings,
)?;

while let Some(sequence) = convert.read_sequence()? {
// begin new sequence if exists
let Some(from_start) = sequence.start else {
continue;
};
}
Ok(program)
}

/// write::LineString::from is not public function, cloned from https://github.com/gimli-rs/gimli/blob/master/src/write/line.rs#L1131
fn convert_line_string(
&mut self,
from_attr: read::AttributeValue<R>,
) -> write::ConvertResult<write::LineString> {
Ok(match from_attr {
read::AttributeValue::String(r) => write::LineString::String(r.to_slice()?.to_vec()),
read::AttributeValue::DebugStrRef(offset) => {
let r = self.debug_str.get_str(offset)?;
let id = self.strings.add(r.to_slice()?);
write::LineString::StringRef(id)
}
read::AttributeValue::DebugLineStrRef(offset) => {
let r = self.debug_line_str.get_str(offset)?;
let id = self.line_strings.add(r.to_slice()?);
write::LineString::LineStringRef(id)
let Some(write::Address::Constant(start)) =
(self.convert_address)(from_start, AddressSearchPreference::ExclusiveFunctionEnd)
else {
continue;
};
let Some(write::Address::Constant(end)) = (self.convert_address)(
from_start + sequence.length,
AddressSearchPreference::InclusiveFunctionEnd,
) else {
continue;
};
convert.begin_sequence(Some(write::Address::Constant(start)));
for mut row in sequence.rows {
let Some(write::Address::Constant(address)) = (self.convert_address)(
from_start + row.address_offset,
AddressSearchPreference::InclusiveFunctionEnd,
) else {
continue;
};
row.address_offset = address - start;
convert.generate_row(row);
}
_ => return Err(write::ConvertError::UnsupportedLineStringForm),
})
convert.end_sequence(end - start);
}
Ok(convert.program().0)
}
}

Expand Down Expand Up @@ -390,20 +224,18 @@ mod tests {
Some(write::Address::Constant(address + 0x10))
};

let empty_debug_str = Default::default();
let empty_debug_line_str = Default::default();
let empty_dwarf = Dwarf::default();

let mut line_strings = write::LineStringTable::default();
let mut strings = write::StringTable::default();
let mut convert_context = crate::module::debug::ConvertContext::new(
&empty_debug_str,
&empty_debug_line_str,
&empty_dwarf,
&mut strings,
&mut line_strings,
&convert_address,
);
convert_context
.convert_line_program(incomplete_debug_line)
.convert_line_program(incomplete_debug_line, None)
.unwrap();
}

Expand All @@ -415,11 +247,11 @@ mod tests {
(0x1000, AddressSearchPreference::ExclusiveFunctionEnd)
); // begin sequence
assert_eq!(
called_address_to_be_converted[1],
called_address_to_be_converted[2],
(0x1000, AddressSearchPreference::InclusiveFunctionEnd)
); // first line row
assert_eq!(
called_address_to_be_converted[2],
called_address_to_be_converted[1],
(0x1001, AddressSearchPreference::InclusiveFunctionEnd)
); // end sequence
}
Expand All @@ -441,14 +273,13 @@ mod tests {
let mut strings = write::StringTable::default();

let mut convert_context = crate::module::debug::ConvertContext::new(
&empty_dwarf.debug_str,
&empty_dwarf.debug_line_str,
&empty_dwarf,
&mut strings,
&mut line_strings,
&convert_address,
);
let converted_program = convert_context
.convert_line_program(incomplete_debug_line)
.convert_line_program(incomplete_debug_line, None)
.unwrap();

converted_program
Expand Down Expand Up @@ -487,14 +318,13 @@ mod tests {
let mut strings = write::StringTable::default();

let mut convert_context = crate::module::debug::ConvertContext::new(
&empty_dwarf.debug_str,
&empty_dwarf.debug_line_str,
&empty_dwarf,
&mut strings,
&mut line_strings,
&convert_address,
);
let converted_program = convert_context
.convert_line_program(incomplete_debug_line)
.convert_line_program(incomplete_debug_line, None)
.unwrap();

converted_program
Expand Down Expand Up @@ -581,8 +411,7 @@ mod tests {
let mut line_strings = write::LineStringTable::default();

let convert_context = crate::module::debug::ConvertContext::new(
&read_dwarf.debug_str,
&read_dwarf.debug_line_str,
&read_dwarf,
&mut strings,
&mut line_strings,
&convert_address,
Expand Down
3 changes: 1 addition & 2 deletions src/module/debug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ impl Emit for ModuleDebugData {
};

let mut convert_context = ConvertContext::new(
&from_dwarf.debug_str,
&from_dwarf.debug_line_str,
&from_dwarf,
&mut dwarf.strings,
&mut dwarf.line_strings,
&convert_address,
Expand Down

0 comments on commit 05f9bac

Please sign in to comment.