ld/pdb: Handle DEBUG_S_INLINEELINES data

The DEBUG_S_INLINEELINES block in the .debug$S section records the line
numbers in a source file covered by inlined functions. It's similar to
the DEBUG_S_LINES block, but as it references LF_FUNC_ID types we also
need to parse it to remap the type numbers.
This commit is contained in:
Mark Harmstone 2024-09-10 01:21:21 +01:00
parent 05ff7a4dfe
commit 909fcd9bc8
6 changed files with 371 additions and 0 deletions

View File

@ -161,6 +161,9 @@ static const uint32_t crc_table[] =
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
static bool remap_type (void *data, struct type_entry **map,
uint32_t type_num, uint32_t num_types);
/* Add a new stream to the PDB archive, and return its BFD. */
static bfd *
add_stream (bfd *pdb, const char *name, uint16_t *stream_num)
@ -1836,6 +1839,76 @@ calculate_symbols_size (uint8_t *data, uint32_t size, uint32_t *sym_size)
return true;
}
/* Parse the DEBUG_S_INLINEELINES data, which records the line numbers that
correspond to inlined functions. This is similar to DEBUG_S_LINES (see
handle_debugs_section), but rather than just copying we also need to remap
the numbers of the referenced LF_FUNC_ID types. */
static bool
parse_inlinee_lines (uint8_t *data, uint32_t size, uint8_t **bufptr,
struct type_entry **map, uint32_t num_types)
{
uint32_t version;
uint8_t *ptr;
unsigned int num_entries;
bfd_putl32 (DEBUG_S_INLINEELINES, *bufptr);
*bufptr += sizeof (uint32_t);
bfd_putl32 (size, *bufptr);
*bufptr += sizeof (uint32_t);
/* The inlinee lines data consists of a version uint32_t (0), followed by an
array of struct inlinee_source_line:
struct inlinee_source_line
{
uint32_t function_id;
uint32_t file_id;
uint32_t line_no;
};
(see InlineeSourceLine in cvinfo.h)
We're only interested here in the function_id, as we need to remap its
type number.
*/
if (size < sizeof (uint32_t))
{
einfo (_("%P: warning: truncated DEBUG_S_INLINEELINES data\n"));
return false;
}
version = bfd_getl32 (data + sizeof (uint32_t) + sizeof (uint32_t));
if (version != CV_INLINEE_SOURCE_LINE_SIGNATURE)
{
einfo (_("%P: warning: unexpected DEBUG_S_INLINEELINES version %u\n"),
version);
return false;
}
memcpy (*bufptr, data, size);
ptr = *bufptr + sizeof (uint32_t);
*bufptr += size;
num_entries = (size - sizeof (uint32_t)) / (3 * sizeof (uint32_t));
for (unsigned int i = 0; i < num_entries; i++)
{
uint32_t func_id;
func_id = bfd_getl32 (ptr);
if (!remap_type (ptr, map, func_id, num_types))
return false;
ptr += 3 * sizeof (uint32_t);
}
return true;
}
/* Parse the .debug$S section within an object file. */
static bool
handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
@ -1951,6 +2024,7 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
switch (type)
{
case DEBUG_S_FILECHKSMS:
case DEBUG_S_INLINEELINES:
c13_size += sizeof (uint32_t) + sizeof (uint32_t) + size;
if (c13_size % sizeof (uint32_t))
@ -2088,6 +2162,16 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
}
break;
case DEBUG_S_INLINEELINES:
if (!parse_inlinee_lines (data + off, size, &bufptr, map, num_types))
{
free (data);
free (symbuf);
return false;
}
break;
}
off += size;

View File

