Skip to content

Commit 448f147

Browse files
committed
Add COFF line number support
1 parent 9d07343 commit 448f147

File tree

1 file changed

+111
-4
lines changed

1 file changed

+111
-4
lines changed

objdiff-core/src/obj/read.rs

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
use std::{collections::HashSet, fs, io::Cursor, path::Path};
1+
use std::{collections::HashSet, fs, io::Cursor, mem::size_of, path::Path};
22

33
use anyhow::{anyhow, bail, ensure, Context, Result};
44
use cwextab::decode_extab;
55
use filetime::FileTime;
66
use flagset::Flags;
77
use object::{
88
Architecture, BinaryFormat, File, Object, ObjectSection, ObjectSymbol, RelocationTarget,
9-
SectionIndex, SectionKind, Symbol, SymbolKind, SymbolScope, SymbolSection,
9+
SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolScope, SymbolSection,
1010
};
11+
use object::read::coff::{CoffFile, CoffHeader, ImageSymbol};
12+
use object::endian::LittleEndian as LE;
13+
use object::pe::{ImageAuxSymbolFunctionBeginEnd, ImageLinenumber};
1114

1215
use crate::{
1316
arch::{new_arch, ObjArch},
@@ -401,7 +404,7 @@ fn relocations_by_section(
401404
Ok(relocations)
402405
}
403406

404-
fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection]) -> Result<()> {
407+
fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection], obj_data: &[u8]) -> Result<()> {
405408
// DWARF 1.1
406409
if let Some(section) = obj_file.section_by_name(".line") {
407410
let data = section.uncompressed_data()?;
@@ -490,6 +493,110 @@ fn line_info(obj_file: &File<'_>, sections: &mut [ObjSection]) -> Result<()> {
490493
}
491494
}
492495

496+
// COFF
497+
if let File::Coff(coff) = obj_file {
498+
line_info_coff(coff, sections, obj_data)?;
499+
}
500+
501+
Ok(())
502+
}
503+
504+
fn line_info_coff(coff: &CoffFile, sections: &mut [ObjSection], obj_data: &[u8]) -> Result<()> {
505+
let symbol_table = coff.coff_header().symbols(obj_data)?;
506+
507+
// Enumerate over all sections.
508+
for sect in coff.sections() {
509+
let ptr_linenums = sect.coff_section().pointer_to_linenumbers.get(LE) as usize;
510+
let num_linenums = sect.coff_section().number_of_linenumbers.get(LE) as usize;
511+
512+
// If we have no line number, skip this section.
513+
if num_linenums == 0 {
514+
continue;
515+
}
516+
517+
// Find this section in our out_section. If it's not in out_section,
518+
// skip it.
519+
let Some(out_section) =
520+
sections.iter_mut().find(|s| s.orig_index == sect.index().0)
521+
else {
522+
continue;
523+
};
524+
525+
// Turn the line numbers into an ImageLinenumber slice.
526+
let Some(linenums) = &obj_data.get(ptr_linenums..ptr_linenums + num_linenums * size_of::<ImageLinenumber>()) else {
527+
continue;
528+
};
529+
let Ok(linenums) = object::pod::slice_from_all_bytes::<ImageLinenumber>(linenums) else {
530+
continue;
531+
};
532+
533+
// In COFF, the line numbers are stored relative to the start of the
534+
// function. Because of this, we need to know the line number where the
535+
// function starts, so we can sum the two and get the line number
536+
// relative to the start of the file.
537+
//
538+
// This variable stores the line number where the function currently
539+
// being processed starts. It is set to None when we failed to find the
540+
// line number of the start of the function.
541+
let mut cur_fun_start_linenumber = None;
542+
for linenum in linenums {
543+
let line_number = linenum.linenumber.get(LE);
544+
if line_number == 0 {
545+
// Starting a new function. We need to find the line where that
546+
// function is located in the file. To do this, we need to find
547+
// the `.bf` symbol "associated" with this function. The .bf
548+
// symbol will have a Function Begin/End Auxillary Record, which
549+
// contains the line number of the start of the function.
550+
551+
// First, set cur_fun_start_linenumber to None. If we fail to
552+
// find the start of the function, this will make sure the
553+
// subsequent line numbers will be ignored until the next start
554+
// of function.
555+
cur_fun_start_linenumber = None;
556+
557+
// Get the symbol associated with this function. We'll need it
558+
// for logging purposes, but also to acquire its Function
559+
// Auxillary Record, which tells us where to find our .bf symbol.
560+
let symtable_entry = linenum.symbol_table_index_or_virtual_address.get(LE);
561+
let Ok(symbol) = symbol_table.symbol(SymbolIndex(symtable_entry as usize)) else {
562+
continue;
563+
};
564+
let Ok(aux_fun) = symbol_table.aux_function(SymbolIndex(symtable_entry as usize)) else {
565+
continue;
566+
};
567+
568+
// Get the .bf symbol associated with this symbol. To do so, we
569+
// look at the Function Auxillary Record's tag_index, which is
570+
// an index in the symbol table pointing to our .bf symbol.
571+
if aux_fun.tag_index.get(LE) == 0 {
572+
continue;
573+
}
574+
let Ok(bf_symbol) = symbol_table.symbol(SymbolIndex(aux_fun.tag_index.get(LE) as usize)) else {
575+
continue;
576+
};
577+
// Do some sanity checks that we are, indeed, looking at a .bf
578+
// symbol.
579+
if bf_symbol.name(symbol_table.strings()) != Ok(b".bf") {
580+
continue;
581+
}
582+
// Get the Function Begin/End Auxillary Record associated with
583+
// our .bf symbol, where we'll fine the linenumber of the start
584+
// of our function.
585+
let Ok(bf_aux) = symbol_table.get::<ImageAuxSymbolFunctionBeginEnd>(SymbolIndex(aux_fun.tag_index.get(LE) as usize), 1) else {
586+
continue;
587+
};
588+
// Set cur_fun_start_linenumber so the following linenumber
589+
// records will know at what line the current function start.
590+
cur_fun_start_linenumber = Some(bf_aux.linenumber.get(LE) as u32);
591+
// Let's also synthesize a line number record from the start of
592+
// the function, as the linenumber records don't always cover it.
593+
out_section.line_info.insert(sect.address() + symbol.value() as u64, bf_aux.linenumber.get(LE) as u32);
594+
} else if let Some(cur_linenumber) = cur_fun_start_linenumber {
595+
let vaddr = linenum.symbol_table_index_or_virtual_address.get(LE);
596+
out_section.line_info.insert(sect.address() + vaddr as u64, cur_linenumber + line_number as u32);
597+
}
598+
}
599+
}
493600
Ok(())
494601
}
495602

@@ -620,7 +727,7 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<ObjInfo> {
620727
if config.combine_data_sections {
621728
combine_data_sections(&mut sections)?;
622729
}
623-
line_info(&obj_file, &mut sections)?;
730+
line_info(&obj_file, &mut sections, &*data)?;
624731
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
625732
let extab = exception_tables(&mut sections, &obj_file)?;
626733
Ok(ObjInfo { arch, path: None, timestamp: None, sections, common, extab, split_meta })

0 commit comments

Comments
 (0)