Skip to content

Commit ea94abd

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

File tree

1 file changed

+122
-4
lines changed

1 file changed

+122
-4
lines changed

objdiff-core/src/obj/read.rs

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
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::{
8+
endian::LittleEndian as LE,
9+
pe::{ImageAuxSymbolFunctionBeginEnd, ImageLinenumber},
10+
read::coff::{CoffFile, CoffHeader, ImageSymbol},
811
Architecture, BinaryFormat, File, Object, ObjectSection, ObjectSymbol, RelocationTarget,
9-
SectionIndex, SectionKind, Symbol, SymbolKind, SymbolScope, SymbolSection,
12+
SectionIndex, SectionKind, Symbol, SymbolIndex, SymbolKind, SymbolScope, SymbolSection,
1013
};
1114

1215
use crate::{
@@ -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,121 @@ 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) = sections.iter_mut().find(|s| s.orig_index == sect.index().0) else {
520+
continue;
521+
};
522+
523+
// Turn the line numbers into an ImageLinenumber slice.
524+
let Some(linenums) =
525+
&obj_data.get(ptr_linenums..ptr_linenums + num_linenums * size_of::<ImageLinenumber>())
526+
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))
565+
else {
566+
continue;
567+
};
568+
569+
// Get the .bf symbol associated with this symbol. To do so, we
570+
// look at the Function Auxillary Record's tag_index, which is
571+
// an index in the symbol table pointing to our .bf symbol.
572+
if aux_fun.tag_index.get(LE) == 0 {
573+
continue;
574+
}
575+
let Ok(bf_symbol) =
576+
symbol_table.symbol(SymbolIndex(aux_fun.tag_index.get(LE) as usize))
577+
else {
578+
continue;
579+
};
580+
// Do some sanity checks that we are, indeed, looking at a .bf
581+
// symbol.
582+
if bf_symbol.name(symbol_table.strings()) != Ok(b".bf") {
583+
continue;
584+
}
585+
// Get the Function Begin/End Auxillary Record associated with
586+
// our .bf symbol, where we'll fine the linenumber of the start
587+
// of our function.
588+
let Ok(bf_aux) = symbol_table.get::<ImageAuxSymbolFunctionBeginEnd>(
589+
SymbolIndex(aux_fun.tag_index.get(LE) as usize),
590+
1,
591+
) else {
592+
continue;
593+
};
594+
// Set cur_fun_start_linenumber so the following linenumber
595+
// records will know at what line the current function start.
596+
cur_fun_start_linenumber = Some(bf_aux.linenumber.get(LE) as u32);
597+
// Let's also synthesize a line number record from the start of
598+
// the function, as the linenumber records don't always cover it.
599+
out_section.line_info.insert(
600+
sect.address() + symbol.value() as u64,
601+
bf_aux.linenumber.get(LE) as u32,
602+
);
603+
} else if let Some(cur_linenumber) = cur_fun_start_linenumber {
604+
let vaddr = linenum.symbol_table_index_or_virtual_address.get(LE);
605+
out_section
606+
.line_info
607+
.insert(sect.address() + vaddr as u64, cur_linenumber + line_number as u32);
608+
}
609+
}
610+
}
493611
Ok(())
494612
}
495613

@@ -620,7 +738,7 @@ pub fn parse(data: &[u8], config: &DiffObjConfig) -> Result<ObjInfo> {
620738
if config.combine_data_sections {
621739
combine_data_sections(&mut sections)?;
622740
}
623-
line_info(&obj_file, &mut sections)?;
741+
line_info(&obj_file, &mut sections, data)?;
624742
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
625743
let extab = exception_tables(&mut sections, &obj_file)?;
626744
Ok(ObjInfo { arch, path: None, timestamp: None, sections, common, extab, split_meta })

0 commit comments

Comments
 (0)