ld: Write types into IPI stream of PDB

This commit is contained in:
Mark Harmstone 2022-12-09 01:52:36 +00:00 committed by Alan Modra
parent d5b4c0ddb9
commit fca9096a94
8 changed files with 699 additions and 18 deletions

204
ld/pdb.c
View File

@ -1124,13 +1124,16 @@ is_name_anonymous (char *name, size_t len)
already seen add it to types (for TPI types) or ids (for IPI types). */
static bool
handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
uint32_t num_types, struct types *types)
uint32_t num_types, struct types *types,
struct types *ids)
{
uint16_t size, type;
void **slot;
hashval_t hash;
bool other_hash = false;
uint32_t cv_hash;
struct types *t;
bool ipi = false;
size = bfd_getl16 (data) + sizeof (uint16_t);
type = bfd_getl16 (data + sizeof (uint16_t));
@ -1911,6 +1914,168 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
/* Does not reference any types, nothing to be done. */
break;
case LF_STRING_ID:
{
struct lf_string_id *str = (struct lf_string_id *) data;
size_t string_len;
if (size < offsetof (struct lf_string_id, string))
{
einfo (_("%P: warning: truncated CodeView type record"
" LF_STRING_ID\n"));
return false;
}
if (!remap_type (&str->substring, map, type_num, num_types))
return false;
string_len = strnlen (str->string,
size - offsetof (struct lf_string_id, string));
if (string_len == size - offsetof (struct lf_string_id, string))
{
einfo (_("%P: warning: string for LF_STRING_ID has no"
" terminating zero\n"));
return false;
}
ipi = true;
break;
}
case LF_SUBSTR_LIST:
{
uint32_t num_entries;
struct lf_arglist *ssl = (struct lf_arglist *) data;
if (size < offsetof (struct lf_arglist, args))
{
einfo (_("%P: warning: truncated CodeView type record"
" LF_SUBSTR_LIST\n"));
return false;
}
num_entries = bfd_getl32 (&ssl->num_entries);
if (size < offsetof (struct lf_arglist, args)
+ (num_entries * sizeof (uint32_t)))
{
einfo (_("%P: warning: truncated CodeView type record"
" LF_SUBSTR_LIST\n"));
return false;
}
for (uint32_t i = 0; i < num_entries; i++)
{
if (!remap_type (&ssl->args[i], map, type_num, num_types))
return false;
}
ipi = true;
break;
}
case LF_BUILDINFO:
{
uint16_t num_entries;
struct lf_build_info *bi = (struct lf_build_info *) data;
if (size < offsetof (struct lf_build_info, strings))
{
einfo (_("%P: warning: truncated CodeView type record"
" LF_BUILDINFO\n"));
return false;
}
num_entries = bfd_getl16 (&bi->count);
if (size < offsetof (struct lf_build_info, strings)
+ (num_entries * sizeof (uint32_t)))
{
einfo (_("%P: warning: truncated CodeView type record"
" LF_BUILDINFO\n"));
return false;
}
for (uint32_t i = 0; i < num_entries; i++)
{
if (!remap_type (&bi->strings[i], map, type_num, num_types))
return false;
}
ipi = true;
break;
}
case LF_FUNC_ID:
{
struct lf_func_id *func = (struct lf_func_id *) data;
size_t name_len;
if (size < offsetof (struct lf_func_id, name))
{
einfo (_("%P: warning: truncated CodeView type record"
" LF_FUNC_ID\n"));
return false;
}
if (!remap_type (&func->parent_scope, map, type_num, num_types))
return false;
if (!remap_type (&func->function_type, map, type_num, num_types))
return false;
name_len = strnlen (func->name,
size - offsetof (struct lf_func_id, name));
if (name_len == size - offsetof (struct lf_func_id, name))
{
einfo (_("%P: warning: string for LF_FUNC_ID has no"
" terminating zero\n"));
return false;
}
ipi = true;
break;
}
case LF_MFUNC_ID:
{
struct lf_mfunc_id *mfunc = (struct lf_mfunc_id *) data;
size_t name_len;
if (size < offsetof (struct lf_mfunc_id, name))
{
einfo (_("%P: warning: truncated CodeView type record"
" LF_MFUNC_ID\n"));
return false;
}
if (!remap_type (&mfunc->parent_type, map, type_num, num_types))
return false;
if (!remap_type (&mfunc->function_type, map, type_num, num_types))
return false;
name_len = strnlen (mfunc->name,
size - offsetof (struct lf_mfunc_id, name));
if (name_len == size - offsetof (struct lf_mfunc_id, name))
{
einfo (_("%P: warning: string for LF_MFUNC_ID has no"
" terminating zero\n"));
return false;
}
ipi = true;
break;
}
default:
einfo (_("%P: warning: unrecognized CodeView type %v\n"), type);
return false;
@ -1918,7 +2083,9 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
hash = iterative_hash (data, size, 0);
slot = htab_find_slot_with_hash (types->hashmap, data, hash, INSERT);
t = ipi ? ids : types;
slot = htab_find_slot_with_hash (t->hashmap, data, hash, INSERT);
if (!slot)
return false;
@ -1931,7 +2098,7 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
e = (struct type_entry *) *slot;
e->next = NULL;
e->index = types->num_types;
e->index = t->num_types;
if (other_hash)
e->cv_hash = cv_hash;
@ -1940,16 +2107,16 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
memcpy (e->data, data, size);
if (types->last)
types->last->next = e;
if (t->last)
t->last->next = e;
else
types->first = e;
t->first = e;
types->last = e;
t->last = e;
map[type_num] = e;
types->num_types++;
t->num_types++;
}
else /* duplicate */
{
@ -1962,7 +2129,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
/* Parse the .debug$T section of a module, and pass any type definitions
found to handle_type. */
static bool
handle_debugt_section (asection *s, bfd *mod, struct types *types)
handle_debugt_section (asection *s, bfd *mod, struct types *types,
struct types *ids)
{
bfd_byte *data = NULL;
size_t off;
@ -2019,7 +2187,7 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types)
size = bfd_getl16 (data + off);
if (!handle_type (data + off, map, type_num, num_types, types))
if (!handle_type (data + off, map, type_num, num_types, types, ids))
{
free (data);
free (map);
@ -2044,7 +2212,8 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
struct string_table *strings,
uint32_t *c13_info_size,
struct mod_source_files *mod_source,
bfd *abfd, struct types *types)
bfd *abfd, struct types *types,
struct types *ids)
{
uint8_t int_buf[sizeof (uint32_t)];
uint8_t *c13_info = NULL;
@ -2068,7 +2237,7 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
}
else if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t))
{
if (!handle_debugt_section (s, mod, types))
if (!handle_debugt_section (s, mod, types, ids))
{
free (c13_info);
free (mod_source->files);
@ -2113,7 +2282,7 @@ static bool
create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
uint32_t *size, struct string_table *strings,
struct source_files_info *source,
struct types *types)
struct types *types, struct types *ids)
{
uint8_t *ptr;
unsigned int mod_num;
@ -2202,7 +2371,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
if (!populate_module_stream (stream, in, &sym_byte_size,
strings, &c13_info_size,
&source->mods[mod_num], abfd,
types))
types, ids))
{
for (unsigned int i = 0; i < source->mod_count; i++)
{
@ -2525,7 +2694,8 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
uint16_t sym_rec_stream_num,
uint16_t publics_stream_num,
struct string_table *strings,
struct types *types)
struct types *types,
struct types *ids)
{
struct pdb_dbi_stream_header h;
struct optional_dbg_header opt;
@ -2537,7 +2707,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
source.mods = NULL;
if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
strings, &source, types))
strings, &source, types, ids))
return false;
if (!create_section_contrib_substream (abfd, &sc, &sc_size))
@ -3210,7 +3380,7 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num,
sym_rec_stream_num, publics_stream_num,
&strings, &types))
&strings, &types, &ids))
{
einfo (_("%P: warning: cannot populate DBI stream "
"in PDB file: %E\n"));

View File

@ -54,6 +54,11 @@
#define LF_METHOD 0x150f
#define LF_NESTTYPE 0x1510
#define LF_ONEMETHOD 0x1511
#define LF_FUNC_ID 0x1601
#define LF_MFUNC_ID 0x1602
#define LF_BUILDINFO 0x1603
#define LF_SUBSTR_LIST 0x1604
#define LF_STRING_ID 0x1605
#define LF_CHAR 0x8000
#define LF_SHORT 0x8001
@ -265,7 +270,7 @@ struct lf_pointer
uint32_t attributes;
} ATTRIBUTE_PACKED;
/* lfArgList in cvinfo.h */
/* lfArgList in cvinfo.h (used for both LF_ARGLIST and LF_SUBSTR_LIST) */
struct lf_arglist
{
uint16_t size;
@ -474,6 +479,44 @@ struct lf_nest_type
char name[];
} ATTRIBUTE_PACKED;
/* lfStringId in cvinfo.h */
struct lf_string_id
{
uint16_t size;
uint16_t kind;
uint32_t substring;
char string[];
} ATTRIBUTE_PACKED;
/* lfBuildInfo in cvinfo.h */
struct lf_build_info
{
uint16_t size;
uint16_t kind;
uint16_t count;
uint32_t strings[];
} ATTRIBUTE_PACKED;
/* lfFuncId in cvinfo.h */
struct lf_func_id
{
uint16_t size;
uint16_t kind;
uint32_t parent_scope;
uint32_t function_type;
char name[];
} ATTRIBUTE_PACKED;
/* lfMFuncId in cvinfo.h */
struct lf_mfunc_id
{
uint16_t size;
uint16_t kind;
uint32_t parent_type;
uint32_t function_type;
char name[];
} ATTRIBUTE_PACKED;
extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
#endif

