diff options
-rw-r--r-- | ld/pdb.c | 171 | ||||
-rw-r--r-- | ld/pdb.h | 23 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types3-hashlist.d | 5 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types3-skiplist.d | 5 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types3-typelist.d | 7 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types3a.s | 57 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types3b.s | 68 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb.exp | 133 |
8 files changed, 459 insertions, 10 deletions
@@ -76,6 +76,7 @@ struct type_entry struct type_entry *next; uint32_t index; uint32_t cv_hash; + bool has_udt_src_line; uint8_t data[]; }; @@ -709,19 +710,19 @@ copy_filechksms (uint8_t *data, uint32_t size, char *string_table, return true; } -/* Add a string to the strings table, if it's not already there. */ -static void +/* Add a string to the strings table, if it's not already there. Returns its + offset within the string table. */ +static uint32_t add_string (char *str, size_t len, struct string_table *strings) { uint32_t hash = calc_hash (str, len); + struct string *s; void **slot; slot = htab_find_slot_with_hash (strings->hashmap, str, hash, INSERT); if (!*slot) { - struct string *s; - *slot = xmalloc (offsetof (struct string, s) + len); s = (struct string *) *slot; @@ -742,6 +743,12 @@ add_string (char *str, size_t len, struct string_table *strings) strings->strings_len += len + 1; } + else + { + s = (struct string *) *slot; + } + + return s->offset; } /* Return the hash of an entry in the string table. */ @@ -1119,13 +1126,149 @@ is_name_anonymous (char *name, size_t len) return false; } +/* Handle LF_UDT_SRC_LINE type entries, which are a special case. These + give the source file and line number for each user-defined type that is + declared. We parse these and emit instead an LF_UDT_MOD_SRC_LINE entry, + which also includes the module number. */ +static bool +handle_udt_src_line (uint8_t *data, uint16_t size, struct type_entry **map, + uint32_t type_num, uint32_t num_types, + struct types *ids, uint16_t mod_num, + struct string_table *strings) +{ + struct lf_udt_src_line *usl = (struct lf_udt_src_line *) data; + uint32_t orig_type, source_file_type; + void **slot; + hashval_t hash; + struct type_entry *e, *type_e, *str_e; + struct lf_udt_mod_src_line *umsl; + struct lf_string_id *str; + uint32_t source_file_offset; + + if (size < sizeof (struct lf_udt_src_line)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_UDT_SRC_LINE\n")); + return false; + } + + /* Check if LF_UDT_MOD_SRC_LINE already present for type, and return. */ + + orig_type = bfd_getl32 (&usl->type); + + if (orig_type < TPI_FIRST_INDEX || + orig_type >= TPI_FIRST_INDEX + num_types || + !map[orig_type - TPI_FIRST_INDEX]) + { + einfo (_("%P: warning: CodeView type record LF_UDT_SRC_LINE" + " referred to unknown type %v\n"), orig_type); + return false; + } + + type_e = map[orig_type - TPI_FIRST_INDEX]; + + /* Skip if type already declared in other module. */ + if (type_e->has_udt_src_line) + return true; + + if (!remap_type (&usl->type, map, type_num, num_types)) + return false; + + /* Extract string from source_file_type. */ + + source_file_type = bfd_getl32 (&usl->source_file_type); + + if (source_file_type < TPI_FIRST_INDEX || + source_file_type >= TPI_FIRST_INDEX + num_types || + !map[source_file_type - TPI_FIRST_INDEX]) + { + einfo (_("%P: warning: CodeView type record LF_UDT_SRC_LINE" + " referred to unknown string %v\n"), source_file_type); + return false; + } + + str_e = map[source_file_type - TPI_FIRST_INDEX]; + + if (bfd_getl16 (str_e->data + sizeof (uint16_t)) != LF_STRING_ID) + { + einfo (_("%P: warning: CodeView type record LF_UDT_SRC_LINE" + " pointed to unexpected record type\n")); + return false; + } + + str = (struct lf_string_id *) str_e->data; + + /* Add string to string table. */ + + source_file_offset = add_string (str->string, strlen (str->string), + strings); + + /* Add LF_UDT_MOD_SRC_LINE entry. */ + + size = sizeof (struct lf_udt_mod_src_line); + + e = xmalloc (offsetof (struct type_entry, data) + size); + + e->next = NULL; + e->index = ids->num_types; + e->has_udt_src_line = false; + + /* LF_UDT_MOD_SRC_LINE use calc_hash on the type number, rather than + the crc32 used for type hashes elsewhere. */ + e->cv_hash = calc_hash ((char *) &usl->type, sizeof (uint32_t)); + + type_e->has_udt_src_line = true; + + umsl = (struct lf_udt_mod_src_line *) e->data; + + bfd_putl16 (size - sizeof (uint16_t), &umsl->size); + bfd_putl16 (LF_UDT_MOD_SRC_LINE, &umsl->kind); + memcpy (&umsl->type, &usl->type, sizeof (uint32_t)); + bfd_putl32 (source_file_offset, &umsl->source_file_string); + memcpy (&umsl->line_no, &usl->line_no, sizeof (uint32_t)); + bfd_putl16 (mod_num + 1, &umsl->module_no); + + hash = iterative_hash (e->data, size, 0); + + slot = htab_find_slot_with_hash (ids->hashmap, data, hash, INSERT); + if (!slot) + { + free (e); + return false; + } + + if (*slot) + { + free (e); + einfo (_("%P: warning: duplicate CodeView type record " + "LF_UDT_MOD_SRC_LINE\n")); + return false; + } + + *slot = e; + + if (ids->last) + ids->last->next = e; + else + ids->first = e; + + ids->last = e; + + map[type_num] = e; + + ids->num_types++; + + return true; +} + /* Parse a type definition in the .debug$T section. We remap the numbers of any referenced types, and if the type is not a duplicate of one 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, - struct types *ids) + struct types *ids, uint16_t mod_num, + struct string_table *strings) { uint16_t size, type; void **slot; @@ -2076,6 +2219,10 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num, break; } + case LF_UDT_SRC_LINE: + return handle_udt_src_line (data, size, map, type_num, num_types, + ids, mod_num, strings); + default: einfo (_("%P: warning: unrecognized CodeView type %v\n"), type); return false; @@ -2105,6 +2252,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num, else e->cv_hash = crc32 (data, size); + e->has_udt_src_line = false; + memcpy (e->data, data, size); if (t->last) @@ -2130,7 +2279,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num, found to handle_type. */ static bool handle_debugt_section (asection *s, bfd *mod, struct types *types, - struct types *ids) + struct types *ids, uint16_t mod_num, + struct string_table *strings) { bfd_byte *data = NULL; size_t off; @@ -2187,7 +2337,8 @@ 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, ids)) + if (!handle_type (data + off, map, type_num, num_types, types, ids, + mod_num, strings)) { free (data); free (map); @@ -2213,7 +2364,7 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size, uint32_t *c13_info_size, struct mod_source_files *mod_source, bfd *abfd, struct types *types, - struct types *ids) + struct types *ids, uint16_t mod_num) { uint8_t int_buf[sizeof (uint32_t)]; uint8_t *c13_info = NULL; @@ -2237,7 +2388,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, ids)) + if (!handle_debugt_section (s, mod, types, ids, mod_num, strings)) { free (c13_info); free (mod_source->files); @@ -2371,7 +2522,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, ids)) + types, ids, mod_num)) { for (unsigned int i = 0; i < source->mod_count; i++) { @@ -59,6 +59,8 @@ #define LF_BUILDINFO 0x1603 #define LF_SUBSTR_LIST 0x1604 #define LF_STRING_ID 0x1605 +#define LF_UDT_SRC_LINE 0x1606 +#define LF_UDT_MOD_SRC_LINE 0x1607 #define LF_CHAR 0x8000 #define LF_SHORT 0x8001 @@ -517,6 +519,27 @@ struct lf_mfunc_id char name[]; } ATTRIBUTE_PACKED; +/* lfUdtSrcLine in cvinfo.h */ +struct lf_udt_src_line +{ + uint16_t size; + uint16_t kind; + uint32_t type; + uint32_t source_file_type; + uint32_t line_no; +} ATTRIBUTE_PACKED; + +/* lfUdtModSrcLine in cvinfo.h */ +struct lf_udt_mod_src_line +{ + uint16_t size; + uint16_t kind; + uint32_t type; + uint32_t source_file_string; + uint32_t line_no; + uint16_t module_no; +} ATTRIBUTE_PACKED; + extern bool create_pdb_file (bfd *, const char *, const unsigned char *); #endif diff --git a/ld/testsuite/ld-pe/pdb-types3-hashlist.d b/ld/testsuite/ld-pe/pdb-types3-hashlist.d new file mode 100644 index 0000000..4a3775b --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types3-hashlist.d @@ -0,0 +1,5 @@ + +*: file format binary + +Contents of section .data: + 0000 d4d90000 0c1c0000 *
\ No newline at end of file diff --git a/ld/testsuite/ld-pe/pdb-types3-skiplist.d b/ld/testsuite/ld-pe/pdb-types3-skiplist.d new file mode 100644 index 0000000..52c10fa --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types3-skiplist.d @@ -0,0 +1,5 @@ + +*: file format binary + +Contents of section .data: + 0000 00100000 00000000 *
\ No newline at end of file diff --git a/ld/testsuite/ld-pe/pdb-types3-typelist.d b/ld/testsuite/ld-pe/pdb-types3-typelist.d new file mode 100644 index 0000000..d6ffaad --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types3-typelist.d @@ -0,0 +1,7 @@ + +*: file format binary + +Contents of section .data: + 0000 0e000516 00000000 666f6f2e 6800f2f1 ........foo.h... + 0010 10000716 01100000 01000000 2a000000 ............*... + 0020 0100 ..
\ No newline at end of file diff --git a/ld/testsuite/ld-pe/pdb-types3a.s b/ld/testsuite/ld-pe/pdb-types3a.s new file mode 100644 index 0000000..def001e --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types3a.s @@ -0,0 +1,57 @@ +.equ CV_SIGNATURE_C13, 4 +.equ T_INT4, 0x0074 + +.equ LF_FIELDLIST, 0x1203 +.equ LF_STRUCTURE, 0x1505 +.equ LF_MEMBER, 0x150d +.equ LF_STRING_ID, 0x1605 +.equ LF_UDT_SRC_LINE, 0x1606 + +.section ".debug$T", "rn" + +.long CV_SIGNATURE_C13 + +# Type 1000, fieldlist for struct foo +.fieldlist1: +.short .struct1 - .fieldlist1 - 2 +.short LF_FIELDLIST +.short LF_MEMBER +.short 3 # public +.long T_INT4 +.short 0 # offset +.asciz "num" +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1001, struct foo +.struct1: +.short .string1 - .struct1 - 2 +.short LF_STRUCTURE +.short 1 # no. members +.short 0 # property +.long 0x1000 # field list +.long 0 # type derived from +.long 0 # type of vshape table +.short 4 # size +.asciz "foo" # name +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1002, string "foo" +.string1: +.short .udtsrcline1 - .string1 - 2 +.short LF_STRING_ID +.long 0 # sub-string +.asciz "foo.h" +.byte 0xf2 +.byte 0xf1 + +# Type 1003, UDT source line for type 1001 +.udtsrcline1: +.short .types_end - .udtsrcline1 - 2 +.short LF_UDT_SRC_LINE +.long 0x1001 +.long 0x1002 # source file string +.long 42 # line no. + +.types_end: diff --git a/ld/testsuite/ld-pe/pdb-types3b.s b/ld/testsuite/ld-pe/pdb-types3b.s new file mode 100644 index 0000000..a22234e --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types3b.s @@ -0,0 +1,68 @@ +.equ CV_SIGNATURE_C13, 4 + +.equ T_LONG, 0x0012 +.equ T_INT4, 0x0074 + +.equ LF_MODIFIER, 0x1001 +.equ LF_FIELDLIST, 0x1203 +.equ LF_STRUCTURE, 0x1505 +.equ LF_MEMBER, 0x150d +.equ LF_STRING_ID, 0x1605 +.equ LF_UDT_SRC_LINE, 0x1606 + +.section ".debug$T", "rn" + +.long CV_SIGNATURE_C13 + +# Type 1000, const long +.mod1: +.short .fieldlist1 - .mod1 - 2 +.short LF_MODIFIER +.long T_LONG +.short 1 # const +.short 0 # padding + +# Type 1001, fieldlist for struct foo +.fieldlist1: +.short .struct1 - .fieldlist1 - 2 +.short LF_FIELDLIST +.short LF_MEMBER +.short 3 # public +.long T_INT4 +.short 0 # offset +.asciz "num" +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1002, struct foo +.struct1: +.short .string1 - .struct1 - 2 +.short LF_STRUCTURE +.short 1 # no. members +.short 0 # property +.long 0x1001 # field list +.long 0 # type derived from +.long 0 # type of vshape table +.short 4 # size +.asciz "foo" # name +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1003, string "foo" +.string1: +.short .udtsrcline1 - .string1 - 2 +.short LF_STRING_ID +.long 0 # sub-string +.asciz "foo.h" +.byte 0xf2 +.byte 0xf1 + +# Type 1004, UDT source line for type 1002 +.udtsrcline1: +.short .types_end - .udtsrcline1 - 2 +.short LF_UDT_SRC_LINE +.long 0x1002 +.long 0x1003 # source file string +.long 42 # line no. + +.types_end: diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp index 9753216..1a54168 100644 --- a/ld/testsuite/ld-pe/pdb.exp +++ b/ld/testsuite/ld-pe/pdb.exp @@ -1319,9 +1319,142 @@ proc test6 { } { } } +proc test7 { } { + global as + global ar + global ld + global objdump + global srcdir + global subdir + + if ![ld_assemble $as $srcdir/$subdir/pdb-types3a.s tmpdir/pdb-types3a.o] { + unsupported "Build pdb-types3a.o" + return + } + + if ![ld_assemble $as $srcdir/$subdir/pdb-types3b.s tmpdir/pdb-types3b.o] { + unsupported "Build pdb-types3b.o" + return + } + + if ![ld_link $ld "tmpdir/pdb-types3.exe" "--pdb=tmpdir/pdb-types3.pdb tmpdir/pdb-types3a.o tmpdir/pdb-types3b.o"] { + unsupported "Create PE image with PDB file" + return + } + + set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types3.pdb 0004"] + + if ![string match "" $exec_output] { + fail "Could not extract IPI stream" + return + } else { + pass "Extracted IPI stream" + } + + set fi [open tmpdir/0004] + fconfigure $fi -translation binary + + seek $fi 16 current + + 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 10 current + + 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-types3-typelist w] + fconfigure $fi -translation binary + puts -nonewline $fi $type_list + close $fi + + # check type list + + set exp [file_contents "$srcdir/$subdir/pdb-types3-typelist.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-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-types3.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-types3-hashlist w] + fconfigure $fi -translation binary + puts -nonewline $fi $hash_list + close $fi + + set exp [file_contents "$srcdir/$subdir/pdb-types3-hashlist.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-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-types3-skiplist w] + fconfigure $fi -translation binary + puts -nonewline $fi $skip_list + close $fi + + set exp [file_contents "$srcdir/$subdir/pdb-types3-skiplist.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-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 +test7 |