From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wr1-x435.google.com (mail-wr1-x435.google.com [IPv6:2a00:1450:4864:20::435]) by sourceware.org (Postfix) with ESMTPS id 0BFD53858401 for ; Tue, 29 Nov 2022 00:10:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 0BFD53858401 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=harmstone.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wr1-x435.google.com with SMTP id bx10so7506706wrb.0 for ; Mon, 28 Nov 2022 16:10:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=dirKYpHgm1NzJihQ8YNa0tD/jiDSivuYzHIQLqVz2Gc=; b=naI16Hog3TEjuqNrDEbvRExYIu+rORHK4KC7NtjdKi9sEGJAc80WByM2VrS9QraUcc K/NYXhftswKO6Vv9hy5G7iSg4OmGbnGK3jSCEdgno5/OowDOX0hY6i3ps0JdZUijAeS0 2EM8vpFEdihlBOUH46fVJLaUZmR1wqaQMyg21vFi3MLR0nXaoIH8WIhoKx6Rr+W1+aQ0 pkaG/8gt62ZEaYrp7on++31vwwuEtOWDBccM4Vc4CUC0UBaZvW2WtHSNv+cmCTV/a19e 8BnFUBxczixA2+wFJEuQUi5MnDrI0ikcagzaRVyA/LBwkPYUnb4Gm2u6qmONlbRtSQX1 meuA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=dirKYpHgm1NzJihQ8YNa0tD/jiDSivuYzHIQLqVz2Gc=; b=LaE4R0Yd5L8n05YEnTEzYk/+g2l+LV/FEVXurDz25v0WyOW7zCxb4GrFkyf2DQBFQZ wD5q7toyOiiNufG9Wbf2fVBp5+Or1Ji5C76op6D9/o1bnIIlBK5ZIC7ukl1pyY6pUOEC jBHkDXVymfUki4vUV4o/NSXy6fST4YnLn/8uUJY44dONKFpaIz8uT9z3XgB+Q8CbQ7m3 EvmVUlGFd1u6NW2cxOob9fB6gGZ9CCTRZcB7D8UJwOsnznJmeCa+xL+oJpZz5Gm3oHir VyxmdIOPv1TNNMPiFaVl9EX6IeZ05JN7F4ST55L03XNGeiBj/YAKepC1XcPUP6aGERMv TQtA== X-Gm-Message-State: ANoB5pkQlC5OdImtiPrcVX/jGZW1P6K+IUaTrMUbG15dfvgjVgfUZJj+ TZT/ImrJZCBRBfOCOkUGn40E0WbPq2E= X-Google-Smtp-Source: AA0mqf5lnX+iCu07awmUguaZ7CskKpUaWtvDJk9DYD3NXsKuFCeoBbv2uYNrAOG1ktW5a0346UjpQg== X-Received: by 2002:a5d:448c:0:b0:242:1f22:df20 with SMTP id j12-20020a5d448c000000b002421f22df20mr1501253wrq.173.1669680621568; Mon, 28 Nov 2022 16:10:21 -0800 (PST) Received: from beren.harmstone.com ([2a02:8010:64ea:0:8eb8:7eff:fe53:9d5f]) by smtp.gmail.com with ESMTPSA id r11-20020a05600c35cb00b003a84375d0d1sm57570wmq.44.2022.11.28.16.10.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 28 Nov 2022 16:10:20 -0800 (PST) Sender: Mark Harmstone From: Mark Harmstone To: binutils@sourceware.org Cc: Mark Harmstone Subject: [PATCH] ld: Write types into TPI stream of PDB Date: Tue, 29 Nov 2022 00:10:13 +0000 Message-Id: <20221129001015.21775-1-mark@harmstone.com> X-Mailer: git-send-email 2.37.4 In-Reply-To: <20221127023840.32080-2-mark@harmstone.com> References: <20221127023840.32080-2-mark@harmstone.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_EF,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM,GIT_PATCH_0,HEADER_FROM_DIFFERENT_DOMAINS,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: This parses the .debug$T section of the object files, and uses them to populate the type information stream. This and the following patches ought to cover all the CodeView types that Clang will emit, and all the types that I've seen MSVC emit. Sorry, I know this is massive, but there didn't seem much sense in splitting it up - most of it is just parsing arbitrary structs. The actual meaning of the records doesn't matter that much here, merely how they reference other types and how their hashes are calculated. As with the other patches, Microsoft's cvdump.exe can be used to generate a textual version of what's output here. --- ld/pdb.c | 1284 +++++++++++++++++++++- ld/pdb.h | 264 +++++ ld/testsuite/ld-pe/pdb-types1-hashlist.d | 13 + ld/testsuite/ld-pe/pdb-types1-skiplist.d | 5 + ld/testsuite/ld-pe/pdb-types1-typelist.d | 60 + ld/testsuite/ld-pe/pdb-types1a.s | 27 + ld/testsuite/ld-pe/pdb-types1b.s | 461 ++++++++ ld/testsuite/ld-pe/pdb.exp | 172 +++ 8 files changed, 2264 insertions(+), 22 deletions(-) create mode 100644 ld/testsuite/ld-pe/pdb-types1-hashlist.d create mode 100644 ld/testsuite/ld-pe/pdb-types1-skiplist.d create mode 100644 ld/testsuite/ld-pe/pdb-types1-typelist.d create mode 100644 ld/testsuite/ld-pe/pdb-types1a.s create mode 100644 ld/testsuite/ld-pe/pdb-types1b.s diff --git a/ld/pdb.c b/ld/pdb.c index cfc76004227..979ea126aa5 100644 --- a/ld/pdb.c +++ b/ld/pdb.c @@ -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; } @@ -866,6 +1033,1003 @@ 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[] = ""; + static const char tag2[] = "__unnamed"; + static const char tag3[] = "::"; + 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 immediate follows. */ + if (val >= 0x8000) + { + size_t param_len; + + switch (val) { + case LF_CHAR: + param_len = 1; + break; + + case LF_SHORT: + case LF_USHORT: + param_len = 2; + break; + + case LF_LONG: + case LF_ULONG: + param_len = 4; + break; + + case LF_QUADWORD: + case LF_UQUADWORD: + param_len = 8; + break; + + default: + 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 +2037,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 +2059,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. */ @@ -933,7 +2106,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; @@ -1021,7 +2195,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++) { @@ -1343,7 +2518,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; @@ -1355,7 +2531,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)) @@ -1886,6 +3062,32 @@ 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 @@ -1894,9 +3096,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) @@ -1930,7 +3133,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")); @@ -1946,7 +3151,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")); @@ -1987,15 +3194,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)) diff --git a/ld/pdb.h b/ld/pdb.h index bbb106043c4..ecc26c1c87a 100644 --- a/ld/pdb.h +++ b/ld/pdb.h @@ -27,6 +27,41 @@ #include "sysdep.h" #include "bfd.h" #include +#include + +#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 00000000000..b75f08c1de7 --- /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 00000000000..52c10fa501d --- /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 00000000000..ff2d91c311e --- /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 ................... + 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 ... + 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 00000000000..a2ee9a9972f --- /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 00000000000..89ee6e3840f --- /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 "" + +# 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 "" + +# 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 2e5f83477aa..5661438c9e9 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 -- 2.37.4