@ -241,10 +241,13 @@ struct optional_dbg_header
#define DEBUG_S_LINES 0xf2
#define DEBUG_S_STRINGTABLE 0xf3
#define DEBUG_S_FILECHKSMS 0xf4
#define DEBUG_S_INLINEELINES 0xf6
#define STRING_TABLE_SIGNATURE 0xeffeeffe
#define STRING_TABLE_VERSION 1
#define CV_INLINEE_SOURCE_LINE_SIGNATURE 0
/* VHdr in nmt.h */
struct string_table_header
{

View File

@ -0,0 +1,10 @@
tmpdir/pdb-inlineelines1-c13-info2: file format binary
Contents of section .data:
0000 f4000000 30000000 02000000 10016745 ....0.........gE
0010 2301efcd ab8998ba dcfe1023 45670000 #..........#Eg..
0020 08000000 100198ba dcfe1023 45676745 ...........#EggE
0030 2301efcd ab890000 f6000000 1c000000 #...............
0040 00000000 02100000 00000000 2a000000 ............*...
0050 03100000 18000000 1c000000 ............

View File

@ -0,0 +1,20 @@
.equ CV_SIGNATURE_C13, 4
.equ LF_STRING_ID, 0x1605
.equ CV_INLINEE_SOURCE_LINE_SIGNATURE, 0
.section ".debug$T", "rn"
.long CV_SIGNATURE_C13
/* Type 1000, string "hello" */
.string1:
.short .types_end - .string1 - 2
.short LF_STRING_ID
.long 0 /* sub-string */
.asciz "hello"
.byte 0xf2 /* padding */
.byte 0xf1 /* padding */
.types_end:

View File

@ -0,0 +1,160 @@
.equ CV_SIGNATURE_C13, 4
.equ DEBUG_S_STRINGTABLE, 0xf3
.equ DEBUG_S_FILECHKSMS, 0xf4
.equ DEBUG_S_INLINEELINES, 0xf6
.equ CHKSUM_TYPE_MD5, 1
.equ NUM_MD5_BYTES, 16
.equ T_VOID, 0x0003
.equ T_UINT4, 0x0075
.equ LF_ARGLIST, 0x1201
.equ LF_PROCEDURE, 0x1008
.equ LF_FUNC_ID, 0x1601
.equ LF_STRING_ID, 0x1605
.equ CV_INLINEE_SOURCE_LINE_SIGNATURE, 0
.section ".debug$T", "rn"
.long CV_SIGNATURE_C13
/* Type 1000, string "world" */
.string1:
.short .arglist1 - .string1 - 2
.short LF_STRING_ID
.long 0 /* sub-string */
.asciz "world"
.byte 0xf2 /* padding */
.byte 0xf1 /* padding */
/* Type 1001, arglist (uint32_t) */
.arglist1:
.short .proctype1 - .arglist1 - 2
.short LF_ARGLIST
.long 1 /* no. entries */
.long T_UINT4
/* Type 1002, procedure (return type T_VOID, arglist 1001) */
.proctype1:
.short .funcid1 - .proctype1 - 2
.short LF_PROCEDURE
.long T_VOID
.byte 0 /* calling convention */
.byte 0 /* attributes */
.short 1 /* no. parameters */
.long 0x1001
/* Type 1003, func ID for proc1 */
.funcid1:
.short .funcid2 - .funcid1 - 2
.short LF_FUNC_ID
.long 0 /* parent scope */
.long 0x1002 /* type */
.asciz "proc1"
.byte 0xf2 /* padding */
.byte 0xf1 /* padding */
/* Type 1004, func ID for proc2 */
.funcid2:
.short .types_end - .funcid2 - 2
.short LF_FUNC_ID
.long 0 /* parent scope */
.long 0x1002 /* type */
.asciz "proc2"
.byte 0xf2 /* padding */
.byte 0xf1 /* padding */
.types_end:
.section ".debug$S", "rn"
.long CV_SIGNATURE_C13
/*
*** STRINGTABLE
00000000
00000001 foo.c
00000007 bar.c
*/
.long DEBUG_S_STRINGTABLE
.long .strings_end - .strings_start
.strings_start:
.asciz ""
.src1:
.asciz "foo.c"
.src2:
.asciz "bar.c"
.strings_end:
.balign 4
/*
*** FILECHKSUMS
FileId St.Offset Cb Type ChksumBytes
0 00000001 10 MD5 67452301EFCDAB8998BADCFE10234567
18 00000007 10 MD5 98BADCFE1023456767452301EFCDAB89
*/
.long DEBUG_S_FILECHKSMS
.long .chksms_end - .chksms_start
.chksms_start:
.file1:
.long .src1 - .strings_start
.byte NUM_MD5_BYTES
.byte CHKSUM_TYPE_MD5
.long 0x01234567
.long 0x89abcdef
.long 0xfedcba98
.long 0x67452310
.short 0 /* padding */
.file2:
.long .src2 - .strings_start
.byte NUM_MD5_BYTES
.byte CHKSUM_TYPE_MD5
.long 0xfedcba98
.long 0x67452310
.long 0x01234567
.long 0x89abcdef
.short 0 /* padding */
.chksms_end:
.balign 4
/*
*** INLINEE LINES
InlineeId FileId StaringLine
1003 0 42
1004 18 28
*/
.long DEBUG_S_INLINEELINES
.long .lines_end - .lines_start
.lines_start:
.long CV_INLINEE_SOURCE_LINE_SIGNATURE
.long 0x1003
.long .file1 - .chksms_start
.long 42
.long 0x1004
.long .file2 - .chksms_start
.long 28
.lines_end:

View File

@ -1744,6 +1744,99 @@ proc test9 { } {
}
}
proc test10 { } {
global as
global ar
global ld
global objdump
global srcdir
global subdir
if ![ld_assemble $as $srcdir/$subdir/pdb-inlineelines1a.s tmpdir/pdb-inlineelines1a.o] {
unsupported "Build pdb-inlineelines1a.o"
return
}
if ![ld_assemble $as $srcdir/$subdir/pdb-inlineelines1b.s tmpdir/pdb-inlineelines1b.o] {
unsupported "Build pdb-inlineelines1a.o"
return
}
if ![ld_link $ld "tmpdir/pdb-inlineelines1.exe" "--pdb=tmpdir/pdb-inlineelines1.pdb tmpdir/pdb-inlineelines1a.o tmpdir/pdb-inlineelines1b.o"] {
unsupported "Create PE image with PDB file"
return
}
# read relevant bits from DBI stream
set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-inlineelines1.pdb 0003"]
if ![string match "" $exec_output] {
fail "Could not extract DBI stream"
return
} else {
pass "Extracted DBI stream"
}
set fi [open tmpdir/0003]
fconfigure $fi -translation binary
seek $fi 24
# read substream sizes
set data [read $fi 4]
binary scan $data i mod_info_size
seek $fi 36 current
set mod_info [read $fi $mod_info_size]
close $fi
# check C13 info in second module
# We're interested here that the inlinee function IDs get rewritten:
# 1003 -> 1002, 1004 -> 1003. The numbers are lower because linking splits
# the types into two separate streams, numbered individually.
# This is what cvdump.exe -inll pdb-inlineelines1.pdb should look like:
# *** INLINEE LINES
#
# InlineeId FileId StaringLine
# 1002 0 42
# 1003 1 28
# For some reason it numbers file IDs in bytes for object files but as an
# index for PDBs, but they're stored on disk the same way.
set fn1_end [string first \000 $mod_info 64]
set fn2_end [string first \000 $mod_info [expr $fn1_end + 1]]
set off [expr $fn2_end + 1]
if { [expr $off % 4] != 0 } {
set off [expr $off + 4 - ($off % 4)]
}
set c13_info [extract_c13_info "tmpdir/pdb-inlineelines1.pdb" [string range $mod_info $off [expr $off + 63]]]
set fi [open tmpdir/pdb-inlineelines1-c13-info2 w]
fconfigure $fi -translation binary
puts -nonewline $fi $c13_info
close $fi
set exp [file_contents "$srcdir/$subdir/pdb-inlineelines1-c13-info2.d"]
set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-inlineelines1-c13-info2"]
if [string match $exp $got] {
pass "Correct C13 info for second module"
} else {
fail "Incorrect C13 info for second module"
}
}
test1
test2
test3
@ -1753,3 +1846,4 @@ test6
test7
test8
test9
test10