Skip to content

Display correct line numbers for multiple .text sections #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions objdiff-core/src/arch/mips.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,7 @@ impl ObjArch for ObjArchMips {
}
}
}
let line = obj
.line_info
.as_ref()
.and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b));
let line = section.line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
insts.push(ObjIns {
address: cur_addr as u64,
size: 4,
Expand Down
5 changes: 1 addition & 4 deletions objdiff-core/src/arch/ppc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,7 @@ impl ObjArch for ObjArchPpc {
}

ops.push(ins.op as u16);
let line = obj
.line_info
.as_ref()
.and_then(|map| map.range(..=cur_addr as u64).last().map(|(_, &b)| b));
let line = section.line_info.range(..=cur_addr as u64).last().map(|(_, &b)| b);
insts.push(ObjIns {
address: cur_addr as u64,
size: 4,
Expand Down
3 changes: 2 additions & 1 deletion objdiff-core/src/arch/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ impl ObjArch for ObjArchX86 {
.relocations
.iter()
.find(|r| r.address >= address && r.address < address + instruction.len() as u64);
let line = section.line_info.range(..=address).last().map(|(_, &b)| b);
output.ins = ObjIns {
address,
size: instruction.len() as u8,
Expand All @@ -81,7 +82,7 @@ impl ObjArch for ObjArchX86 {
args: vec![],
reloc: reloc.cloned(),
branch_dest: None,
line: obj.line_info.as_ref().and_then(|m| m.get(&address).cloned()),
line,
formatted: String::new(),
orig: None,
};
Expand Down
4 changes: 2 additions & 2 deletions objdiff-core/src/obj/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pub struct ObjSection {
pub symbols: Vec<ObjSymbol>,
pub relocations: Vec<ObjReloc>,
pub virtual_address: Option<u64>,
/// Line number info (.line or .debug_line section)
pub line_info: BTreeMap<u64, u64>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -128,8 +130,6 @@ pub struct ObjInfo {
pub sections: Vec<ObjSection>,
/// Common BSS symbols
pub common: Vec<ObjSymbol>,
/// Line number info (.line or .debug_line section)
pub line_info: Option<BTreeMap<u64, u64>>,
/// Split object metadata (.note.split section)
pub split_meta: Option<SplitMeta>,
}
Expand Down
96 changes: 65 additions & 31 deletions objdiff-core/src/obj/read.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::BTreeMap, fs, io::Cursor, path::Path};
use std::{fs, io::Cursor, path::Path};

use anyhow::{anyhow, bail, ensure, Context, Result};
use byteorder::{BigEndian, ReadBytesExt};
Expand Down Expand Up @@ -111,6 +111,7 @@ fn filter_sections(obj_file: &File<'_>, split_meta: Option<&SplitMeta>) -> Resul
symbols: Vec::new(),
relocations: Vec::new(),
virtual_address,
line_info: Default::default(),
});
}
result.sort_by(|a, b| a.name.cmp(&b.name));
Expand Down Expand Up @@ -268,26 +269,40 @@ fn relocations_by_section(
Ok(relocations)
}

fn line_info(obj_file: &File<'_>) -> Result<Option<BTreeMap<u64, u64>>> {
fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection]) -> Result<()> {
// DWARF 1.1
let mut map = BTreeMap::new();
if let Some(section) = obj_file.section_by_name(".line") {
if section.size() == 0 {
return Ok(None);
}
let data = section.uncompressed_data()?;
let mut reader = Cursor::new(data.as_ref());

let size = reader.read_u32::<BigEndian>()?;
let base_address = reader.read_u32::<BigEndian>()? as u64;
while reader.position() < size as u64 {
let line_number = reader.read_u32::<BigEndian>()? as u64;
let statement_pos = reader.read_u16::<BigEndian>()?;
if statement_pos != 0xFFFF {
log::warn!("Unhandled statement pos {}", statement_pos);
let mut text_sections = obj_file.sections().filter(|s| s.kind() == SectionKind::Text);
while reader.position() < data.len() as u64 {
let text_section_index = text_sections
.next()
.ok_or_else(|| anyhow!("Next text section not found for line info"))?
.index()
.0;
let start = reader.position();
let size = reader.read_u32::<BigEndian>()?;
let base_address = reader.read_u32::<BigEndian>()? as u64;
let Some(out_section) =
sections.iter_mut().find(|s| s.orig_index == text_section_index)
else {
// Skip line info for sections we filtered out
reader.set_position(start + size as u64);
continue;
};
let end = start + size as u64;
while reader.position() < end {
let line_number = reader.read_u32::<BigEndian>()? as u64;
let statement_pos = reader.read_u16::<BigEndian>()?;
if statement_pos != 0xFFFF {
log::warn!("Unhandled statement pos {}", statement_pos);
}
let address_delta = reader.read_u32::<BigEndian>()? as u64;
out_section.line_info.insert(base_address + address_delta, line_number);
log::debug!("Line: {:#x} -> {}", base_address + address_delta, line_number);
}
let address_delta = reader.read_u32::<BigEndian>()? as u64;
map.insert(base_address + address_delta, line_number);
}
}

Expand All @@ -308,22 +323,48 @@ fn line_info(obj_file: &File<'_>) -> Result<Option<BTreeMap<u64, u64>>> {
};
let dwarf = dwarf_cow.borrow(|section| gimli::EndianSlice::new(section, endian));
let mut iter = dwarf.units();
while let Some(header) = iter.next()? {
if let Some(header) = iter.next()? {
let unit = dwarf.unit(header)?;
if let Some(program) = unit.line_program.clone() {
let mut text_sections =
obj_file.sections().filter(|s| s.kind() == SectionKind::Text);
let section_index = text_sections
.next()
.ok_or_else(|| anyhow!("Next text section not found for line info"))?
.index()
.0;
let mut lines = sections
.iter_mut()
.find(|s| s.orig_index == section_index)
.map(|s| &mut s.line_info);

let mut rows = program.rows();
while let Some((_header, row)) = rows.next_row()? {
if let Some(line) = row.line() {
map.insert(row.address(), line.get());
if let (Some(line), Some(lines)) = (row.line(), &mut lines) {
lines.insert(row.address(), line.get());
}
if row.end_sequence() {
// The next row is the start of a new sequence, which means we must
// advance to the next .text section.
let section_index = text_sections
.next()
.ok_or_else(|| anyhow!("Next text section not found for line info"))?
.index()
.0;
lines = sections
.iter_mut()
.find(|s| s.orig_index == section_index)
.map(|s| &mut s.line_info);
}
}
}
}
if iter.next()?.is_some() {
log::warn!("Multiple units found in DWARF data, only processing the first");
}
}
if map.is_empty() {
return Ok(None);
}
Ok(Some(map))

Ok(())
}

pub fn read(obj_path: &Path) -> Result<ObjInfo> {
Expand All @@ -342,16 +383,9 @@ pub fn read(obj_path: &Path) -> Result<ObjInfo> {
section.relocations =
relocations_by_section(arch.as_ref(), &obj_file, section, split_meta.as_ref())?;
}
line_info(&obj_file, &mut sections)?;
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
Ok(ObjInfo {
arch,
path: obj_path.to_owned(),
timestamp,
sections,
common,
line_info: line_info(&obj_file)?,
split_meta,
})
Ok(ObjInfo { arch, path: obj_path.to_owned(), timestamp, sections, common, split_meta })
}

pub fn has_function(obj_path: &Path, symbol_name: &str) -> Result<bool> {
Expand Down