View File

@ -0,0 +1,8 @@
*: file format binary
Contents of section .data:
0000 75cf0100 8d660300 f2a20300 aea00000 *
0010 ef990300 223d0000 d6b60000 24070100 *
0020 7f220100 f6d10200 16100200 010a0300 *
0030 0b4f0300 12690300 a56d0300 *

View File

@ -0,0 +1,5 @@
*: file format binary
Contents of section .data:
0000 00100000 00000000 *

View File

@ -0,0 +1,20 @@
*: file format binary
Contents of section .data:
0000 0e000516 00000000 74657374 00f3f2f1 ........test....
0010 0a000516 00000000 666f6f00 0a000516 ........foo.....
0020 00000000 62617200 0e000416 02000000 ....bar.........
0030 01100000 02100000 0a000516 03100000 ................
0040 62617a00 0e000516 00000000 2f746d70 baz........./tmp
0050 00f3f2f1 0a000516 00000000 67636300 ............gcc.
0060 0e000516 00000000 746d702e 6300f2f1 ........tmp.c...
0070 0e000516 00000000 746d702e 70646200 ........tmp.pdb.
0080 12000516 00000000 2d67636f 64657669 ........-gcodevi
0090 657700f1 1a000316 05000510 00000610 ew..............
00a0 00000710 00000810 00000910 0000f2f1 ................
00b0 12000516 00000000 6e616d65 73706163 ........namespac
00c0 6500f2f1 12000116 00000000 01100000 e...............
00d0 66756e63 3100f2f1 12000116 0b100000 func1...........
00e0 01100000 66756e63 3200f2f1 12000216 ....func2.......
00f0 02100000 04100000 6d657468 6f6400f1 ........method..

