diff options
author | Mark Harmstone <mark@harmstone.com> | 2022-12-09 01:52:35 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2022-12-23 20:05:48 +1030 |
commit | d5b4c0ddb9afaac5d5f741da4a863a4a71992969 (patch) | |
tree | ed1550d1d9251d8f71869eb4d18b94aecabf3e8e /ld | |
parent | 598c1ae610f7fd6352ed5445ab38f99a21062f21 (diff) | |
download | gdb-d5b4c0ddb9afaac5d5f741da4a863a4a71992969.zip gdb-d5b4c0ddb9afaac5d5f741da4a863a4a71992969.tar.gz gdb-d5b4c0ddb9afaac5d5f741da4a863a4a71992969.tar.bz2 |
ld: Write types into TPI stream of PDB
Diffstat (limited to 'ld')
-rw-r--r-- | ld/pdb.c | 1290 | ||||
-rw-r--r-- | ld/pdb.h | 264 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types1-hashlist.d | 13 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types1-skiplist.d | 5 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types1-typelist.d | 60 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types1a.s | 27 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb-types1b.s | 461 | ||||
-rw-r--r-- | ld/testsuite/ld-pe/pdb.exp | 172 |
8 files changed, 2270 insertions, 22 deletions
@@ -71,6 +71,69 @@ struct source_files_info struct mod_source_files *mods; }; +struct type_entry +{ + struct type_entry *next; + uint32_t index; + uint32_t cv_hash; + uint8_t data[]; +}; + +struct types +{ + htab_t hashmap; + uint32_t num_types; + struct type_entry *first; + struct type_entry *last; +}; + +static const uint32_t crc_table[] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + /* 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) @@ -365,38 +428,142 @@ end: return ret; } +/* Calculate the CRC32 used for type hashes. */ +static uint32_t +crc32 (const uint8_t *data, size_t len) +{ + uint32_t crc = 0; + + while (len > 0) + { + crc = (crc >> 8) ^ crc_table[(crc & 0xff) ^ *data]; + + data++; + len--; + } + + return crc; +} + /* Stream 2 is the type information (TPI) stream, and stream 4 is the ID information (IPI) stream. They differ only in which records go in which stream. */ static bool -create_type_stream (bfd *pdb) +populate_type_stream (bfd *pdb, bfd *stream, struct types *types) { - bfd *stream; struct pdb_tpi_stream_header h; + struct type_entry *e; + uint32_t len = 0, index_offset_len, off; + struct bfd *hash_stream = NULL; + uint16_t hash_stream_index; - stream = add_stream (pdb, NULL, NULL); - if (!stream) + static const uint32_t index_skip = 0x2000; + + e = types->first; + + index_offset_len = 0; + + while (e) + { + uint32_t old_len = len; + + len += sizeof (uint16_t) + bfd_getl16 (e->data); + + if (old_len == 0 || old_len / index_skip != len / index_skip) + index_offset_len += sizeof (uint32_t) * 2; + + e = e->next; + } + + /* Each type stream also has a stream which holds the hash value for each + type, along with a skip list to speed up searching. */ + + hash_stream = add_stream (pdb, "", &hash_stream_index); + + if (!hash_stream) return false; bfd_putl32 (TPI_STREAM_VERSION_80, &h.version); bfd_putl32 (sizeof (h), &h.header_size); bfd_putl32 (TPI_FIRST_INDEX, &h.type_index_begin); - bfd_putl32 (TPI_FIRST_INDEX, &h.type_index_end); - bfd_putl32 (0, &h.type_record_bytes); - bfd_putl16 (0xffff, &h.hash_stream_index); + bfd_putl32 (TPI_FIRST_INDEX + types->num_types, &h.type_index_end); + bfd_putl32 (len, &h.type_record_bytes); + bfd_putl16 (hash_stream_index, &h.hash_stream_index); bfd_putl16 (0xffff, &h.hash_aux_stream_index); - bfd_putl32 (4, &h.hash_key_size); - bfd_putl32 (0x3ffff, &h.num_hash_buckets); + bfd_putl32 (sizeof (uint32_t), &h.hash_key_size); + bfd_putl32 (NUM_TPI_HASH_BUCKETS, &h.num_hash_buckets); bfd_putl32 (0, &h.hash_value_buffer_offset); - bfd_putl32 (0, &h.hash_value_buffer_length); - bfd_putl32 (0, &h.index_offset_buffer_offset); - bfd_putl32 (0, &h.index_offset_buffer_length); - bfd_putl32 (0, &h.hash_adj_buffer_offset); + bfd_putl32 (types->num_types * sizeof (uint32_t), + &h.hash_value_buffer_length); + bfd_putl32 (types->num_types * sizeof (uint32_t), + &h.index_offset_buffer_offset); + bfd_putl32 (index_offset_len, &h.index_offset_buffer_length); + bfd_putl32 ((types->num_types * sizeof (uint32_t)) + index_offset_len, + &h.hash_adj_buffer_offset); bfd_putl32 (0, &h.hash_adj_buffer_length); if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h)) return false; + /* Write the type definitions into the main stream, and the hashes + into the hash stream. The hashes have already been calculated + in handle_type. */ + + e = types->first; + + while (e) + { + uint8_t buf[sizeof (uint32_t)]; + uint16_t size; + + size = bfd_getl16 (e->data); + + if (bfd_bwrite (e->data, size + sizeof (uint16_t), stream) + != size + sizeof (uint16_t)) + return false; + + bfd_putl32 (e->cv_hash % NUM_TPI_HASH_BUCKETS, buf); + + if (bfd_bwrite (buf, sizeof (uint32_t), hash_stream) + != sizeof (uint32_t)) + return false; + + e = e->next; + } + + /* Write the index offsets, i.e. the skip list, into the hash stream. We + copy MSVC here by writing a new entry for every 8192 bytes. */ + + e = types->first; + off = 0; + + while (e) + { + uint32_t old_off = off; + uint16_t size = bfd_getl16 (e->data); + + off += size + sizeof (uint16_t); + + if (old_off == 0 || old_off / index_skip != len / index_skip) + { + uint8_t buf[sizeof (uint32_t)]; + + bfd_putl32 (TPI_FIRST_INDEX + e->index, buf); + + if (bfd_bwrite (buf, sizeof (uint32_t), hash_stream) + != sizeof (uint32_t)) + return false; + + bfd_putl32 (old_off, buf); + + if (bfd_bwrite (buf, sizeof (uint32_t), hash_stream) + != sizeof (uint32_t)) + return false; + } + + e = e->next; + } + return true; } @@ -620,6 +787,32 @@ parse_string_table (bfd_byte *data, size_t size, } } +/* Return the size of an extended value parameter, as used in + LF_ENUMERATE etc. */ +static unsigned int +extended_value_len (uint16_t type) +{ + switch (type) + { + case LF_CHAR: + return 1; + + case LF_SHORT: + case LF_USHORT: + return 2; + + case LF_LONG: + case LF_ULONG: + return 4; + + case LF_QUADWORD: + case LF_UQUADWORD: + return 8; + } + + return 0; +} + /* Parse the .debug$S section within an object file. */ static bool handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, @@ -866,6 +1059,984 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, return true; } +/* Remap the type number stored in data from the per-module numbering to + that of the deduplicated output list. */ +static bool +remap_type (void *data, struct type_entry **map, + uint32_t type_num, uint32_t num_types) +{ + uint32_t type = bfd_getl32 (data); + + /* Ignore builtin types (those with IDs below 0x1000). */ + if (type < TPI_FIRST_INDEX) + return true; + + if (type >= TPI_FIRST_INDEX + type_num) + { + einfo (_("%P: CodeView type %v references other type %v not yet " + "declared\n"), TPI_FIRST_INDEX + type_num, type); + return false; + } + + if (type >= TPI_FIRST_INDEX + num_types) + { + einfo (_("%P: CodeView type %v references out of range type %v\n"), + TPI_FIRST_INDEX + type_num, type); + return false; + } + + type = TPI_FIRST_INDEX + map[type - TPI_FIRST_INDEX]->index; + bfd_putl32 (type, data); + + return true; +} + +/* Determines whether the name of a struct, class, or union counts as + "anonymous". Non-anonymous types have a hash based on just the name, + rather than the whole structure. */ +static bool +is_name_anonymous (char *name, size_t len) +{ + static const char tag1[] = "<unnamed-tag>"; + static const char tag2[] = "__unnamed"; + static const char tag3[] = "::<unnamed-tag>"; + static const char tag4[] = "::__unnamed"; + + if (len == sizeof (tag1) - 1 && !memcmp (name, tag1, sizeof (tag1) - 1)) + return true; + + if (len == sizeof (tag2) - 1 && !memcmp (name, tag2, sizeof (tag2) - 1)) + return true; + + if (len >= sizeof (tag3) - 1 + && !memcmp (name + len - sizeof (tag3) + 1, tag3, sizeof (tag3) - 1)) + return true; + + if (len >= sizeof (tag4) - 1 + && !memcmp (name + len - sizeof (tag4) + 1, tag4, sizeof (tag4) - 1)) + return true; + + return false; +} + +/* 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) +{ + uint16_t size, type; + void **slot; + hashval_t hash; + bool other_hash = false; + uint32_t cv_hash; + + size = bfd_getl16 (data) + sizeof (uint16_t); + type = bfd_getl16 (data + sizeof (uint16_t)); + + switch (type) + { + case LF_MODIFIER: + { + struct lf_modifier *mod = (struct lf_modifier *) data; + + if (size < offsetof (struct lf_modifier, modifier)) + { + einfo (_("%P: warning: truncated CodeView type record " + "LF_MODIFIER\n")); + return false; + } + + if (!remap_type (&mod->base_type, map, type_num, num_types)) + return false; + + break; + } + + case LF_POINTER: + { + struct lf_pointer *ptr = (struct lf_pointer *) data; + + if (size < offsetof (struct lf_pointer, attributes)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_POINTER\n")); + return false; + } + + if (!remap_type (&ptr->base_type, map, type_num, num_types)) + return false; + + break; + } + + case LF_PROCEDURE: + { + struct lf_procedure *proc = (struct lf_procedure *) data; + + if (size < sizeof (struct lf_procedure)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_PROCEDURE\n")); + return false; + } + + if (!remap_type (&proc->return_type, map, type_num, num_types)) + return false; + + if (!remap_type (&proc->arglist, map, type_num, num_types)) + return false; + + break; + } + + case LF_MFUNCTION: + { + struct lf_mfunction *func = (struct lf_mfunction *) data; + + if (size < sizeof (struct lf_procedure)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_MFUNCTION\n")); + return false; + } + + if (!remap_type (&func->return_type, map, type_num, num_types)) + return false; + + if (!remap_type (&func->containing_class_type, map, type_num, + num_types)) + return false; + + if (!remap_type (&func->this_type, map, type_num, num_types)) + return false; + + if (!remap_type (&func->arglist, map, type_num, num_types)) + return false; + + break; + } + + case LF_ARGLIST: + { + uint32_t num_entries; + struct lf_arglist *al = (struct lf_arglist *) data; + + if (size < offsetof (struct lf_arglist, args)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_ARGLIST\n")); + return false; + } + + num_entries = bfd_getl32 (&al->num_entries); + + if (size < offsetof (struct lf_arglist, args) + + (num_entries * sizeof (uint32_t))) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_ARGLIST\n")); + return false; + } + + for (uint32_t i = 0; i < num_entries; i++) + { + if (!remap_type (&al->args[i], map, type_num, num_types)) + return false; + } + + break; + } + + case LF_FIELDLIST: + { + uint16_t left = size - sizeof (uint16_t) - sizeof (uint16_t); + uint8_t *ptr = data + sizeof (uint16_t) + sizeof (uint16_t); + + while (left > 0) + { + uint16_t subtype; + + if (left < sizeof (uint16_t)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_FIELDLIST\n")); + return false; + } + + subtype = bfd_getl16 (ptr); + + switch (subtype) + { + case LF_MEMBER: + { + struct lf_member *mem = (struct lf_member *) ptr; + size_t name_len, subtype_len; + + if (left < offsetof (struct lf_member, name)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_MEMBER\n")); + return false; + } + + if (!remap_type (&mem->type, map, type_num, num_types)) + return false; + + name_len = + strnlen (mem->name, + left - offsetof (struct lf_member, name)); + + if (name_len == left - offsetof (struct lf_member, name)) + { + einfo (_("%P: warning: name for LF_MEMBER has no" + " terminating zero\n")); + return false; + } + + name_len++; + + subtype_len = offsetof (struct lf_member, name) + name_len; + + if (subtype_len % 4 != 0) + subtype_len += 4 - (subtype_len % 4); + + if (left < subtype_len) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_FIELDLIST\n")); + return false; + } + + ptr += subtype_len; + left -= subtype_len; + + break; + } + + case LF_ENUMERATE: + { + struct lf_enumerate *en = (struct lf_enumerate *) ptr; + size_t name_len, subtype_len; + uint16_t val; + + if (left < offsetof (struct lf_enumerate, name)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_ENUMERATE\n")); + return false; + } + + subtype_len = offsetof (struct lf_enumerate, name); + + val = bfd_getl16 (&en->value); + + /* If val >= 0x8000, the actual value immediately follows. */ + if (val >= 0x8000) + { + unsigned int param_len = extended_value_len (val); + + if (param_len == 0) + { + einfo (_("%P: warning: unhandled type %v within" + " LF_ENUMERATE\n"), val); + return false; + } + + if (left < subtype_len + param_len) + { + einfo (_("%P: warning: truncated CodeView type" + " record LF_ENUMERATE\n")); + return false; + } + + subtype_len += param_len; + } + + name_len = strnlen ((char *) ptr + subtype_len, + left - subtype_len); + + if (name_len == left - offsetof (struct lf_enumerate, name)) + { + einfo (_("%P: warning: name for LF_ENUMERATE has no" + " terminating zero\n")); + return false; + } + + name_len++; + + subtype_len += name_len; + + if (subtype_len % 4 != 0) + subtype_len += 4 - (subtype_len % 4); + + if (left < subtype_len) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_ENUMERATE\n")); + return false; + } + + ptr += subtype_len; + left -= subtype_len; + + break; + } + + case LF_INDEX: + { + struct lf_index *ind = (struct lf_index *) ptr; + + if (left < sizeof (struct lf_index)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_INDEX\n")); + return false; + } + + if (!remap_type (&ind->index, map, type_num, num_types)) + return false; + + ptr += sizeof (struct lf_index); + left -= sizeof (struct lf_index); + + break; + } + + case LF_ONEMETHOD: + { + struct lf_onemethod *meth = (struct lf_onemethod *) ptr; + size_t name_len, subtype_len; + + if (left < offsetof (struct lf_onemethod, name)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_ONEMETHOD\n")); + return false; + } + + if (!remap_type (&meth->method_type, map, type_num, + num_types)) + return false; + + name_len = + strnlen (meth->name, + left - offsetof (struct lf_onemethod, name)); + + if (name_len == left - offsetof (struct lf_onemethod, name)) + { + einfo (_("%P: warning: name for LF_ONEMETHOD has no" + " terminating zero\n")); + return false; + } + + name_len++; + + subtype_len = offsetof (struct lf_onemethod, name) + + name_len; + + if (subtype_len % 4 != 0) + subtype_len += 4 - (subtype_len % 4); + + if (left < subtype_len) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_FIELDLIST\n")); + return false; + } + + ptr += subtype_len; + left -= subtype_len; + + break; + } + + case LF_METHOD: + { + struct lf_method *meth = (struct lf_method *) ptr; + size_t name_len, subtype_len; + + if (left < offsetof (struct lf_method, name)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_METHOD\n")); + return false; + } + + if (!remap_type (&meth->method_list, map, type_num, + num_types)) + return false; + + name_len = + strnlen (meth->name, + left - offsetof (struct lf_method, name)); + + if (name_len == left - offsetof (struct lf_method, name)) + { + einfo (_("%P: warning: name for LF_METHOD has no" + " terminating zero\n")); + return false; + } + + name_len++; + + subtype_len = offsetof (struct lf_method, name) + name_len; + + if (subtype_len % 4 != 0) + subtype_len += 4 - (subtype_len % 4); + + if (left < subtype_len) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_FIELDLIST\n")); + return false; + } + + ptr += subtype_len; + left -= subtype_len; + + break; + } + + case LF_BCLASS: + { + struct lf_bclass *bc = (struct lf_bclass *) ptr; + + if (left < sizeof (struct lf_bclass)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_BCLASS\n")); + return false; + } + + if (!remap_type (&bc->base_class_type, map, type_num, + num_types)) + return false; + + ptr += sizeof (struct lf_bclass); + left -= sizeof (struct lf_bclass); + + break; + } + + case LF_VFUNCTAB: + { + struct lf_vfunctab *vft = (struct lf_vfunctab *) ptr; + + if (left < sizeof (struct lf_vfunctab)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_VFUNCTAB\n")); + return false; + } + + if (!remap_type (&vft->type, map, type_num, num_types)) + return false; + + ptr += sizeof (struct lf_vfunctab); + left -= sizeof (struct lf_vfunctab); + + break; + } + + case LF_VBCLASS: + case LF_IVBCLASS: + { + struct lf_vbclass *vbc = (struct lf_vbclass *) ptr; + + if (left < sizeof (struct lf_vbclass)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_VBCLASS/LF_IVBCLASS\n")); + return false; + } + + if (!remap_type (&vbc->base_class_type, map, type_num, + num_types)) + return false; + + if (!remap_type (&vbc->virtual_base_pointer_type, map, + type_num, num_types)) + return false; + + ptr += sizeof (struct lf_vbclass); + left -= sizeof (struct lf_vbclass); + + break; + } + + case LF_STMEMBER: + { + struct lf_static_member *st = + (struct lf_static_member *) ptr; + size_t name_len, subtype_len; + + if (left < offsetof (struct lf_static_member, name)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_STMEMBER\n")); + return false; + } + + if (!remap_type (&st->type, map, type_num, num_types)) + return false; + + name_len = + strnlen (st->name, + left - offsetof (struct lf_static_member, name)); + + if (name_len == left + - offsetof (struct lf_static_member, name)) + { + einfo (_("%P: warning: name for LF_STMEMBER has no" + " terminating zero\n")); + return false; + } + + name_len++; + + subtype_len = offsetof (struct lf_static_member, name) + + name_len; + + if (subtype_len % 4 != 0) + subtype_len += 4 - (subtype_len % 4); + + if (left < subtype_len) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_FIELDLIST\n")); + return false; + } + + ptr += subtype_len; + left -= subtype_len; + + break; + } + + case LF_NESTTYPE: + { + struct lf_nest_type *nest = (struct lf_nest_type *) ptr; + size_t name_len, subtype_len; + + if (left < offsetof (struct lf_nest_type, name)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_NESTTYPE\n")); + return false; + } + + if (!remap_type (&nest->type, map, type_num, num_types)) + return false; + + name_len = + strnlen (nest->name, + left - offsetof (struct lf_nest_type, name)); + + if (name_len == left - offsetof (struct lf_nest_type, name)) + { + einfo (_("%P: warning: name for LF_NESTTYPE has no" + " terminating zero\n")); + return false; + } + + name_len++; + + subtype_len = offsetof (struct lf_nest_type, name) + + name_len; + + if (subtype_len % 4 != 0) + subtype_len += 4 - (subtype_len % 4); + + if (left < subtype_len) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_FIELDLIST\n")); + return false; + } + + ptr += subtype_len; + left -= subtype_len; + + break; + } + + default: + einfo (_("%P: warning: unrecognized CodeView subtype %v\n"), + subtype); + return false; + } + } + + break; + } + + case LF_BITFIELD: + { + struct lf_bitfield *bf = (struct lf_bitfield *) data; + + if (size < offsetof (struct lf_bitfield, length)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_BITFIELD\n")); + return false; + } + + if (!remap_type (&bf->base_type, map, type_num, num_types)) + return false; + + break; + } + + case LF_METHODLIST: + { + struct lf_methodlist *ml = (struct lf_methodlist *) data; + unsigned int num_entries; + + if (size < offsetof (struct lf_methodlist, entries)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_METHODLIST\n")); + return false; + } + + if ((size - offsetof (struct lf_methodlist, entries)) + % sizeof (struct lf_methodlist_entry)) + { + einfo (_("%P: warning: malformed CodeView type record" + " LF_METHODLIST\n")); + return false; + } + + num_entries = (size - offsetof (struct lf_methodlist, entries)) + / sizeof (struct lf_methodlist_entry); + + for (unsigned int i = 0; i < num_entries; i++) + { + if (!remap_type (&ml->entries[i].method_type, map, + type_num, num_types)) + return false; + } + + break; + } + + case LF_ARRAY: + { + struct lf_array *arr = (struct lf_array *) data; + + if (size < offsetof (struct lf_array, length_in_bytes)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_ARRAY\n")); + return false; + } + + if (!remap_type (&arr->element_type, map, type_num, num_types)) + return false; + + if (!remap_type (&arr->index_type, map, type_num, num_types)) + return false; + + break; + } + + case LF_CLASS: + case LF_STRUCTURE: + { + struct lf_class *cl = (struct lf_class *) data; + uint16_t prop; + size_t name_len; + + if (size < offsetof (struct lf_class, name)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_CLASS/LF_STRUCTURE\n")); + return false; + } + + if (!remap_type (&cl->field_list, map, type_num, num_types)) + return false; + + if (!remap_type (&cl->derived_from, map, type_num, num_types)) + return false; + + if (!remap_type (&cl->vshape, map, type_num, num_types)) + return false; + + name_len = strnlen (cl->name, size - offsetof (struct lf_class, name)); + + if (name_len == size - offsetof (struct lf_class, name)) + { + einfo (_("%P: warning: name for LF_CLASS/LF_STRUCTURE has no" + " terminating zero\n")); + return false; + } + + prop = bfd_getl16 (&cl->properties); + + if (prop & CV_PROP_HAS_UNIQUE_NAME) + { + /* Structure has another name following first one. */ + + size_t len = offsetof (struct lf_class, name) + name_len + 1; + size_t unique_name_len; + + unique_name_len = strnlen (cl->name + name_len + 1, size - len); + + if (unique_name_len == size - len) + { + einfo (_("%P: warning: unique name for LF_CLASS/LF_STRUCTURE" + " has no terminating zero\n")); + return false; + } + } + + if (!(prop & (CV_PROP_FORWARD_REF | CV_PROP_SCOPED)) + && !is_name_anonymous (cl->name, name_len)) + { + other_hash = true; + cv_hash = crc32 ((uint8_t *) cl->name, name_len); + } + + break; + } + + case LF_UNION: + { + struct lf_union *un = (struct lf_union *) data; + uint16_t prop; + size_t name_len; + + if (size < offsetof (struct lf_union, name)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_UNION\n")); + return false; + } + + if (!remap_type (&un->field_list, map, type_num, num_types)) + return false; + + name_len = strnlen (un->name, size - offsetof (struct lf_union, name)); + + if (name_len == size - offsetof (struct lf_union, name)) + { + einfo (_("%P: warning: name for LF_UNION has no" + " terminating zero\n")); + return false; + } + + prop = bfd_getl16 (&un->properties); + + if (prop & CV_PROP_HAS_UNIQUE_NAME) + { + /* Structure has another name following first one. */ + + size_t len = offsetof (struct lf_union, name) + name_len + 1; + size_t unique_name_len; + + unique_name_len = strnlen (un->name + name_len + 1, size - len); + + if (unique_name_len == size - len) + { + einfo (_("%P: warning: unique name for LF_UNION has" + " no terminating zero\n")); + return false; + } + } + + if (!(prop & (CV_PROP_FORWARD_REF | CV_PROP_SCOPED)) + && !is_name_anonymous (un->name, name_len)) + { + other_hash = true; + cv_hash = crc32 ((uint8_t *) un->name, name_len); + } + + break; + } + + case LF_ENUM: + { + struct lf_enum *en = (struct lf_enum *) data; + uint16_t prop; + size_t name_len; + + if (size < offsetof (struct lf_enum, name)) + { + einfo (_("%P: warning: truncated CodeView type record" + " LF_ENUM\n")); + return false; + } + + if (!remap_type (&en->underlying_type, map, type_num, num_types)) + return false; + + if (!remap_type (&en->field_list, map, type_num, num_types)) + return false; + + name_len = strnlen (en->name, size - offsetof (struct lf_enum, name)); + + if (name_len == size - offsetof (struct lf_enum, name)) + { + einfo (_("%P: warning: name for LF_ENUM has no" + " terminating zero\n")); + return false; + } + + prop = bfd_getl16 (&en->properties); + + if (prop & CV_PROP_HAS_UNIQUE_NAME) + { + /* Structure has another name following first one. */ + + size_t len = offsetof (struct lf_enum, name) + name_len + 1; + size_t unique_name_len; + + unique_name_len = strnlen (en->name + name_len + 1, size - len); + + if (unique_name_len == size - len) + { + einfo (_("%P: warning: unique name for LF_ENUM has" + " no terminating zero\n")); + return false; + } + } + + break; + } + + case LF_VTSHAPE: + /* Does not reference any types, nothing to be done. */ + break; + + default: + einfo (_("%P: warning: unrecognized CodeView type %v\n"), type); + return false; + } + + hash = iterative_hash (data, size, 0); + + slot = htab_find_slot_with_hash (types->hashmap, data, hash, INSERT); + if (!slot) + return false; + + if (!*slot) /* new entry */ + { + struct type_entry *e; + + *slot = xmalloc (offsetof (struct type_entry, data) + size); + + e = (struct type_entry *) *slot; + + e->next = NULL; + e->index = types->num_types; + + if (other_hash) + e->cv_hash = cv_hash; + else + e->cv_hash = crc32 (data, size); + + memcpy (e->data, data, size); + + if (types->last) + types->last->next = e; + else + types->first = e; + + types->last = e; + + map[type_num] = e; + + types->num_types++; + } + else /* duplicate */ + { + map[type_num] = (struct type_entry *) *slot; + } + + return true; +} + +/* 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) +{ + bfd_byte *data = NULL; + size_t off; + unsigned int num_types = 0; + struct type_entry **map; + uint32_t type_num; + + if (!bfd_get_full_section_contents (mod, s, &data)) + return false; + + if (!data) + return false; + + if (bfd_getl32 (data) != CV_SIGNATURE_C13) + { + free (data); + return true; + } + + off = sizeof (uint32_t); + + while (off + sizeof (uint16_t) <= s->size) + { + uint16_t size; + + size = bfd_getl16 (data + off); + off += sizeof (uint16_t); + + if (size + off > s->size || size <= sizeof (uint16_t)) + { + free (data); + bfd_set_error (bfd_error_bad_value); + return false; + } + + num_types++; + off += size; + } + + if (num_types == 0) + { + free (data); + return true; + } + + map = xcalloc (num_types, sizeof (struct type_entry *)); + + off = sizeof (uint32_t); + type_num = 0; + + while (off + sizeof (uint16_t) <= s->size) + { + uint16_t size; + + size = bfd_getl16 (data + off); + + if (!handle_type (data + off, map, type_num, num_types, types)) + { + free (data); + free (map); + bfd_set_error (bfd_error_bad_value); + return false; + } + + off += sizeof (uint16_t) + size; + type_num++; + } + + free (data); + free (map); + + return true; +} + /* Populate the module stream, which consists of the transformed .debug$S data for each object file. */ static bool @@ -873,7 +2044,7 @@ 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) + bfd *abfd, struct types *types) { uint8_t int_buf[sizeof (uint32_t)]; uint8_t *c13_info = NULL; @@ -895,6 +2066,15 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size, return false; } } + else if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t)) + { + if (!handle_debugt_section (s, mod, types)) + { + free (c13_info); + free (mod_source->files); + return false; + } + } } /* Write the signature. */ @@ -932,7 +2112,8 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size, 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 source_files_info *source, + struct types *types) { uint8_t *ptr; unsigned int mod_num; @@ -1020,7 +2201,8 @@ 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)) + &source->mods[mod_num], abfd, + types)) { for (unsigned int i = 0; i < source->mod_count; i++) { @@ -1342,7 +2524,8 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb, uint16_t section_header_stream_num, uint16_t sym_rec_stream_num, uint16_t publics_stream_num, - struct string_table *strings) + struct string_table *strings, + struct types *types) { struct pdb_dbi_stream_header h; struct optional_dbg_header opt; @@ -1354,7 +2537,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)) + strings, &source, types)) return false; if (!create_section_contrib_substream (abfd, &sc, &sc_size)) @@ -1884,6 +3067,31 @@ populate_names_stream (bfd *stream, struct string_table *strings) return true; } +/* Calculate the hash of a type_entry. */ +static hashval_t +hash_type_entry (const void *p) +{ + const struct type_entry *e = (const struct type_entry *) p; + uint16_t size = bfd_getl16 (e->data) + sizeof (uint16_t); + + return iterative_hash (e->data, size, 0); +} + +/* Compare a type_entry with a type. */ +static int +eq_type_entry (const void *a, const void *b) +{ + const struct type_entry *e = (const struct type_entry *) a; + uint16_t size_a = bfd_getl16 (e->data); + uint16_t size_b = bfd_getl16 (b); + + if (size_a != size_b) + return 0; + + return memcmp (e->data + sizeof (uint16_t), + (const uint8_t *) b + sizeof (uint16_t), size_a) == 0; +} + /* Create a PDB debugging file for the PE image file abfd with the build ID guid, stored at pdb_name. */ bool @@ -1892,9 +3100,10 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid) bfd *pdb; bool ret = false; bfd *info_stream, *dbi_stream, *names_stream, *sym_rec_stream, - *publics_stream; + *publics_stream, *tpi_stream, *ipi_stream; uint16_t section_header_stream_num, sym_rec_stream_num, publics_stream_num; struct string_table strings; + struct types types, ids; pdb = bfd_openw (pdb_name, "pdb"); if (!pdb) @@ -1928,7 +3137,9 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid) goto end; } - if (!create_type_stream (pdb)) + tpi_stream = add_stream (pdb, NULL, NULL); + + if (!tpi_stream) { einfo (_("%P: warning: cannot create TPI stream " "in PDB file: %E\n")); @@ -1944,7 +3155,9 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid) goto end; } - if (!create_type_stream (pdb)) + ipi_stream = add_stream (pdb, NULL, NULL); + + if (!ipi_stream) { einfo (_("%P: warning: cannot create IPI stream " "in PDB file: %E\n")); @@ -1985,15 +3198,48 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid) goto end; } + types.num_types = 0; + types.hashmap = htab_create_alloc (0, hash_type_entry, eq_type_entry, + free, xcalloc, free); + types.first = types.last = NULL; + + ids.num_types = 0; + ids.hashmap = htab_create_alloc (0, hash_type_entry, eq_type_entry, + free, xcalloc, free); + ids.first = ids.last = NULL; + if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num, sym_rec_stream_num, publics_stream_num, - &strings)) + &strings, &types)) { einfo (_("%P: warning: cannot populate DBI stream " "in PDB file: %E\n")); + htab_delete (types.hashmap); + htab_delete (ids.hashmap); + goto end; + } + + if (!populate_type_stream (pdb, tpi_stream, &types)) + { + einfo (_("%P: warning: cannot populate TPI stream " + "in PDB file: %E\n")); + htab_delete (types.hashmap); + htab_delete (ids.hashmap); goto end; } + htab_delete (types.hashmap); + + if (!populate_type_stream (pdb, ipi_stream, &ids)) + { + einfo (_("%P: warning: cannot populate IPI stream " + "in PDB file: %E\n")); + htab_delete (ids.hashmap); + goto end; + } + + htab_delete (ids.hashmap); + add_string ("", 0, &strings); if (!populate_names_stream (names_stream, &strings)) @@ -27,6 +27,41 @@ #include "sysdep.h" #include "bfd.h" #include <stdbool.h> +#include <stddef.h> + +#define LF_VTSHAPE 0x000a +#define LF_MODIFIER 0x1001 +#define LF_POINTER 0x1002 +#define LF_PROCEDURE 0x1008 +#define LF_MFUNCTION 0x1009 +#define LF_ARGLIST 0x1201 +#define LF_FIELDLIST 0x1203 +#define LF_BITFIELD 0x1205 +#define LF_METHODLIST 0x1206 +#define LF_BCLASS 0x1400 +#define LF_VBCLASS 0x1401 +#define LF_IVBCLASS 0x1402 +#define LF_INDEX 0x1404 +#define LF_VFUNCTAB 0x1409 +#define LF_ENUMERATE 0x1502 +#define LF_ARRAY 0x1503 +#define LF_CLASS 0x1504 +#define LF_STRUCTURE 0x1505 +#define LF_UNION 0x1506 +#define LF_ENUM 0x1507 +#define LF_MEMBER 0x150d +#define LF_STMEMBER 0x150e +#define LF_METHOD 0x150f +#define LF_NESTTYPE 0x1510 +#define LF_ONEMETHOD 0x1511 + +#define LF_CHAR 0x8000 +#define LF_SHORT 0x8001 +#define LF_USHORT 0x8002 +#define LF_LONG 0x8003 +#define LF_ULONG 0x8004 +#define LF_QUADWORD 0x8009 +#define LF_UQUADWORD 0x800a #define S_PUB32 0x110e @@ -65,6 +100,7 @@ struct pdb_tpi_stream_header #define TPI_STREAM_VERSION_80 20040203 #define TPI_FIRST_INDEX 0x1000 +#define NUM_TPI_HASH_BUCKETS 0x3ffff /* NewDBIHdr in dbi.h */ struct pdb_dbi_stream_header @@ -210,6 +246,234 @@ struct file_checksum uint8_t checksum_type; } ATTRIBUTE_PACKED; +/* lfModifier in cvinfo.h */ +struct lf_modifier +{ + uint16_t size; + uint16_t kind; + uint32_t base_type; + uint16_t modifier; + uint16_t padding; +} ATTRIBUTE_PACKED; + +/* lfPointer in cvinfo.h */ +struct lf_pointer +{ + uint16_t size; + uint16_t kind; + uint32_t base_type; + uint32_t attributes; +} ATTRIBUTE_PACKED; + +/* lfArgList in cvinfo.h */ +struct lf_arglist +{ + uint16_t size; + uint16_t kind; + uint32_t num_entries; + uint32_t args[]; +} ATTRIBUTE_PACKED; + +/* lfProc in cvinfo.h */ +struct lf_procedure +{ + uint16_t size; + uint16_t kind; + uint32_t return_type; + uint8_t calling_convention; + uint8_t attributes; + uint16_t num_parameters; + uint32_t arglist; +} ATTRIBUTE_PACKED; + +/* lfMFunc in cvinfo.h */ +struct lf_mfunction +{ + uint16_t size; + uint16_t kind; + uint32_t return_type; + uint32_t containing_class_type; + uint32_t this_type; + uint8_t calling_convention; + uint8_t attributes; + uint16_t num_parameters; + uint32_t arglist; + int32_t this_adjustment; +} ATTRIBUTE_PACKED; + +/* lfArray in cvinfo.h */ +struct lf_array +{ + uint16_t size; + uint16_t kind; + uint32_t element_type; + uint32_t index_type; + uint16_t length_in_bytes; + char name[]; +} ATTRIBUTE_PACKED; + +/* lfBitfield in cvinfo.h */ +struct lf_bitfield +{ + uint16_t size; + uint16_t kind; + uint32_t base_type; + uint8_t length; + uint8_t position; +} ATTRIBUTE_PACKED; + +/* lfMember in cvinfo.h */ +struct lf_member +{ + uint16_t kind; + uint16_t attributes; + uint32_t type; + uint16_t offset; + char name[]; +} ATTRIBUTE_PACKED; + +/* from bitfield structure CV_prop_t in cvinfo.h */ +#define CV_PROP_FORWARD_REF 0x80 +#define CV_PROP_SCOPED 0x100 +#define CV_PROP_HAS_UNIQUE_NAME 0x200 + +/* lfClass in cvinfo.h */ +struct lf_class +{ + uint16_t size; + uint16_t kind; + uint16_t num_members; + uint16_t properties; + uint32_t field_list; + uint32_t derived_from; + uint32_t vshape; + uint16_t length; + char name[]; +} ATTRIBUTE_PACKED; + +/* lfUnion in cvinfo.h */ +struct lf_union +{ + uint16_t size; + uint16_t kind; + uint16_t num_members; + uint16_t properties; + uint32_t field_list; + uint16_t length; + char name[]; +} ATTRIBUTE_PACKED; + +/* lfEnumerate in cvinfo.h */ +struct lf_enumerate +{ + uint16_t kind; + uint16_t attributes; + uint16_t value; + /* then actual value if value >= 0x8000 */ + char name[]; +} ATTRIBUTE_PACKED; + +/* lfEnum in cvinfo.h */ +struct lf_enum +{ + uint16_t size; + uint16_t kind; + uint16_t num_elements; + uint16_t properties; + uint32_t underlying_type; + uint32_t field_list; + char name[]; +} ATTRIBUTE_PACKED; + +/* lfIndex in cvinfo.h */ +struct lf_index +{ + uint16_t kind; + uint16_t padding; + uint32_t index; +} ATTRIBUTE_PACKED; + +/* lfOneMethod in cvinfo.h */ +struct lf_onemethod +{ + uint16_t kind; + uint16_t method_attribute; + uint32_t method_type; + char name[]; +} ATTRIBUTE_PACKED; + +/* mlMethod in cvinfo.h */ +struct lf_methodlist_entry +{ + uint16_t method_attribute; + uint16_t padding; + uint32_t method_type; +} ATTRIBUTE_PACKED; + +/* lfMethodList in cvinfo.h */ +struct lf_methodlist +{ + uint16_t size; + uint16_t kind; + struct lf_methodlist_entry entries[]; +} ATTRIBUTE_PACKED; + +/* lfMethod in cvinfo.h */ +struct lf_method +{ + uint16_t kind; + uint16_t count; + uint32_t method_list; + char name[]; +} ATTRIBUTE_PACKED; + +/* lfBClass in cvinfo.h */ +struct lf_bclass +{ + uint16_t kind; + uint16_t attributes; + uint32_t base_class_type; + uint16_t offset; + uint16_t padding; +} ATTRIBUTE_PACKED; + +/* lfVFuncTab in cvinfo.h */ +struct lf_vfunctab +{ + uint16_t kind; + uint16_t padding; + uint32_t type; +} ATTRIBUTE_PACKED; + +/* lfVBClass in cvinfo.h */ +struct lf_vbclass +{ + uint16_t kind; + uint16_t attributes; + uint32_t base_class_type; + uint32_t virtual_base_pointer_type; + uint16_t virtual_base_pointer_offset; + uint16_t virtual_base_vbtable_offset; +} ATTRIBUTE_PACKED; + +/* lfSTMember in cvinfo.h */ +struct lf_static_member +{ + uint16_t kind; + uint16_t attributes; + uint32_t type; + char name[]; +} ATTRIBUTE_PACKED; + +/* lfNestType in cvinfo.h */ +struct lf_nest_type +{ + uint16_t kind; + uint16_t padding; + uint32_t type; + char name[]; +} ATTRIBUTE_PACKED; + extern bool create_pdb_file (bfd *, const char *, const unsigned char *); #endif diff --git a/ld/testsuite/ld-pe/pdb-types1-hashlist.d b/ld/testsuite/ld-pe/pdb-types1-hashlist.d new file mode 100644 index 0000000..b75f08c --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types1-hashlist.d @@ -0,0 +1,13 @@ + +*: file format binary + +Contents of section .data: + 0000 5b4f0200 e29b0300 a99a0300 90160300 * + 0010 32970300 d6540100 fad50000 c3b00000 * + 0020 6a270200 7b000100 31de0000 2bf50200 * + 0030 b59b0300 73cf0300 10b90000 84240300 * + 0040 64150100 2e8a0000 88fe0000 c0660000 * + 0050 ffd80200 b0260100 7c060200 e3240200 * + 0060 63ff0100 fb6b0300 0ad90100 523c0200 * + 0070 4d5e0200 8a940200 4b710300 6aa90300 * + 0080 0a2c0300 67e10300 4a3d0300 *
\ No newline at end of file diff --git a/ld/testsuite/ld-pe/pdb-types1-skiplist.d b/ld/testsuite/ld-pe/pdb-types1-skiplist.d new file mode 100644 index 0000000..52c10fa --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types1-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-types1-typelist.d b/ld/testsuite/ld-pe/pdb-types1-typelist.d new file mode 100644 index 0000000..ff2d91c --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types1-typelist.d @@ -0,0 +1,60 @@ + +*: file format binary + +Contents of section .data: + 0000 0a000110 12000000 02000000 0a000110 ................ + 0010 12000000 01000000 0a000110 22000000 ............"... + 0020 02000000 0a000110 74000000 03000000 ........t....... + 0030 0a000210 01100000 0c000100 0a000210 ................ + 0040 02100000 0a800000 12000112 03000000 ................ + 0050 01100000 02100000 03100000 0e000810 ................ + 0060 02100000 00000300 06100000 0e000315 ................ + 0070 04100000 74000000 180000f1 0a000512 ....t........... + 0080 75000000 0100f2f1 0a000512 75000000 u...........u... + 0090 1f01f2f1 22000312 0d150300 09100000 ...."........... + 00a0 00006e75 6d3100f1 0d150300 0a100000 ..num1.......... + 00b0 00006e75 6d3200f1 22000515 02000000 ..num2.."....... + 00c0 0b100000 00000000 00000000 04003c75 ..............<u + 00d0 6e6e616d 65642d74 61673e00 1e000515 nnamed-tag>..... + 00e0 00008002 00000000 00000000 00000000 ................ + 00f0 0000666f 6f006261 7200f2f1 0a000210 ..foo.bar....... + 0100 0d100000 0c000100 06000112 00000000 ................ + 0110 1a000910 02100000 0d100000 0e100000 ................ + 0120 00000000 0f100000 00000000 1a000910 ................ + 0130 02100000 0d100000 0e100000 00000300 ................ + 0140 06100000 00000000 12000612 00000000 ................ + 0150 10100000 00000000 11100000 32000312 ............2... + 0160 0d150300 75000000 00006e75 6d00f2f1 ....u.....num... + 0170 11150000 10100000 6d657468 6f6400f1 ........method.. + 0180 0f150200 12100000 6d657468 6f643200 ........method2. + 0190 1e000515 02000002 13100000 00000000 ................ + 01a0 00000000 0400666f 6f006261 7200f2f1 ......foo.bar... + 01b0 22000312 0d150300 75000000 00006e75 ".......u.....nu + 01c0 6d3100f1 0d150300 10000000 00006e75 m1............nu + 01d0 6d3200f1 1a000615 02000000 15100000 m2.............. + 01e0 04003c75 6e6e616d 65642d74 61673e00 ..<unnamed-tag>. + 01f0 16000615 00008002 00000000 00006261 ..............ba + 0200 7a007175 7800f2f1 16000615 02000002 z.qux........... + 0210 15100000 04006261 7a007175 7800f2f1 ......baz.qux... + 0220 52000312 02150300 00007265 6400f2f1 R.........red... + 0230 02150300 01006772 65656e00 02150300 ......green..... + 0240 0380ffff ffff626c 756500f1 02150300 ......blue...... + 0250 02800080 79656c6c 6f7700f1 02150300 ....yellow...... + 0260 0a800000 00000100 00007075 72706c65 ..........purple + 0270 00f3f2f1 1e000715 00008002 23000000 ............#... + 0280 00000000 636f6c6f 75720063 6f6c6f75 ....colour.colou + 0290 723200f1 1e000715 05000002 23000000 r2..........#... + 02a0 19100000 636f6c6f 75720063 6f6c6f75 ....colour.colou + 02b0 723200f1 0a000312 04140000 19100000 r2.............. + 02c0 06000a00 010000f1 0a000210 1d100000 ................ + 02d0 0c000100 0a000312 02150300 00006100 ..............a. + 02e0 22000715 01000800 75000000 1f100000 ".......u....... + 02f0 71757578 3a3a6e65 73746564 5f656e75 quux::nested_enu + 0300 6d00f2f1 52000312 00140000 14100000 m...R........... + 0310 0400f2f1 09140000 1e100000 01140000 ................ + 0320 14100000 1e100000 00000000 0e150000 ................ + 0330 02100000 73746174 69635f6d 656d6265 ....static_membe + 0340 7200f2f1 10150000 20100000 6e657374 r....... ...nest + 0350 65645f65 6e756d00 1a000515 01000000 ed_enum......... + 0360 21100000 00000000 00000000 04007175 !.............qu + 0370 757800f1 ux..
\ No newline at end of file diff --git a/ld/testsuite/ld-pe/pdb-types1a.s b/ld/testsuite/ld-pe/pdb-types1a.s new file mode 100644 index 0000000..a2ee9a9 --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types1a.s @@ -0,0 +1,27 @@ +.equ CV_SIGNATURE_C13, 4 + +.equ T_LONG, 0x0012 + +.equ LF_MODIFIER, 0x1001 + +.section ".debug$T", "rn" + +.long CV_SIGNATURE_C13 + +# Type 1000, volatile long +.mod1: +.short .mod2 - .mod1 - 2 +.short LF_MODIFIER +.long T_LONG +.short 2 # volatile +.p2align 2 + +# Type 1001, const long +.mod2: +.short .types_end - .mod2 - 2 +.short LF_MODIFIER +.long T_LONG +.short 1 # const +.p2align 2 + +.types_end: diff --git a/ld/testsuite/ld-pe/pdb-types1b.s b/ld/testsuite/ld-pe/pdb-types1b.s new file mode 100644 index 0000000..89ee6e3 --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-types1b.s @@ -0,0 +1,461 @@ +.equ CV_SIGNATURE_C13, 4 + +.equ T_CHAR, 0x0010 +.equ T_LONG, 0x0012 +.equ T_ULONG, 0x0022 +.equ T_INT4, 0x0074 +.equ T_UINT4, 0x0075 +.equ T_UQUAD, 0x0023 + +.equ LF_VTSHAPE, 0x000a +.equ LF_MODIFIER, 0x1001 +.equ LF_POINTER, 0x1002 +.equ LF_PROCEDURE, 0x1008 +.equ LF_MFUNCTION, 0x1009 +.equ LF_ARGLIST, 0x1201 +.equ LF_FIELDLIST, 0x1203 +.equ LF_BITFIELD, 0x1205 +.equ LF_METHODLIST, 0x1206 +.equ LF_BCLASS, 0x1400 +.equ LF_VBCLASS, 0x1401 +.equ LF_INDEX, 0x1404 +.equ LF_VFUNCTAB, 0x1409 +.equ LF_ENUMERATE, 0x1502 +.equ LF_ARRAY, 0x1503 +.equ LF_STRUCTURE, 0x1505 +.equ LF_UNION, 0x1506 +.equ LF_ENUM, 0x1507 +.equ LF_MEMBER, 0x150d +.equ LF_STMEMBER, 0x150e +.equ LF_METHOD, 0x150f +.equ LF_NESTTYPE, 0x1510 +.equ LF_ONEMETHOD, 0x1511 + +.equ LF_USHORT, 0x8002 +.equ LF_LONG, 0x8003 +.equ LF_UQUADWORD, 0x800a + +.equ CV_PTR_NEAR32, 0xa +.equ CV_PTR_64, 0xc + +.section ".debug$T", "rn" + +.long CV_SIGNATURE_C13 + +# Type 1000, const long +.mod1: +.short .mod2 - .mod1 - 2 +.short LF_MODIFIER +.long T_LONG +.short 1 # const +.p2align 2 + +# Type 1001, volatile unsigned long +.mod2: +.short .mod3 - .mod2 - 2 +.short LF_MODIFIER +.long T_ULONG +.short 2 # volatile +.p2align 2 + +# Type 1002, const volatile int +.mod3: +.short .ptr1 - .mod3 - 2 +.short LF_MODIFIER +.long T_INT4 +.short 3 # const volatile +.p2align 2 + +# Type 1003, const long * (64-bit pointer) +.ptr1: +.short .ptr2 - .ptr1 - 2 +.short LF_POINTER +.long 0x1000 +.long (8 << 13) | CV_PTR_64 + +# Type 1004, volatile unsigned long * (32-bit pointer) +.ptr2: +.short .arglist1 - .ptr2 - 2 +.short LF_POINTER +.long 0x1001 +.long (4 << 13) | CV_PTR_NEAR32 + +# Type 1005, arg list of types 1000, 1001, 1002 +.arglist1: +.short .proc1 - .arglist1 - 2 +.short LF_ARGLIST +.long 3 # no. entries +.long 0x1000 +.long 0x1001 +.long 0x1002 + +# Type 1006, procedure, return type 1001, arg list 1005 +.proc1: +.short .arr1 - .proc1 - 2 +.short LF_PROCEDURE +.long 0x1001 +.byte 0 # calling convention +.byte 0 # attributes +.short 3 # no. parameters +.long 0x1005 + +# Type 1007, array[3] of const long * +.arr1: +.short .bitfield1 - .arr1 - 2 +.short LF_ARRAY +.long 0x1003 # element type +.long T_INT4 # index type +.short 24 # length in bytes +.byte 0 # name +.byte 0xf1 # padding + +# Type 1008, bitfield of uint32_t, position 0, length 1 +.bitfield1: +.short .bitfield2 - .bitfield1 - 2 +.short LF_BITFIELD +.long T_UINT4 +.byte 1 +.byte 0 +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1009, bitfield of uint32_t, position 1, length 31 +.bitfield2: +.short .fieldlist1 - .bitfield2 - 2 +.short LF_BITFIELD +.long T_UINT4 +.byte 31 +.byte 1 +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 100a, field list (1008 as num1, 1009 as num2) +.fieldlist1: +.short .struct1 - .fieldlist1 - 2 +.short LF_FIELDLIST +.short LF_MEMBER +.short 3 # public +.long 0x1008 +.short 0 # offset +.asciz "num1" +.byte 0xf1 # padding +.short LF_MEMBER +.short 3 # public +.long 0x1009 +.short 0 # offset +.asciz "num2" +.byte 0xf1 # padding + +# Type 100b, anonymous struct, field list 100a +.struct1: +.short .struct2 - .struct1 - 2 +.short LF_STRUCTURE +.short 2 # no. members +.short 0 # property +.long 0x100a # field list +.long 0 # type derived from +.long 0 # type of vshape table +.short 4 # size +.asciz "<unnamed-tag>" + +# Type 100c, forward declaration of struct foo +.struct2: +.short .ptr3 - .struct2 - 2 +.short LF_STRUCTURE +.short 0 # no. members +.short 0x280 # 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 +.asciz "bar" # unique name +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 100d, pointer to 100c +.ptr3: +.short .arglist2 - .ptr3 - 2 +.short LF_POINTER +.long 0x100c +.long (8 << 13) | CV_PTR_64 + +# Type 100e, empty arg list +.arglist2: +.short .mfunc1 - .arglist2 - 2 +.short LF_ARGLIST +.long 0 # no. entries + +# Type 100f, member function of 100c, return type 1001 +.mfunc1: +.short .mfunc2 - .mfunc1 - 2 +.short LF_MFUNCTION +.long 0x1001 +.long 0x100c +.long 0x100d # type of "this" pointer +.byte 0 # calling convention +.byte 0 # attributes +.short 0 # no. parameters +.long 0x100e # arg list +.long 0 # "this" adjustment + +# Type 1010, member function of 100c, return type 1001, arg list 1005 +.mfunc2: +.short .methodlist1 - .mfunc2 - 2 +.short LF_MFUNCTION +.long 0x1001 +.long 0x100c +.long 0x100d # type of "this" pointer +.byte 0 # calling convention +.byte 0 # attributes +.short 3 # no. parameters +.long 0x1005 # arg list +.long 0 # "this" adjustment + +# Type 1011, method list for both member functions 100f and 1010 +.methodlist1: +.short .fieldlist2 - .methodlist1 - 2 +.short LF_METHODLIST +.short 0 # attributes +.short 0 # padding +.long 0x100f +.short 0 # attributes +.short 0 # padding +.long 0x1010 + +# Type 1012, field list (uint32_t as num1) +.fieldlist2: +.short .struct3 - .fieldlist2 - 2 +.short LF_FIELDLIST +.short LF_MEMBER +.short 3 # public +.long T_UINT4 +.short 0 # offset +.asciz "num" +.byte 0xf2 # padding +.byte 0xf1 # padding +.short LF_ONEMETHOD +.short 0 # method attribute +.long 0x100f # method type +.asciz "method" +.byte 0xf1 +.short LF_METHOD +.short 2 # no. overloads +.long 0x1011 # method list +.asciz "method2" + +# Type 1013, struct foo, field list 1012 +.struct3: +.short .fieldlist3 - .struct3 - 2 +.short LF_STRUCTURE +.short 2 # no. members +.short 0x200 # property (has unique name) +.long 0x1012 # field list +.long 0 # type derived from +.long 0 # type of vshape table +.short 4 # size +.asciz "foo" # name +.asciz "bar" # unique name +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1014, field list (uint32_t as num1, char as num2) +.fieldlist3: +.short .union1 - .fieldlist3 - 2 +.short LF_FIELDLIST +.short LF_MEMBER +.short 3 # public +.long T_UINT4 +.short 0 # offset +.asciz "num1" +.byte 0xf1 # padding +.short LF_MEMBER +.short 3 # public +.long T_CHAR +.short 0 # offset +.asciz "num2" +.byte 0xf1 # padding + +# Type 1015, anonymous union (field list 1014) +.union1: +.short .union2 - .union1 - 2 +.short LF_UNION +.short 2 # no. members +.short 0 # property +.long 0x1014 +.short 4 # size +.asciz "<unnamed-tag>" + +# Type 1016, forward declaration of union baz +.union2: +.short .union3 - .union2 - 2 +.short LF_UNION +.short 0 # no. members +.short 0x280 # property (has unique name, forward declaration) +.long 0 # field list +.short 0 # size +.asciz "baz" +.asciz "qux" +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1017, union baz (field list 1014) +.union3: +.short .fieldlist4 - .union3 - 2 +.short LF_UNION +.short 2 # no. members +.short 0x200 # property (has unique name, forward declaration) +.long 0x1014 # field list +.short 4 # size +.asciz "baz" +.asciz "qux" +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1018, field list for enum (red = 0, green = 1, blue = -1, yellow = 0x8000, purple = 0x100000000) +.fieldlist4: +.short .enum1 - .fieldlist4 - 2 +.short LF_FIELDLIST +.short LF_ENUMERATE +.short 3 # public +.short 0 # value +.asciz "red" +.byte 0xf2 # padding +.byte 0xf1 # padding +.short LF_ENUMERATE +.short 3 # public +.short 1 # value +.asciz "green" +.short LF_ENUMERATE +.short 3 # public +.short LF_LONG +.long 0xffffffff # value +.asciz "blue" +.byte 0xf1 # padding +.short LF_ENUMERATE +.short 3 # public +.short LF_USHORT +.short 0x8000 # value +.asciz "yellow" +.byte 0xf1 # padding +.short LF_ENUMERATE +.short 3 # public +.short LF_UQUADWORD +.quad 0x100000000 # value +.asciz "purple" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1019, forward reference to enum +.enum1: +.short .enum2 - .enum1 - 2 +.short LF_ENUM +.short 0 # no. elements +.short 0x280 # property (has unique name, forward ref) +.long T_UQUAD # underlying type +.long 0 # field list +.asciz "colour" +.asciz "colour2" +.byte 0xf1 # padding + +# Type 101a, enum (field list 1018) +.enum2: +.short .fieldlist5 - .enum2 - 2 +.short LF_ENUM +.short 5 # no. elements +.short 0x200 # property (has unique name) +.long T_UQUAD # underlying type +.long 0x1018 # field list +.asciz "colour" +.asciz "colour2" +.byte 0xf1 # padding + +# Type 101b, field list referencing other field list 1018 +.fieldlist5: +.short .vtshape1 - .fieldlist5 - 2 +.short LF_FIELDLIST +.short LF_INDEX +.short 0 # padding +.long 0x1018 + +# Type 101c, virtual function table shape +.vtshape1: +.short .ptr4 - .vtshape1 - 2 +.short LF_VTSHAPE +.short 1 # no. descriptors +.byte 0 # descriptor (CV_VTS_near) +.byte 0xf1 # padding + +# Type 101d, pointer to 101c +.ptr4: +.short .fieldlist6 - .ptr4 - 2 +.short LF_POINTER +.long 0x101c +.long (8 << 13) | CV_PTR_64 + +# Type 101e, fieldlist for enum +.fieldlist6: +.short .enum3 - .fieldlist6 - 2 +.short LF_FIELDLIST +.short LF_ENUMERATE +.short 3 # public +.short 0 # value +.asciz "a" + +# Type 101f, nested enum +.enum3: +.short .fieldlist7 - .enum3 - 2 +.short LF_ENUM +.short 1 # no. elements +.short 0x8 # property (is nested) +.long T_UINT4 # underlying type +.long 0x101e # field list +.asciz "quux::nested_enum" +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1020, field list for struct quux +.fieldlist7: +.short .struct4 - .fieldlist7 - 2 +.short LF_FIELDLIST +.short LF_BCLASS +.short 0 # attributes +.long 0x1013 # base class +.short 4 # offset within class +.byte 0xf2 # padding +.byte 0xf1 # padding +.short LF_VFUNCTAB +.short 0 # padding +.long 0x101d # pointer to vtshape +.short LF_VBCLASS +.short 0 # attribute +.long 0x1013 # type index of direct virtual base class +.long 0x101d # type index of virtual base pointer +.short 0 # virtual base pointer offset +.short 0 # virtual base offset from vbtable +.short LF_STMEMBER +.short 0 # attribute +.long 0x1001 # volatile unsigned long +.asciz "static_member" +.byte 0xf2 # padding +.byte 0xf1 # padding +.short LF_NESTTYPE +.short 0 # padding +.long 0x101f # enum type +.asciz "nested_enum" + +# Type 1021, struct quux, field list 1020 +.struct4: +.short .types_end - .struct4 - 2 +.short LF_STRUCTURE +.short 1 # no. members +.short 0 # property +.long 0x1020 # field list +.long 0 # type derived from +.long 0 # type of vshape table +.short 4 # size +.asciz "quux" # name +.byte 0xf1 # padding + +.types_end: diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp index 2e5f834..5661438 100644 --- a/ld/testsuite/ld-pe/pdb.exp +++ b/ld/testsuite/ld-pe/pdb.exp @@ -977,7 +977,179 @@ proc test4 { } { } } +proc test5 { } { + global as + global ar + global ld + global objdump + global srcdir + global subdir + + if ![ld_assemble $as $srcdir/$subdir/pdb-types1a.s tmpdir/pdb-types1a.o] { + unsupported "Build pdb-types1a.o" + return + } + + if ![ld_assemble $as $srcdir/$subdir/pdb-types1b.s tmpdir/pdb-types1b.o] { + unsupported "Build pdb-types1b.o" + return + } + + if ![ld_link $ld "tmpdir/pdb-types1.exe" "--pdb=tmpdir/pdb-types1.pdb tmpdir/pdb-types1a.o tmpdir/pdb-types1b.o"] { + unsupported "Create PE image with PDB file" + return + } + + set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types1.pdb 0002"] + + if ![string match "" $exec_output] { + fail "Could not extract TPI stream" + return + } else { + pass "Extracted TPI stream" + } + + # check values in TPI header, and save anything interesting + + set fi [open tmpdir/0002] + 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 TPI stream." + } else { + pass "Correct first type value in TPI 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 != 0x1023 } { + fail "Incorrect end type value in TPI stream." + } else { + pass "Correct end type value in TPI 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 TPI stream." + } else { + pass "Correct hash size in TPI stream." + } + + set data [read $fi 4] + binary scan $data i num_buckets + + if { $num_buckets != 0x3ffff } { + fail "Incorrect number of buckets in TPI stream." + } else { + pass "Correct number of buckets in TPI 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-types1-typelist w] + fconfigure $fi -translation binary + puts -nonewline $fi $type_list + close $fi + + # check type list + + set exp [file_contents "$srcdir/$subdir/pdb-types1-typelist.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-typelist"] + if ![string match $exp $got] { + fail "Incorrect type list in TPI stream." + } else { + pass "Correct type list in TPI 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-types1.pdb $index_str"] + + if ![string match "" $exec_output] { + fail "Could not extract TPI hash stream." + } else { + pass "Extracted TPI 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-types1-hashlist w] + fconfigure $fi -translation binary + puts -nonewline $fi $hash_list + close $fi + + set exp [file_contents "$srcdir/$subdir/pdb-types1-hashlist.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-hashlist"] + if ![string match $exp $got] { + fail "Incorrect hash list in TPI stream." + } else { + pass "Correct hash list in TPI stream." + } + + # check skip list + + set fi [open tmpdir/pdb-types1-skiplist w] + fconfigure $fi -translation binary + puts -nonewline $fi $skip_list + close $fi + + set exp [file_contents "$srcdir/$subdir/pdb-types1-skiplist.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-skiplist"] + if ![string match $exp $got] { + fail "Incorrect skip list in TPI stream." + } else { + pass "Correct skip list in TPI stream." + } +} + test1 test2 test3 test4 +test5 |