View File

@ -0,0 +1,42 @@
.equ CV_SIGNATURE_C13, 4
.equ T_VOID, 0x0003
.equ T_INT4, 0x0074
.equ LF_PROCEDURE, 0x1008
.equ LF_MFUNCTION, 0x1009
.equ LF_POINTER, 0x1002
.equ LF_ARGLIST, 0x1201
.equ LF_FIELDLIST, 0x1203
.equ LF_CLASS, 0x1504
.equ LF_ONEMETHOD, 0x1511
.equ LF_FUNC_ID, 0x1601
.equ LF_MFUNC_ID, 0x1602
.equ LF_BUILDINFO, 0x1603
.equ LF_SUBSTR_LIST, 0x1604
.equ LF_STRING_ID, 0x1605
.equ CV_PTR_64, 0xc
.section ".debug$T", "rn"
.long CV_SIGNATURE_C13
# Type 1000, string "test"
.string1:
.short .string2 - .string1 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "test"
.byte 0xf3
.byte 0xf2
.byte 0xf1
# Type 1001, string "foo"
.string2:
.short .types_end - .string2 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "foo"
.types_end:

View File

@ -0,0 +1,221 @@
.equ CV_SIGNATURE_C13, 4
.equ T_VOID, 0x0003
.equ T_INT4, 0x0074
.equ LF_PROCEDURE, 0x1008
.equ LF_MFUNCTION, 0x1009
.equ LF_POINTER, 0x1002
.equ LF_ARGLIST, 0x1201
.equ LF_FIELDLIST, 0x1203
.equ LF_CLASS, 0x1504
.equ LF_ONEMETHOD, 0x1511
.equ LF_FUNC_ID, 0x1601
.equ LF_MFUNC_ID, 0x1602
.equ LF_BUILDINFO, 0x1603
.equ LF_SUBSTR_LIST, 0x1604
.equ LF_STRING_ID, 0x1605
.equ CV_PTR_64, 0xc
.section ".debug$T", "rn"
.long CV_SIGNATURE_C13
# Type 1000, string "foo"
.string1:
.short .string2 - .string1 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "foo"
# Type 1001, string "bar"
.string2:
.short .substrlist1 - .string2 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "bar"
# Type 1002, substr list of "foo" and "bar"
.substrlist1:
.short .string3 - .substrlist1 - 2
.short LF_SUBSTR_LIST
.long 2 # count
.long 0x1000
.long 0x1001
# Type 1003, string "baz" referencing substr list 1002
.string3:
.short .string4 - .string3 - 2
.short LF_STRING_ID
.long 0x1002
.asciz "baz"
# Type 1004, string "/tmp" (build directory)
.string4:
.short .string5 - .string4 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "/tmp"
.byte 0xf3 # padding
.byte 0xf2 # padding
.byte 0xf1 # padding
# Type 1005, string "gcc" (compiler)
.string5:
.short .string6 - .string5 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "gcc"
# Type 1006, string "tmp.c" (source file)
.string6:
.short .string7 - .string6 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "tmp.c"
.byte 0xf2 # padding
.byte 0xf1 # padding
# Type 1007, string "tmp.pdb" (PDB file)
.string7:
.short .string8 - .string7 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "tmp.pdb"
# Type 1008, string "-gcodeview" (command arguments)
.string8:
.short .buildinfo1 - .string8 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "-gcodeview"
.byte 0xf1 # padding
# The 1009, build info
.buildinfo1:
.short .string9 - .buildinfo1 - 2
.short LF_BUILDINFO
.short 5 # count
.long 0x1004 # build directory
.long 0x1005 # compiler
.long 0x1006 # source file
.long 0x1007 # PDB file
.long 0x1008 # command arguments
.byte 0xf2 # padding
.byte 0xf1 # padding
# Type 100a, string "namespace"
.string9:
.short .arglist1 - .string9 - 2
.short LF_STRING_ID
.long 0 # sub-string
.asciz "namespace"
.byte 0xf2 # padding
.byte 0xf1 # padding
# Type 100b, arg list of type T_INT4
.arglist1:
.short .proc1 - .arglist1 - 2
.short LF_ARGLIST
.long 1 # no. entries
.long T_INT4
# Type 100c, procedure, return type T_VOID, arg list 100b
.proc1:
.short .func1 - .proc1 - 2
.short LF_PROCEDURE
.long T_VOID
.byte 0 # calling convention
.byte 0 # attributes
.short 1 # no. parameters
.long 0x100b
# Type 100d, function "func1"
.func1:
.short .func2 - .func1 - 2
.short LF_FUNC_ID
.long 0 # parent scope
.long 0x100c # type
.asciz "func1"
.byte 0xf2 # padding
.byte 0xf1 # padding
# Type 100e, function "func2" within scope "namespace"
.func2:
.short .class1 - .func2 - 2
.short LF_FUNC_ID
.long 0x100a # parent scope
.long 0x100c # type
.asciz "func2"
.byte 0xf2 # padding
.byte 0xf1 # padding
# Type 100f, forward declaration of class foo
.class1:
.short .ptr1 - .class1 - 2
.short LF_CLASS
.short 0 # no. members
.short 0x80 # property (has unique name, forward declaration)
.long 0 # field list
.long 0 # type derived from
.long 0 # type of vshape table
.short 0 # size
.asciz "foo" # name
.byte 0xf2 # padding
.byte 0xf1 # padding
# Type 1010, pointer to 100f
.ptr1:
.short .mfunction1 - .ptr1 - 2
.short LF_POINTER
.long 0x100f
.long (8 << 13) | CV_PTR_64
# Type 1011, member function of 100f, return type void, arg list 100b
.mfunction1:
.short .fieldlist1 - .mfunction1 - 2
.short LF_MFUNCTION
.long T_VOID
.long 0x100f
.long 0x1010 # type of "this" pointer
.byte 0 # calling convention
.byte 0 # attributes
.short 1 # no. parameters
.long 0x100b # arg list
.long 0 # "this" adjustment
# Type 1012, field list for class foo
.fieldlist1:
.short .class2 - .fieldlist1 - 2
.short LF_FIELDLIST
.short LF_ONEMETHOD
.short 0 # method attribute
.long 0x1010 # method type
.asciz "method"
.byte 0xf1
# Type 1013, actual declaration of class foo
.class2:
.short .mfunc1 - .class2 - 2
.short LF_CLASS
.short 0 # no. members
.short 0 # property
.long 0x1012 # field list
.long 0 # type derived from
.long 0 # type of vshape table
.short 0 # size
.asciz "foo" # name
.byte 0xf2 # padding
.byte 0xf1 # padding
# Type 1014, function "method" within class "foo"
.mfunc1:
.short .types_end - .mfunc1 - 2
.short LF_MFUNC_ID
.long 0x100f # parent class
.long 0x1011 # function type
.asciz "method"
.byte 0xf1 # padding
.types_end:

View File

@ -1148,8 +1148,180 @@ proc test5 { } {
}
}
proc test6 { } {
global as
global ar
global ld
global objdump
global srcdir
global subdir
if ![ld_assemble $as $srcdir/$subdir/pdb-types2a.s tmpdir/pdb-types2a.o] {
unsupported "Build pdb-types2a.o"
return
}
if ![ld_assemble $as $srcdir/$subdir/pdb-types2b.s tmpdir/pdb-types2b.o] {
unsupported "Build pdb-types2b.o"
return
}
if ![ld_link $ld "tmpdir/pdb-types2.exe" "--pdb=tmpdir/pdb-types2.pdb tmpdir/pdb-types2a.o tmpdir/pdb-types2b.o"] {
unsupported "Create PE image with PDB file"
return
}
set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types2.pdb 0004"]
if ![string match "" $exec_output] {
fail "Could not extract IPI stream"
return
} else {
pass "Extracted IPI stream"
}
# check values in IPI header, and save anything interesting
set fi [open tmpdir/0004]
fconfigure $fi -translation binary
seek $fi 8 current
set data [read $fi 4]
binary scan $data i first_type
if { $first_type != 0x1000 } {
fail "Incorrect first type value in IPI stream."
} else {
pass "Correct first type value in IPI stream."
}
set data [read $fi 4]
binary scan $data i end_type
# end_type is one greater than the last type in the stream
if { $end_type != 0x100f } {
fail "Incorrect end type value in IPI stream."
} else {
pass "Correct end type value in IPI stream."
}
set data [read $fi 4]
binary scan $data i type_list_size
set data [read $fi 2]
binary scan $data s hash_stream_index
seek $fi 2 current
set data [read $fi 4]
binary scan $data i hash_size
if { $hash_size != 4 } {
fail "Incorrect hash size in IPI stream."
} else {
pass "Correct hash size in IPI stream."
}
set data [read $fi 4]
binary scan $data i num_buckets
if { $num_buckets != 0x3ffff } {
fail "Incorrect number of buckets in IPI stream."
} else {
pass "Correct number of buckets in IPI stream."
}
set data [read $fi 4]
binary scan $data i hash_list_offset
set data [read $fi 4]
binary scan $data i hash_list_size
set data [read $fi 4]
binary scan $data i skip_list_offset
set data [read $fi 4]
binary scan $data i skip_list_size
seek $fi 8 current
set type_list [read $fi $type_list_size]
close $fi
set fi [open tmpdir/pdb-types2-typelist w]
fconfigure $fi -translation binary
puts -nonewline $fi $type_list
close $fi
# check type list
set exp [file_contents "$srcdir/$subdir/pdb-types2-typelist.d"]
set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types2-typelist"]
if ![string match $exp $got] {
fail "Incorrect type list in IPI stream."
} else {
pass "Correct type list in IPI stream."
}
# extract hash list and skip list
set index_str [format "%04x" $hash_stream_index]
set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types2.pdb $index_str"]
if ![string match "" $exec_output] {
fail "Could not extract IPI hash stream."
} else {
pass "Extracted IPI hash stream."
}
set fi [open tmpdir/$index_str]
fconfigure $fi -translation binary
seek $fi $hash_list_offset
set hash_list [read $fi $hash_list_size]
seek $fi $skip_list_offset
set skip_list [read $fi $skip_list_size]
close $fi
# check hash list
set fi [open tmpdir/pdb-types2-hashlist w]
fconfigure $fi -translation binary
puts -nonewline $fi $hash_list
close $fi
set exp [file_contents "$srcdir/$subdir/pdb-types2-hashlist.d"]
set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types2-hashlist"]
if ![string match $exp $got] {
fail "Incorrect hash list in IPI stream."
} else {
pass "Correct hash list in IPI stream."
}
# check skip list
set fi [open tmpdir/pdb-types2-skiplist w]
fconfigure $fi -translation binary
puts -nonewline $fi $skip_list
close $fi
set exp [file_contents "$srcdir/$subdir/pdb-types2-skiplist.d"]
set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types2-skiplist"]
if ![string match $exp $got] {
fail "Incorrect skip list in IPI stream."
} else {
pass "Correct skip list in IPI stream."
}
}
test1
test2
test3
test4
test5
test6