From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by sourceware.org (Postfix) with ESMTPS id C0BC8382FCBC for ; Mon, 5 Dec 2022 01:53:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C0BC8382FCBC 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-wm1-x329.google.com with SMTP id n7so7707170wms.3 for ; Sun, 04 Dec 2022 17:53:54 -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=eWgjVJ//XUZHaJvgIB8svUE2CokyqEzGGtPgPQ7TzVw=; b=DqB4YpJUpToAz1xCf3LhNvlctOb6L6+KssHT/rN7Ac7KHBqlB7d/mHgAnvfhfsQw1K n8eHWW+eG7e7FbGoDVLBxTiIuzxkFBRrHjm76mHMluO7A3+k9FRSpdzv49rRZzP5rzEO k0unJRlsTocljEpAPy2ozkeStFIYyd+sLeWaAzOOhn/LIZcq4nFVyRBCx33indeO7hIq IRzMdx0ctEMj27vVV9HIrF3epqyoWAizBvoRfD/aePr2vDyVmrFGP3zQCcFWSXMSCmYv zJoHS9OiRvkpX1AB4zZKnVYGsnLjhSBRe7hO98v3O/z293M9AoZiRMA6dNvb3Zg/L4e8 er1A== 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=eWgjVJ//XUZHaJvgIB8svUE2CokyqEzGGtPgPQ7TzVw=; b=Ig2tBTjTOBARatKhQjnsT7OvLCVKMnz5WbyW/NKgrAbMJQg/jbi97gzUhry3mdIfxE i8Wic/EVjVv5f/Sm7t0M7ad68NfpaJEig/0YFC+cVh0jctb4qP6SZ+V1z3NF9kdooLg2 VGjp2+ILLX0YPjuTVcRiCCD4PG4ZRlEa29+2JpiYZ3v50SKcx90pHBFNM7C9qe0Ijq6F AxhdMMQ9ElX8jYsz4EwEC3Mamf5zH63H7mXQp/KQvvoMrQ9hIgVvYG4wF44aIUsBSFy5 /hypxLHl3iTyxZdPlYWD27qXHrLd7SzISgDeXNvnOTIqPPCAA+SZIRr1Uk1FjJVOXaLl gQjA== X-Gm-Message-State: ANoB5pkzNvp8DMuBv6g7TbXddJzftiDLVFb/6MPZzlG3sWH6uZKmyuFr JexxCXQIpJr8n49MaJ9cP6ekt0PGOvU= X-Google-Smtp-Source: AA0mqf4+0MOuXSr5nYH2693VtlMAsp0tHyH4OnFRHKOiOtmgMRntTgeRALJjlsWn73gvkAMkKPicHA== X-Received: by 2002:a05:600c:c8:b0:3cf:b287:7722 with SMTP id u8-20020a05600c00c800b003cfb2877722mr58686155wmm.51.1670205232365; Sun, 04 Dec 2022 17:53:52 -0800 (PST) Received: from beren.harmstone.com ([2a02:8010:64ea:0:8eb8:7eff:fe53:9d5f]) by smtp.gmail.com with ESMTPSA id l28-20020a05600c1d1c00b003c6b7f5567csm26288362wms.0.2022.12.04.17.53.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 04 Dec 2022 17:53:51 -0800 (PST) Sender: Mark Harmstone From: Mark Harmstone To: binutils@sourceware.org Cc: Mark Harmstone Subject: [PATCH] ld: Write globals stream in PDB Date: Mon, 5 Dec 2022 01:53:45 +0000 Message-Id: <20221205015347.25781-1-mark@harmstone.com> X-Mailer: git-send-email 2.37.4 In-Reply-To: <20221129001015.21775-3-mark@harmstone.com> References: <20221129001015.21775-3-mark@harmstone.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.2 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: Parses the .debug$S sections of object files for DEBUG_S_SYMBOLS subsections, creating a globals stream based on the records we find. These also get copied into the module stream for each object file. The globals stream lists the addresses and types of procedures and global variables - similar to the publics stream, but using the unmangled names. --- ld/pdb.c | 1133 +++++++++++++++++++++-- ld/pdb.h | 77 ++ ld/testsuite/ld-pe/pdb-syms1-globals.d | 57 ++ ld/testsuite/ld-pe/pdb-syms1-records.d | 61 ++ ld/testsuite/ld-pe/pdb-syms1-symbols1.d | 8 + ld/testsuite/ld-pe/pdb-syms1-symbols2.d | 56 ++ ld/testsuite/ld-pe/pdb-syms1a.s | 110 +++ ld/testsuite/ld-pe/pdb-syms1b.s | 737 +++++++++++++++ ld/testsuite/ld-pe/pdb.exp | 164 ++++ 9 files changed, 2306 insertions(+), 97 deletions(-) create mode 100644 ld/testsuite/ld-pe/pdb-syms1-globals.d create mode 100644 ld/testsuite/ld-pe/pdb-syms1-records.d create mode 100644 ld/testsuite/ld-pe/pdb-syms1-symbols1.d create mode 100644 ld/testsuite/ld-pe/pdb-syms1-symbols2.d create mode 100644 ld/testsuite/ld-pe/pdb-syms1a.s create mode 100644 ld/testsuite/ld-pe/pdb-syms1b.s diff --git a/ld/pdb.c b/ld/pdb.c index 6326e018666..268e2521b03 100644 --- a/ld/pdb.c +++ b/ld/pdb.c @@ -88,6 +88,24 @@ struct types struct type_entry *last; }; +struct global +{ + struct global *next; + uint32_t offset; + uint32_t hash; + uint32_t refcount; + unsigned int index; + uint8_t data[]; +}; + +struct globals +{ + uint32_t num_entries; + struct global *first; + struct global *last; + htab_t hashmap; +}; + static const uint32_t crc_table[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, @@ -747,50 +765,697 @@ add_string (char *str, size_t len, struct string_table *strings) s = (struct string *) *slot; } - return s->offset; -} + return s->offset; +} + +/* Return the hash of an entry in the string table. */ +static hashval_t +hash_string_table_entry (const void *p) +{ + const struct string *s = (const struct string *) p; + + return s->hash; +} + +/* Compare an entry in the string table with a string. */ +static int +eq_string_table_entry (const void *a, const void *b) +{ + const struct string *s1 = (const struct string *) a; + const char *s2 = (const char *) b; + size_t s2_len = strlen (s2); + + if (s2_len != s1->len) + return 0; + + return memcmp (s1->s, s2, s2_len) == 0; +} + +/* Parse the string table within the .debug$S section. */ +static void +parse_string_table (bfd_byte *data, size_t size, + struct string_table *strings) +{ + while (true) + { + size_t len = strnlen ((char *) data, size); + + add_string ((char *) data, len, strings); + + data += len + 1; + + if (size <= len + 1) + break; + + size -= len + 1; + } +} + +/* Remap a type reference within a CodeView symbol. */ +static bool remap_symbol_type (void *data, struct type_entry **map, + 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 + num_types) + { + einfo (_("%P: CodeView symbol references out of range type %v\n"), + type); + return false; + } + + type = TPI_FIRST_INDEX + map[type - TPI_FIRST_INDEX]->index; + bfd_putl32 (type, data); + + return true; +} + +/* Add an entry into the globals stream. If it already exists, increase + the refcount. */ +static bool +add_globals_ref (struct globals *glob, bfd *sym_rec_stream, const char *name, + size_t name_len, uint8_t *data, size_t len) +{ + void **slot; + uint32_t hash; + struct global *g; + + slot = htab_find_slot_with_hash (glob->hashmap, data, + iterative_hash (data, len, 0), INSERT); + + if (*slot) + { + g = *slot; + g->refcount++; + return true; + } + + *slot = xmalloc (offsetof (struct global, data) + len); + + hash = crc32 ((const uint8_t *) name, name_len); + hash %= NUM_GLOBALS_HASH_BUCKETS; + + g = *slot; + g->next = NULL; + g->offset = bfd_tell (sym_rec_stream); + g->hash = hash; + g->refcount = 1; + memcpy (g->data, data, len + 1); + + glob->num_entries++; + + if (glob->last) + glob->last->next = g; + else + glob->first = g; + + glob->last = g; + + return bfd_bwrite (data, len, sym_rec_stream) == len; +} + +/* Find the end of the current scope within symbols data. */ +static uint8_t * +find_end_of_scope (uint8_t *data, uint32_t size) +{ + unsigned int scope_level = 1; + uint16_t len; + + len = bfd_getl16 (data) + sizeof (uint16_t); + + data += len; + size -= len; + + while (true) + { + uint16_t type; + + if (size < sizeof (uint32_t)) + return NULL; + + len = bfd_getl16 (data) + sizeof (uint16_t); + type = bfd_getl16 (data + sizeof (uint16_t)); + + if (size < len) + return NULL; + + switch (type) + { + case S_GPROC32: + case S_LPROC32: + scope_level++; + break; + + case S_END: + case S_PROC_ID_END: + scope_level--; + + if (scope_level == 0) + return data; + + break; + } + + data += len; + size -= len; + } +} + +/* 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 symbols in a .debug$S section, and copy them to the module's + symbol stream. */ +static bool +parse_symbols (uint8_t *data, uint32_t size, uint8_t **buf, + struct type_entry **map, uint32_t num_types, + bfd *sym_rec_stream, struct globals *glob, uint16_t mod_num) +{ + uint8_t *orig_buf = *buf; + unsigned int scope_level = 0; + + while (size >= sizeof (uint16_t)) + { + uint16_t len, type; + + len = bfd_getl16 (data) + sizeof (uint16_t); + + if (len > size) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + type = bfd_getl16 (data + sizeof (uint16_t)); + + switch (type) + { + case S_LDATA32: + case S_GDATA32: + case S_LTHREAD32: + case S_GTHREAD32: + { + struct datasym *d = (struct datasym *) data; + size_t name_len; + + if (len < offsetof (struct datasym, name)) + { + einfo (_("%P: warning: truncated CodeView record" + " S_LDATA32/S_GDATA32/S_LTHREAD32/S_GTHREAD32\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + if (scope_level == 0) + { + uint16_t section = bfd_getl16 (&d->section); + + if (section == 0) /* GC'd, ignore */ + break; + } + + name_len = + strnlen (d->name, len - offsetof (struct datasym, name)); + + if (name_len == len - offsetof (struct datasym, name)) + { + einfo (_("%P: warning: name for S_LDATA32/S_GDATA32/" + "S_LTHREAD32/S_GTHREAD32 has no terminating" + " zero\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + if (!remap_symbol_type (&d->type, map, num_types)) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + /* If S_LDATA32 or S_LTHREAD32, copy into module symbols. */ + + if (type == S_LDATA32 || type == S_LTHREAD32) + { + memcpy (*buf, d, len); + *buf += len; + } + + /* S_LDATA32 and S_LTHREAD32 only go in globals if + not in function scope. */ + if (type == S_GDATA32 || type == S_GTHREAD32 || scope_level == 0) + { + if (!add_globals_ref (glob, sym_rec_stream, d->name, + name_len, data, len)) + return false; + } + + break; + } + + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + { + struct procsym *proc = (struct procsym *) data; + size_t name_len; + uint16_t section; + uint32_t end; + uint8_t *endptr; + size_t ref_size, padding; + struct refsym *ref; + + if (len < offsetof (struct procsym, name)) + { + einfo (_("%P: warning: truncated CodeView record" + " S_GPROC32/S_LPROC32\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + section = bfd_getl16 (&proc->section); + + endptr = find_end_of_scope (data, size); + + if (!endptr) + { + einfo (_("%P: warning: could not find end of" + " S_GPROC32/S_LPROC32 record\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + if (section == 0) /* skip if GC'd */ + { + /* Skip to after S_END. */ + + size -= endptr - data; + data = endptr; + + len = bfd_getl16 (data) + sizeof (uint16_t); + + data += len; + size -= len; + + continue; + } + + name_len = + strnlen (proc->name, len - offsetof (struct procsym, name)); + + if (name_len == len - offsetof (struct procsym, name)) + { + einfo (_("%P: warning: name for S_GPROC32/S_LPROC32 has no" + " terminating zero\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + if (type == S_GPROC32_ID || type == S_LPROC32_ID) + { + /* Transform into S_GPROC32 / S_LPROC32. */ + + uint32_t t_idx = bfd_getl32 (&proc->type); + struct type_entry *t; + uint16_t t_type; + + if (t_idx < TPI_FIRST_INDEX + || t_idx >= TPI_FIRST_INDEX + num_types) + { + einfo (_("%P: CodeView symbol references out of range" + " type %v\n"), type); + bfd_set_error (bfd_error_bad_value); + return false; + } + + t = map[t_idx - TPI_FIRST_INDEX]; + + t_type = bfd_getl16 (t->data + sizeof (uint16_t)); + + switch (t_type) + { + case LF_FUNC_ID: + { + struct lf_func_id *t_data = + (struct lf_func_id *) t->data; + + /* Replace proc->type with function type. */ + + memcpy (&proc->type, &t_data->function_type, + sizeof (uint32_t)); + + break; + } + + case LF_MFUNC_ID: + { + struct lf_mfunc_id *t_data = + (struct lf_mfunc_id *) t->data; + + /* Replace proc->type with function type. */ + + memcpy (&proc->type, &t_data->function_type, + sizeof (uint32_t)); + + break; + } + + default: + einfo (_("%P: CodeView S_GPROC32_ID/S_LPROC32_ID symbol" + " referenced unknown type as ID\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + /* Change record type. */ + + if (type == S_GPROC32_ID) + bfd_putl32 (S_GPROC32, &proc->kind); + else + bfd_putl32 (S_LPROC32, &proc->kind); + } + else + { + if (!remap_symbol_type (&proc->type, map, num_types)) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + } + + end = *buf - orig_buf + sizeof (uint32_t) + endptr - data; + bfd_putl32 (end, &proc->end); + + /* Add S_PROCREF / S_LPROCREF to globals stream. */ + + ref_size = offsetof (struct refsym, name) + name_len + 1; + + if (ref_size % sizeof (uint32_t)) + padding = sizeof (uint32_t) - (ref_size % sizeof (uint32_t)); + else + padding = 0; + + ref = xmalloc (ref_size + padding); + + bfd_putl16 (ref_size + padding - sizeof (uint16_t), &ref->size); + bfd_putl16 (type == S_GPROC32 || type == S_GPROC32_ID ? + S_PROCREF : S_LPROCREF, &ref->kind); + bfd_putl32 (0, &ref->sum_name); + bfd_putl32 (*buf - orig_buf + sizeof (uint32_t), + &ref->symbol_offset); + bfd_putl16 (mod_num + 1, &ref->mod); + + memcpy (ref->name, proc->name, name_len + 1); + + memset (ref->name + name_len + 1, 0, padding); + + if (!add_globals_ref (glob, sym_rec_stream, proc->name, name_len, + (uint8_t *) ref, ref_size + padding)) + { + free (ref); + return false; + } + + free (ref); + + memcpy (*buf, proc, len); + *buf += len; + + scope_level++; + + break; + } + + case S_UDT: + { + struct udtsym *udt = (struct udtsym *) data; + size_t name_len; + + if (len < offsetof (struct udtsym, name)) + { + einfo (_("%P: warning: truncated CodeView record" + " S_UDT\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + name_len = + strnlen (udt->name, len - offsetof (struct udtsym, name)); + + if (name_len == len - offsetof (struct udtsym, name)) + { + einfo (_("%P: warning: name for S_UDT has no" + " terminating zero\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + if (!remap_symbol_type (&udt->type, map, num_types)) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + /* S_UDT goes in the symbols stream if within a procedure, + otherwise it goes in the globals stream. */ + if (scope_level == 0) + { + if (!add_globals_ref (glob, sym_rec_stream, udt->name, + name_len, data, len)) + return false; + } + else + { + memcpy (*buf, udt, len); + *buf += len; + } + + break; + } + + case S_CONSTANT: + { + struct constsym *c = (struct constsym *) data; + size_t name_len, rec_size; + uint16_t val; + + if (len < offsetof (struct constsym, name)) + { + einfo (_("%P: warning: truncated CodeView record" + " S_CONSTANT\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + rec_size = offsetof (struct constsym, name); + + val = bfd_getl16 (&c->value); + + /* If val >= 0x8000, actual value follows. */ + if (val >= 0x8000) + { + unsigned int param_len = extended_value_len (val); + + if (param_len == 0) + { + einfo (_("%P: warning: unhandled type %v within" + " S_CONSTANT\n"), val); + bfd_set_error (bfd_error_bad_value); + return false; + } + + rec_size += param_len; + } + + name_len = + strnlen ((const char *) data + rec_size, len - rec_size); + + if (name_len == len - rec_size) + { + einfo (_("%P: warning: name for S_CONSTANT has no" + " terminating zero\n")); + bfd_set_error (bfd_error_bad_value); + return false; + } + + if (!remap_symbol_type (&c->type, map, num_types)) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + + if (!add_globals_ref (glob, sym_rec_stream, + (const char *) data + rec_size, name_len, + data, len)) + return false; + + break; + } + + case S_END: + case S_PROC_ID_END: + memcpy (*buf, data, len); + + if (type == S_PROC_ID_END) /* transform to S_END */ + bfd_putl16 (S_END, *buf + sizeof (uint16_t)); + + *buf += len; + scope_level--; + break; + + default: + einfo (_("%P: warning: unrecognized CodeView record %v\n"), type); + bfd_set_error (bfd_error_bad_value); + return false; + } + + data += len; + size -= len; + } + + return true; +} + +/* For a given symbol subsection, work out how much space to allocate in the + result module stream. This is different because we don't copy certain + symbols, such as S_CONSTANT, and we skip over any procedures or data that + have been GC'd out. */ +static bool +calculate_symbols_size (uint8_t *data, uint32_t size, uint32_t *sym_size) +{ + unsigned int scope_level = 0; + + while (size >= sizeof (uint32_t)) + { + uint16_t len = bfd_getl16 (data) + sizeof (uint16_t); + uint16_t type = bfd_getl16 (data + sizeof (uint16_t)); + + switch (type) + { + case S_LDATA32: + case S_LTHREAD32: + { + struct datasym *d = (struct datasym *) data; + uint16_t section; + + if (len < offsetof (struct datasym, name)) + { + einfo (_("%P: warning: truncated CodeView record" + " S_LDATA32/S_LTHREAD32\n")); + return false; + } + + section = bfd_getl16 (&d->section); + + /* copy if not GC'd or within function */ + if (scope_level != 0 || section != 0) + *sym_size += len; + } + + case S_GDATA32: + case S_GTHREAD32: + case S_CONSTANT: + /* Not copied into symbols stream. */ + break; + + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + { + struct procsym *proc = (struct procsym *) data; + uint16_t section; + + if (len < offsetof (struct procsym, name)) + { + einfo (_("%P: warning: truncated CodeView record" + " S_GPROC32/S_LPROC32\n")); + return false; + } + + section = bfd_getl16 (&proc->section); + + if (section != 0) + { + *sym_size += len; + } + else + { + uint8_t *endptr = find_end_of_scope (data, size); + + if (!endptr) + { + einfo (_("%P: warning: could not find end of" + " S_GPROC32/S_LPROC32 record\n")); + return false; + } -/* Return the hash of an entry in the string table. */ -static hashval_t -hash_string_table_entry (const void *p) -{ - const struct string *s = (const struct string *) p; + /* Skip to after S_END. */ - return s->hash; -} + size -= endptr - data; + data = endptr; -/* Compare an entry in the string table with a string. */ -static int -eq_string_table_entry (const void *a, const void *b) -{ - const struct string *s1 = (const struct string *) a; - const char *s2 = (const char *) b; - size_t s2_len = strlen (s2); + len = bfd_getl16 (data) + sizeof (uint16_t); - if (s2_len != s1->len) - return 0; + data += len; + size -= len; - return memcmp (s1->s, s2, s2_len) == 0; -} + continue; + } -/* Parse the string table within the .debug$S section. */ -static void -parse_string_table (bfd_byte *data, size_t size, - struct string_table *strings) -{ - while (true) - { - size_t len = strnlen ((char *) data, size); + scope_level++; - add_string ((char *) data, len, strings); + break; + } - data += len + 1; + case S_UDT: + if (scope_level != 0) /* only goes in symbols if local */ + *sym_size += len; + break; - if (size <= len + 1) - break; + case S_END: /* always copied */ + case S_PROC_ID_END: + *sym_size += len; + scope_level--; + break; - size -= len + 1; + default: + einfo (_("%P: warning: unrecognized CodeView record %v\n"), type); + return false; + } + + data += len; + size -= len; } + + return true; } /* Parse the .debug$S section within an object file. */ @@ -798,13 +1463,17 @@ static bool handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, uint8_t **dataptr, uint32_t *sizeptr, struct mod_source_files *mod_source, - bfd *abfd) + bfd *abfd, uint8_t **syms, uint32_t *sym_byte_size, + struct type_entry **map, uint32_t num_types, + bfd *sym_rec_stream, struct globals *glob, + uint16_t mod_num) { bfd_byte *data = NULL; size_t off; uint32_t c13_size = 0; char *string_table = NULL; - uint8_t *buf, *bufptr; + uint8_t *buf, *bufptr, *symbuf, *symbufptr; + uint32_t sym_size = 0; if (!bfd_get_full_section_contents (mod, s, &data)) return false; @@ -943,6 +1612,16 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, break; } + + case DEBUG_S_SYMBOLS: + if (!calculate_symbols_size (data + off, size, &sym_size)) + { + free (data); + bfd_set_error (bfd_error_bad_value); + return false; + } + + break; } off += size; @@ -951,7 +1630,10 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, off += sizeof (uint32_t) - (off % sizeof (uint32_t)); } - if (c13_size == 0) + if (sym_size % sizeof (uint32_t)) + sym_size += sizeof (uint32_t) - (sym_size % sizeof (uint32_t)); + + if (c13_size == 0 && sym_size == 0) { free (data); return true; @@ -959,8 +1641,25 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, /* copy data */ - buf = xmalloc (c13_size); - bufptr = buf; + if (c13_size != 0) + { + buf = xmalloc (c13_size); + bufptr = buf; + } + else + { + buf = NULL; + } + + if (sym_size != 0) + { + symbuf = xmalloc (sym_size); + symbufptr = symbuf; + } + else + { + symbuf = NULL; + } off = sizeof (uint32_t); @@ -982,6 +1681,7 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, mod_source)) { free (data); + free (symbuf); return false; } @@ -1010,6 +1710,17 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, break; } + + case DEBUG_S_SYMBOLS: + if (!parse_symbols (data + off, size, &symbufptr, map, num_types, + sym_rec_stream, glob, mod_num)) + { + free (data); + free (symbuf); + return false; + } + + break; } off += size; @@ -1020,22 +1731,42 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, free (data); - if (*dataptr) + if (buf) { - /* Append the C13 info to what's already there, if the module has - multiple .debug$S sections. */ + if (*dataptr) + { + /* Append the C13 info to what's already there, if the module has + multiple .debug$S sections. */ - *dataptr = xrealloc (*dataptr, *sizeptr + c13_size); - memcpy (*dataptr + *sizeptr, buf, c13_size); + *dataptr = xrealloc (*dataptr, *sizeptr + c13_size); + memcpy (*dataptr + *sizeptr, buf, c13_size); - free (buf); + free (buf); + } + else + { + *dataptr = buf; + } + + *sizeptr += c13_size; } - else + + if (symbuf) { - *dataptr = buf; - } + if (*syms) + { + *syms = xrealloc (*syms, *sym_byte_size + sym_size); + memcpy (*syms + *sym_byte_size, symbuf, sym_size); + + free (symbuf); + } + else + { + *syms = symbuf; + } - *sizeptr += c13_size; + *sym_byte_size += sym_size; + } return true; } @@ -1455,33 +2186,14 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num, /* If val >= 0x8000, the actual value immediate follows. */ if (val >= 0x8000) { - size_t param_len; - - switch (val) { - case LF_CHAR: - param_len = 1; - break; + unsigned int param_len = extended_value_len (val); - 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: + if (param_len == 0) + { einfo (_("%P: warning: unhandled type %v within" " LF_ENUMERATE\n"), val); return false; - } + } if (left < subtype_len + param_len) { @@ -2274,12 +2986,11 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num, static bool handle_debugt_section (asection *s, bfd *mod, struct types *types, struct types *ids, uint16_t mod_num, - struct string_table *strings) + struct string_table *strings, + struct type_entry ***map, uint32_t *num_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)) @@ -2310,17 +3021,17 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types, return false; } - num_types++; + (*num_types)++; off += size; } - if (num_types == 0) + if (*num_types == 0) { free (data); return true; } - map = xcalloc (num_types, sizeof (struct type_entry *)); + *map = xcalloc (*num_types, sizeof (struct type_entry *)); off = sizeof (uint32_t); type_num = 0; @@ -2331,11 +3042,11 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types, size = bfd_getl16 (data + off); - if (!handle_type (data + off, map, type_num, num_types, types, ids, + if (!handle_type (data + off, *map, type_num, *num_types, types, ids, mod_num, strings)) { free (data); - free (map); + free (*map); bfd_set_error (bfd_error_bad_value); return false; } @@ -2345,7 +3056,6 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types, } free (data); - free (map); return true; } @@ -2358,39 +3068,57 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size, uint32_t *c13_info_size, struct mod_source_files *mod_source, bfd *abfd, struct types *types, - struct types *ids, uint16_t mod_num) + struct types *ids, uint16_t mod_num, + bfd *sym_rec_stream, struct globals *glob) { uint8_t int_buf[sizeof (uint32_t)]; uint8_t *c13_info = NULL; + uint8_t *syms = NULL; + struct type_entry **map = NULL; + uint32_t num_types = 0; - *sym_byte_size = sizeof (uint32_t); + *sym_byte_size = 0; *c13_info_size = 0; - /* Process .debug$S section(s). */ + /* Process .debug$T section. */ for (asection *s = mod->sections; s; s = s->next) { - if (!strcmp (s->name, ".debug$S") && s->size >= sizeof (uint32_t)) + if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t)) { - if (!handle_debugs_section (s, mod, strings, &c13_info, - c13_info_size, mod_source, abfd)) + if (!handle_debugt_section (s, mod, types, ids, mod_num, strings, + &map, &num_types)) { - free (c13_info); free (mod_source->files); return false; } + + break; } - else if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t)) + } + + /* Process .debug$S section(s). */ + + for (asection *s = mod->sections; s; s = s->next) + { + if (!strcmp (s->name, ".debug$S") && s->size >= sizeof (uint32_t)) { - if (!handle_debugt_section (s, mod, types, ids, mod_num, strings)) + if (!handle_debugs_section (s, mod, strings, &c13_info, + c13_info_size, mod_source, abfd, + &syms, sym_byte_size, map, num_types, + sym_rec_stream, glob, mod_num)) { free (c13_info); + free (syms); free (mod_source->files); + free (map); return false; } } } + free (map); + /* Write the signature. */ bfd_putl32 (CV_SIGNATURE_C13, int_buf); @@ -2398,9 +3126,23 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size, if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t)) { free (c13_info); + free (syms); return false; } + if (syms) + { + if (bfd_bwrite (syms, *sym_byte_size, stream) + != *sym_byte_size) + { + free (c13_info); + free (syms); + return false; + } + + free (syms); + } + if (c13_info) { if (bfd_bwrite (c13_info, *c13_info_size, stream) @@ -2428,7 +3170,8 @@ static bool create_module_info_substream (bfd *abfd, bfd *pdb, void **data, uint32_t *size, struct string_table *strings, struct source_files_info *source, - struct types *types, struct types *ids) + struct types *types, struct types *ids, + bfd *sym_rec_stream, struct globals *glob) { uint8_t *ptr; unsigned int mod_num; @@ -2517,7 +3260,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, - types, ids, mod_num)) + types, ids, mod_num, + sym_rec_stream, glob)) { for (unsigned int i = 0; i < source->mod_count; i++) { @@ -2545,7 +3289,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data, bfd_putl16 (0, &mod->flags); bfd_putl16 (stream_num, &mod->module_sym_stream); - bfd_putl32 (sym_byte_size, &mod->sym_byte_size); + bfd_putl32 (sizeof (uint32_t) + sym_byte_size, &mod->sym_byte_size); bfd_putl32 (0, &mod->c11_byte_size); bfd_putl32 (c13_info_size, &mod->c13_byte_size); bfd_putl16 (0, &mod->source_file_count); @@ -2833,6 +3577,170 @@ create_source_info_substream (void **data, uint32_t *size, } } +/* Used as parameter to qsort, to sort globals by hash. */ +static int +global_compare_hash (const void *s1, const void *s2) +{ + const struct global *g1 = *(const struct global **) s1; + const struct global *g2 = *(const struct global **) s2; + + if (g1->hash < g2->hash) + return -1; + if (g1->hash > g2->hash) + return 1; + + return 0; +} + +/* Create the globals stream, which contains the unmangled symbol names. */ +static bool create_globals_stream (bfd *pdb, struct globals *glob, + uint16_t *stream_num) +{ + bfd *stream; + struct globals_hash_header h; + uint32_t buckets_size, filled_buckets = 0; + struct global **sorted = NULL; + bool ret = false; + struct global *buckets[NUM_GLOBALS_HASH_BUCKETS]; + char int_buf[sizeof (uint32_t)]; + + stream = add_stream (pdb, NULL, stream_num); + if (!stream) + return false; + + memset (buckets, 0, sizeof (buckets)); + + if (glob->num_entries > 0) + { + struct global *g; + + /* Create an array of pointers, sorted by hash value. */ + + sorted = xmalloc (sizeof (struct global *) * glob->num_entries); + + g = glob->first; + for (unsigned int i = 0; i < glob->num_entries; i++) + { + sorted[i] = g; + g = g->next; + } + + qsort (sorted, glob->num_entries, sizeof (struct global *), + global_compare_hash); + + /* Populate the buckets. */ + + for (unsigned int i = 0; i < glob->num_entries; i++) + { + if (!buckets[sorted[i]->hash]) + { + buckets[sorted[i]->hash] = sorted[i]; + filled_buckets++; + } + + sorted[i]->index = i; + } + } + + buckets_size = NUM_GLOBALS_HASH_BUCKETS / 8; + buckets_size += sizeof (uint32_t); + buckets_size += filled_buckets * sizeof (uint32_t); + + bfd_putl32 (GLOBALS_HASH_SIGNATURE, &h.signature); + bfd_putl32 (GLOBALS_HASH_VERSION_70, &h.version); + bfd_putl32 (glob->num_entries * sizeof (struct hash_record), + &h.entries_size); + bfd_putl32 (buckets_size, &h.buckets_size); + + if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h)) + return false; + + /* Write hash entries, sorted by hash. */ + + for (unsigned int i = 0; i < glob->num_entries; i++) + { + struct hash_record hr; + + bfd_putl32 (sorted[i]->offset + 1, &hr.offset); + bfd_putl32 (sorted[i]->refcount, &hr.reference); + + if (bfd_bwrite (&hr, sizeof (hr), stream) != sizeof (hr)) + goto end; + } + + /* Write the bitmap for filled and unfilled buckets. */ + + for (unsigned int i = 0; i < NUM_GLOBALS_HASH_BUCKETS; i += 8) + { + uint8_t v = 0; + + for (unsigned int j = 0; j < 8; j++) + { + if (buckets[i + j]) + v |= 1 << j; + } + + if (bfd_bwrite (&v, sizeof (v), stream) != sizeof (v)) + goto end; + } + + /* Add a 4-byte gap. */ + + bfd_putl32 (0, int_buf); + + if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t)) + goto end; + + /* Write the bucket offsets. */ + + for (unsigned int i = 0; i < NUM_GLOBALS_HASH_BUCKETS; i++) + { + if (buckets[i]) + { + /* 0xc is size of internal hash_record structure in + Microsoft's parser. */ + bfd_putl32 (buckets[i]->index * 0xc, int_buf); + + if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != + sizeof (uint32_t)) + goto end; + } + } + + ret = true; + +end: + free (sorted); + + return ret; +} + +/* Hash an entry in the globals list. */ +static hashval_t +hash_global_entry (const void *p) +{ + const struct global *g = (const struct global *) p; + uint16_t len = bfd_getl16 (g->data); + + return iterative_hash (g->data, len, 0); +} + +/* Compare an entry in the globals list with a symbol. */ +static int +eq_global_entry (const void *a, const void *b) +{ + const struct global *g = (const struct global *) a; + uint16_t len1, len2; + + len1 = bfd_getl16 (g->data) + sizeof (uint16_t); + len2 = bfd_getl16 (b) + sizeof (uint16_t); + + if (len1 != len2) + return 0; + + return !memcmp (g->data, b, len1); +} + /* Stream 4 is the debug information (DBI) stream. */ static bool populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb, @@ -2841,20 +3749,51 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb, uint16_t publics_stream_num, struct string_table *strings, struct types *types, - struct types *ids) + struct types *ids, + bfd *sym_rec_stream) { struct pdb_dbi_stream_header h; struct optional_dbg_header opt; void *mod_info, *sc, *source_info; uint32_t mod_info_size, sc_size, source_info_size; struct source_files_info source; + struct globals glob; + uint16_t globals_stream_num; source.mod_count = 0; source.mods = NULL; + glob.num_entries = 0; + glob.first = NULL; + glob.last = NULL; + + glob.hashmap = htab_create_alloc (0, hash_global_entry, + eq_global_entry, free, + xcalloc, free); + if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size, - strings, &source, types, ids)) - return false; + strings, &source, types, ids, + sym_rec_stream, &glob)) + { + htab_delete (glob.hashmap); + return false; + } + + if (!create_globals_stream (pdb, &glob, &globals_stream_num)) + { + htab_delete (glob.hashmap); + + for (unsigned int i = 0; i < source.mod_count; i++) + { + free (source.mods[i].files); + } + free (source.mods); + + free (mod_info); + return false; + } + + htab_delete (glob.hashmap); if (!create_section_contrib_substream (abfd, &sc, &sc_size)) { @@ -2880,7 +3819,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb, bfd_putl32 (0xffffffff, &h.version_signature); bfd_putl32 (DBI_STREAM_VERSION_70, &h.version_header); bfd_putl32 (1, &h.age); - bfd_putl16 (0xffff, &h.global_stream_index); + bfd_putl16 (globals_stream_num, &h.global_stream_index); bfd_putl16 (0x8e1d, &h.build_number); // MSVC 14.29 bfd_putl16 (publics_stream_num, &h.public_stream_index); bfd_putl16 (0, &h.pdb_dll_version); @@ -3528,7 +4467,7 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid) if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num, sym_rec_stream_num, publics_stream_num, - &strings, &types, &ids)) + &strings, &types, &ids, sym_rec_stream)) { einfo (_("%P: warning: cannot populate DBI stream " "in PDB file: %E\n")); diff --git a/ld/pdb.h b/ld/pdb.h index 668f3e168fd..3f516f8a5f8 100644 --- a/ld/pdb.h +++ b/ld/pdb.h @@ -70,7 +70,21 @@ #define LF_QUADWORD 0x8009 #define LF_UQUADWORD 0x800a +#define S_END 0x0006 +#define S_CONSTANT 0x1107 +#define S_UDT 0x1108 +#define S_LDATA32 0x110c +#define S_GDATA32 0x110d #define S_PUB32 0x110e +#define S_LPROC32 0x110f +#define S_GPROC32 0x1110 +#define S_LTHREAD32 0x1112 +#define S_GTHREAD32 0x1113 +#define S_PROCREF 0x1125 +#define S_LPROCREF 0x1127 +#define S_LPROC32_ID 0x1146 +#define S_GPROC32_ID 0x1147 +#define S_PROC_ID_END 0x114f /* PDBStream70 in pdb1.h */ struct pdb_stream_70 @@ -109,6 +123,8 @@ struct pdb_tpi_stream_header #define TPI_FIRST_INDEX 0x1000 #define NUM_TPI_HASH_BUCKETS 0x3ffff +#define NUM_GLOBALS_HASH_BUCKETS 4096 + /* NewDBIHdr in dbi.h */ struct pdb_dbi_stream_header { @@ -198,6 +214,7 @@ struct optional_dbg_header #define CV_SIGNATURE_C13 4 +#define DEBUG_S_SYMBOLS 0xf1 #define DEBUG_S_LINES 0xf2 #define DEBUG_S_STRINGTABLE 0xf3 #define DEBUG_S_FILECHKSMS 0xf4 @@ -540,6 +557,66 @@ struct lf_udt_mod_src_line uint16_t module_no; } ATTRIBUTE_PACKED; +/* DATASYM32 in cvinfo.h */ +struct datasym +{ + uint16_t size; + uint16_t kind; + uint32_t type; + uint32_t offset; + uint16_t section; + char name[]; +} ATTRIBUTE_PACKED; + +/* PROCSYM32 in cvinfo.h */ +struct procsym +{ + uint16_t size; + uint16_t kind; + uint32_t parent; + uint32_t end; + uint32_t next; + uint32_t proc_len; + uint32_t debug_start; + uint32_t debug_end; + uint32_t type; + uint32_t offset; + uint16_t section; + uint8_t flags; + char name[]; +} ATTRIBUTE_PACKED; + +/* REFSYM2 in cvinfo.h */ +struct refsym +{ + uint16_t size; + uint16_t kind; + uint32_t sum_name; + uint32_t symbol_offset; + uint16_t mod; + char name[]; +} ATTRIBUTE_PACKED; + +/* UDTSYM in cvinfo.h */ +struct udtsym +{ + uint16_t size; + uint16_t kind; + uint32_t type; + char name[]; +} ATTRIBUTE_PACKED; + +/* CONSTSYM in cvinfo.h */ +struct constsym +{ + uint16_t size; + uint16_t kind; + uint32_t type; + uint16_t value; + /* then actual value if value >= 0x8000 */ + char name[]; +} ATTRIBUTE_PACKED; + extern bool create_pdb_file (bfd *, const char *, const unsigned char *); #endif diff --git a/ld/testsuite/ld-pe/pdb-syms1-globals.d b/ld/testsuite/ld-pe/pdb-syms1-globals.d new file mode 100644 index 00000000000..356c5de22a3 --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-syms1-globals.d @@ -0,0 +1,57 @@ + +*: file format binary + +Contents of section .data: + 0000 ffffffff 1a092ff1 d0000000 64020000 ....../.....d... + 0010 fd000000 01000000 35010000 01000000 ........5....... + 0020 15000000 01000000 19010000 01000000 ................ + 0030 c1000000 01000000 b9010000 01000000 ................ + 0040 d5000000 01000000 61010000 01000000 ........a....... + 0050 e9000000 01000000 11020000 01000000 ................ + 0060 e9010000 01000000 d5010000 01000000 ................ + 0070 49010000 01000000 a5010000 01000000 I............... + 0080 99010000 01000000 85000000 01000000 ................ + 0090 01000000 01000000 99000000 01000000 ................ + 00a0 7d010000 01000000 ad000000 01000000 }............... + 00b0 5d000000 01000000 49000000 01000000 ].......I....... + 00c0 21000000 02000000 35000000 01000000 !.......5....... + 00d0 fd010000 01000000 71000000 01000000 ........q....... + 00e0 00000000 00000000 00000000 00000000 ................ + 00f0 00000000 00000000 00000000 00000000 ................ + 0100 00000000 00000000 00000000 00000000 ................ + 0110 00000000 00000000 00000000 00000000 ................ + 0120 00000000 00000000 00000000 00000000 ................ + 0130 00000000 00000000 00000000 00000000 ................ + 0140 00000000 00000000 00000000 00000000 ................ + 0150 00000002 00000000 00000000 00000000 ................ + 0160 00000000 00000000 00000000 00000000 ................ + 0170 00000000 00020000 00000000 00000000 ................ + 0180 00000000 00000000 00000000 00000000 ................ + 0190 00000000 00000001 00000000 00000000 ................ + 01a0 00000000 00000000 00000000 00000000 ................ + 01b0 00000000 00000000 00000000 00000000 ................ + 01c0 00000000 00000000 00000000 00000000 ................ + 01d0 00000000 00000000 00000000 00000000 ................ + 01e0 08001000 00000000 00000000 00000040 ...............@ + 01f0 04002000 00000000 00000000 00000000 .. ............. + 0200 00000000 00000001 00000000 00000000 ................ + 0210 00000000 00000000 00000000 00000000 ................ + 0220 00000000 00000000 00000000 00000000 ................ + 0230 00000000 00000000 00080000 00004000 ..............@. + 0240 00000000 00000000 00100010 00000000 ................ + 0250 40000000 00000000 00000000 00000000 @............... + 0260 00000000 00000800 00000000 00000000 ................ + 0270 00000008 00000000 00000000 00000000 ................ + 0280 00000000 02004000 00000000 00000000 ......@......... + 0290 00000000 00008000 00000000 00000000 ................ + 02a0 00000000 00000000 10000000 00000000 ................ + 02b0 00000000 00000000 00000000 00800000 ................ + 02c0 00000000 00000000 00002000 00010000 .......... ..... + 02d0 00000000 00000000 00000000 00000040 ...............@ + 02e0 00000000 00000000 0c000000 18000000 ................ + 02f0 24000000 30000000 3c000000 48000000 $...0...<...H... + 0300 54000000 60000000 6c000000 78000000 T...`...l...x... + 0310 84000000 90000000 9c000000 a8000000 ................ + 0320 b4000000 c0000000 d8000000 e4000000 ................ + 0330 f0000000 fc000000 08010000 20010000 ............ ... + 0340 2c010000 ,... \ No newline at end of file diff --git a/ld/testsuite/ld-pe/pdb-syms1-records.d b/ld/testsuite/ld-pe/pdb-syms1-records.d new file mode 100644 index 00000000000..bbf6d7f158e --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-syms1-records.d @@ -0,0 +1,61 @@ + +*: file format binary + +Contents of section .data: + 0000 12002511 00000000 04000000 01007072 ..%...........pr + 0010 6f633200 0a000811 04100000 62617200 oc2.........bar. + 0020 12000c11 00100000 08000000 02006c76 ..............lv + 0030 61723100 12000c11 00100000 0c000000 ar1............. + 0040 02006c76 61723100 12000c11 00100000 ..lvar1......... + 0050 00000000 03006c76 61723200 12000d11 ......lvar2..... + 0060 00100000 18000000 02006776 61723100 ..........gvar1. + 0070 12000d11 00100000 04000000 03006776 ..............gv + 0080 61723200 12002511 00000000 54000000 ar2...%.....T... + 0090 02007072 6f633100 12002511 00000000 ..proc1...%..... + 00a0 88000000 02007072 6f633200 12002711 ......proc2...'. + 00b0 00000000 f0000000 02007072 6f633300 ..........proc3. + 00c0 12002711 00000000 24010000 02007072 ..'.....$.....pr + 00d0 6f633400 12002511 00000000 58010000 oc4...%.....X... + 00e0 02007072 6f633500 12002511 00000000 ..proc5...%..... + 00f0 8c010000 02007072 6f633600 1a002511 ......proc6...%. + 0100 00000000 c0010000 0200666f 6f3a3a6d ..........foo::m + 0110 6574686f 64000000 1a002511 00000000 ethod.....%..... + 0120 f8010000 0200666f 6f3a3a6d 6574686f ......foo::metho + 0130 64320000 12002711 00000000 30020000 d2....'.....0... + 0140 02007072 6f633900 16002511 00000000 ..proc9...%..... + 0150 64020000 02007072 6f633130 00000000 d.....proc10.... + 0160 1a002711 00000000 98020000 0200666f ..'...........fo + 0170 6f3a3a6d 6574686f 64330000 1a002711 o::method3....'. + 0180 00000000 d0020000 0200666f 6f3a3a6d ..........foo::m + 0190 6574686f 64340000 0a000811 0a100000 ethod4.......... + 01a0 666f6f00 12000711 75000000 2a00616e foo.....u...*.an + 01b0 73776572 00f3f2f1 1a000711 23000000 swer........#... + 01c0 0a80efcd ab896745 2301616e 73776572 ......gE#.answer + 01d0 3200f2f1 12001211 00100000 20000000 2........... ... + 01e0 02006c76 61723500 12001211 00100000 ..lvar5......... + 01f0 12000000 03006c76 61723600 12001311 ......lvar6..... + 0200 00100000 1c000000 02006776 61723300 ..........gvar3. + 0210 12001311 00100000 08000000 03006776 ..............gv + 0220 61723400 12000e11 00000000 18000000 ar4............. + 0230 02006776 61723100 12000e11 00000000 ..gvar1......... + 0240 04000000 03006776 61723200 12000e11 ......gvar2..... + 0250 00000000 0c000000 03007072 6f633100 ..........proc1. + 0260 12000e11 02000000 06000000 01007072 ..............pr + 0270 6f633200 12000e11 00000000 0d000000 oc2............. + 0280 03007072 6f633300 12000e11 02000000 ..proc3......... + 0290 07000000 01007072 6f633400 12000e11 ......proc4..... + 02a0 00000000 0e000000 03007072 6f633500 ..........proc5. + 02b0 12000e11 02000000 08000000 01007072 ..............pr + 02c0 6f633600 12000e11 00000000 0f000000 oc6............. + 02d0 03007072 6f633700 12000e11 02000000 ..proc7......... + 02e0 09000000 01007072 6f633800 12000e11 ......proc8..... + 02f0 00000000 10000000 03007072 6f633900 ..........proc9. + 0300 16000e11 02000000 0a000000 01007072 ..............pr + 0310 6f633130 00000000 16000e11 00000000 oc10............ + 0320 11000000 03007072 6f633131 00000000 ......proc11.... + 0330 16000e11 02000000 0b000000 01007072 ..............pr + 0340 6f633132 00000000 12000e11 00000000 oc12............ + 0350 1c000000 02006776 61723300 12000e11 ......gvar3..... + 0360 00000000 08000000 03006776 61723400 ..........gvar4. + 0370 12000e11 02000000 00000000 01006d61 ..............ma + 0380 696e0000 in.. \ No newline at end of file diff --git a/ld/testsuite/ld-pe/pdb-syms1-symbols1.d b/ld/testsuite/ld-pe/pdb-syms1-symbols1.d new file mode 100644 index 00000000000..4de22acbd08 --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-syms1-symbols1.d @@ -0,0 +1,8 @@ + +*: file format binary + +Contents of section .data: + 0000 04000000 2e001011 00000000 34000000 ............4... + 0010 00000000 01000000 00000000 00000000 ................ + 0020 02100000 06000000 01000070 726f6332 ...........proc2 + 0030 00f3f2f1 02000600 00000000 ............ \ No newline at end of file diff --git a/ld/testsuite/ld-pe/pdb-syms1-symbols2.d b/ld/testsuite/ld-pe/pdb-syms1-symbols2.d new file mode 100644 index 00000000000..f134637a744 --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-syms1-symbols2.d @@ -0,0 +1,56 @@ + +*: file format binary + +Contents of section .data: + 0000 04000000 12000c11 00100000 08000000 ................ + 0010 02006c76 61723100 12000c11 00100000 ..lvar1......... + 0020 08000000 02006c76 61723100 12000c11 ......lvar1..... + 0030 00100000 0c000000 02006c76 61723100 ..........lvar1. + 0040 12000c11 00100000 00000000 03006c76 ..............lv + 0050 61723200 2e001011 00000000 84000000 ar2............. + 0060 00000000 01000000 00000000 00000000 ................ + 0070 05100000 0c000000 03000070 726f6331 ...........proc1 + 0080 00f3f2f1 02000600 2e001011 00000000 ................ + 0090 ec000000 00000000 01000000 00000000 ................ + 00a0 00000000 05100000 06000000 01000070 ...............p + 00b0 726f6332 00f3f2f1 0a000811 04100000 roc2............ + 00c0 62617200 12000c11 00100000 10000000 bar............. + 00d0 02006c76 61723300 12001211 00100000 ..lvar3......... + 00e0 14000000 02006c76 61723400 02000600 ......lvar4..... + 00f0 2e000f11 00000000 20010000 00000000 ........ ....... + 0100 01000000 00000000 00000000 05100000 ................ + 0110 0d000000 03000070 726f6333 00f3f2f1 .......proc3.... + 0120 02000600 2e000f11 00000000 54010000 ............T... + 0130 00000000 01000000 00000000 00000000 ................ + 0140 05100000 07000000 01000070 726f6334 ...........proc4 + 0150 00f3f2f1 02000600 2e001011 00000000 ................ + 0160 88010000 00000000 01000000 00000000 ................ + 0170 00000000 05100000 0e000000 03000070 ...............p + 0180 726f6335 00f3f2f1 02000600 2e001011 roc5............ + 0190 00000000 bc010000 00000000 01000000 ................ + 01a0 00000000 00000000 05100000 08000000 ................ + 01b0 01000070 726f6336 00f3f2f1 02000600 ...proc6........ + 01c0 32001011 00000000 f4010000 00000000 2............... + 01d0 01000000 00000000 00000000 05100000 ................ + 01e0 0f000000 03000066 6f6f3a3a 6d657468 .......foo::meth + 01f0 6f6400f1 02000600 32001011 00000000 od......2....... + 0200 2c020000 00000000 01000000 00000000 ,............... + 0210 00000000 05100000 09000000 01000066 ...............f + 0220 6f6f3a3a 6d657468 6f643200 02000600 oo::method2..... + 0230 2e000f11 00000000 60020000 00000000 ........`....... + 0240 01000000 00000000 00000000 05100000 ................ + 0250 10000000 03000070 726f6339 00f3f2f1 .......proc9.... + 0260 02000600 2e001011 00000000 94020000 ................ + 0270 00000000 01000000 00000000 00000000 ................ + 0280 05100000 0a000000 01000070 726f6331 ...........proc1 + 0290 3000f2f1 02000600 32000f11 00000000 0.......2....... + 02a0 cc020000 00000000 01000000 00000000 ................ + 02b0 00000000 05100000 11000000 03000066 ...............f + 02c0 6f6f3a3a 6d657468 6f643300 02000600 oo::method3..... + 02d0 32000f11 00000000 04030000 00000000 2............... + 02e0 01000000 00000000 00000000 05100000 ................ + 02f0 0b000000 01000066 6f6f3a3a 6d657468 .......foo::meth + 0300 6f643400 02000600 12001211 00100000 od4............. + 0310 20000000 02006c76 61723500 12001211 .....lvar5..... + 0320 00100000 12000000 03006c76 61723600 ..........lvar6. + 0330 00000000 .... \ No newline at end of file diff --git a/ld/testsuite/ld-pe/pdb-syms1a.s b/ld/testsuite/ld-pe/pdb-syms1a.s new file mode 100644 index 00000000000..c1929c3ec85 --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-syms1a.s @@ -0,0 +1,110 @@ +.equ CV_SIGNATURE_C13, 4 +.equ DEBUG_S_SYMBOLS, 0xf1 + +.equ T_UINT4, 0x0075 + +.equ LF_MODIFIER, 0x1001 +.equ LF_PROCEDURE, 0x1008 +.equ LF_ARGLIST, 0x1201 +.equ LF_FIELDLIST, 0x1203 +.equ LF_STRUCTURE, 0x1505 +.equ LF_MEMBER, 0x150d + +.equ S_END, 0x0006 +.equ S_UDT, 0x1108 +.equ S_GPROC32, 0x1110 + +.section ".debug$S", "rn" + +.long CV_SIGNATURE_C13 + +.long DEBUG_S_SYMBOLS +.long .syms_end - .syms_start + +.syms_start: + +.gproc2: +.short .gproc2_end - .gproc2 - 2 +.short S_GPROC32 +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long 1 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x1002 # type +.secrel32 proc2 +.secidx proc2 +.byte 0 # flags +.asciz "proc2" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +.gproc2_end: +.short .udt1 - .gproc2_end - 2 +.short S_END + +.udt1: +.short .syms_end - .udt1 - 2 +.short S_UDT +.long 0x1004 # struct bar +.asciz "bar" + +.syms_end: + +.section ".debug$T", "rn" + +.long CV_SIGNATURE_C13 + +# Type 1000, const uint32_t +.mod1: +.short .arglist1 - .mod1 - 2 +.short LF_MODIFIER +.long T_UINT4 +.short 1 # const +.p2align 2 + +# Type 1001, arglist (uint32_t) +.arglist1: +.short .proctype1 - .arglist1 - 2 +.short LF_ARGLIST +.long 1 # no. entries +.long T_UINT4 + +# Type 1002, procedure (return type T_VOID, arglist 1001) +.proctype1: +.short .fieldlist1 - .proctype1 - 2 +.short LF_PROCEDURE +.long T_VOID +.byte 0 # calling convention +.byte 0 # attributes +.short 1 # no. parameters +.long 0x1001 + +# Type 1003, field list for struct bar +.fieldlist1: +.short .struct1 - .fieldlist1 - 2 +.short LF_FIELDLIST +.short LF_MEMBER +.short 3 # public +.long T_UINT4 +.short 0 # offset +.asciz "num1" +.byte 0xf1 # padding + +# Type 1004, declaration of struct bar +.struct1: +.short .types_end - .struct1 - 2 +.short LF_STRUCTURE +.short 1 # no. members +.short 0 # property +.long 0x1003 # field list +.long 0 # type derived from +.long 0 # type of vshape table +.short 4 # size +.asciz "bar" # name +.byte 0xf2 # padding +.byte 0xf1 # padding + +.types_end: diff --git a/ld/testsuite/ld-pe/pdb-syms1b.s b/ld/testsuite/ld-pe/pdb-syms1b.s new file mode 100644 index 00000000000..ddc471104b5 --- /dev/null +++ b/ld/testsuite/ld-pe/pdb-syms1b.s @@ -0,0 +1,737 @@ +.equ CV_SIGNATURE_C13, 4 +.equ DEBUG_S_SYMBOLS, 0xf1 + +.equ T_VOID, 0x0003 +.equ T_UQUAD, 0x0023 +.equ T_UINT4, 0x0075 + +.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_CLASS, 0x1504 +.equ LF_STRUCTURE, 0x1505 +.equ LF_MEMBER, 0x150d +.equ LF_ONEMETHOD, 0x1511 +.equ LF_FUNC_ID, 0x1601 +.equ LF_MFUNC_ID, 0x1602 + +.equ LF_UQUADWORD, 0x800a + +.equ S_END, 0x0006 +.equ S_CONSTANT, 0x1107 +.equ S_UDT, 0x1108 +.equ S_LDATA32, 0x110c +.equ S_GDATA32, 0x110d +.equ S_LPROC32, 0x110f +.equ S_GPROC32, 0x1110 +.equ S_LTHREAD32, 0x1112 +.equ S_GTHREAD32, 0x1113 +.equ S_LPROC32_ID, 0x1146 +.equ S_GPROC32_ID, 0x1147 +.equ S_PROC_ID_END, 0x114f + +.equ CV_PTR_64, 0xc + +.section ".debug$S", "rn" + +.long CV_SIGNATURE_C13 + +.long DEBUG_S_SYMBOLS +.long .syms_end - .syms_start + +.syms_start: + +.ldata1: +.short .ldata1a - .ldata1 - 2 +.short S_LDATA32 +.long 0x1000 # const uint32_t +.secrel32 lvar1 +.secidx lvar1 +.asciz "lvar1" + +.ldata1a: # duplicate with same address +.short .ldata1b - .ldata1a - 2 +.short S_LDATA32 +.long 0x1000 # const uint32_t +.secrel32 lvar1 +.secidx lvar1 +.asciz "lvar1" + +.ldata1b: # duplicate with different address +.short .ldata2 - .ldata1b - 2 +.short S_LDATA32 +.long 0x1000 # const uint32_t +.secrel32 lvar1a +.secidx lvar1a +.asciz "lvar1" + +.ldata2: +.short .gdata1 - .ldata2 - 2 +.short S_LDATA32 +.long 0x1000 # const uint32_t +.secrel32 lvar2 +.secidx lvar2 +.asciz "lvar2" + +.gdata1: +.short .gdata2 - .gdata1 - 2 +.short S_GDATA32 +.long 0x1000 # const uint32_t +.secrel32 gvar1 +.secidx gvar1 +.asciz "gvar1" + +.gdata2: +.short .gproc1 - .gdata2 - 2 +.short S_GDATA32 +.long 0x1000 # const uint32_t +.secrel32 gvar2 +.secidx gvar2 +.asciz "gvar2" + +.gproc1: +.short .gproc1_end - .gproc1 - 2 +.short S_GPROC32 +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc1_end - proc1 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x1002 # type +.secrel32 proc1 +.secidx proc1 +.byte 0 # flags +.asciz "proc1" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +.gproc1_end: +.short .gproc2 - .gproc1_end - 2 +.short S_END + +.gproc2: +.short .udt1 - .gproc2 - 2 +.short S_GPROC32 +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc2_end - proc2 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x1002 # type +.secrel32 proc2 +.secidx proc2 +.byte 0 # flags +.asciz "proc2" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +.udt1: +.short .ldata3 - .udt1 - 2 +.short S_UDT +.long 0x1011 # struct bar +.asciz "bar" + +.ldata3: +.short .lthread1 - .ldata3 - 2 +.short S_LDATA32 +.long 0x1000 # const uint32_t +.secrel32 lvar3 +.secidx lvar3 +.asciz "lvar3" + +.lthread1: +.short .gproc2_end - .lthread1 - 2 +.short S_LTHREAD32 +.long 0x1000 # const uint32_t +.secrel32 lvar4 +.secidx lvar4 +.asciz "lvar4" + +.gproc2_end: +.short .gproc3 - .gproc2_end - 2 +.short S_END + +.gproc3: +.short .gproc3_end - .gproc3 - 2 +.short S_LPROC32 +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc3_end - proc3 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x1002 # type +.secrel32 proc3 +.secidx proc3 +.byte 0 # flags +.asciz "proc3" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +.gproc3_end: +.short .gproc4 - .gproc3_end - 2 +.short S_END + +.gproc4: +.short .gproc4_end - .gproc4 - 2 +.short S_LPROC32 +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc4_end - proc4 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x1002 # type +.secrel32 proc4 +.secidx proc4 +.byte 0 # flags +.asciz "proc4" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +.gproc4_end: +.short .gproc5 - .gproc4_end - 2 +.short S_END + +.gproc5: +.short .gproc5_end - .gproc5 - 2 +.short S_GPROC32_ID +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc5_end - proc5 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x1003 # func ID +.secrel32 proc5 +.secidx proc5 +.byte 0 # flags +.asciz "proc5" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +.gproc5_end: +.short .gproc6 - .gproc5_end - 2 +.short S_PROC_ID_END + +.gproc6: +.short .gproc6_end - .gproc6 - 2 +.short S_GPROC32_ID +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc6_end - proc6 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x1004 # func ID +.secrel32 proc6 +.secidx proc6 +.byte 0 # flags +.asciz "proc6" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +.gproc6_end: +.short .gproc7 - .gproc6_end - 2 +.short S_PROC_ID_END + +.gproc7: +.short .gproc7_end - .gproc7 - 2 +.short S_GPROC32_ID +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc7_end - proc7 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x100a # func ID +.secrel32 proc7 +.secidx proc7 +.byte 0 # flags +.asciz "foo::method" +.byte 0xf1 # padding + +.gproc7_end: +.short .gproc8 - .gproc7_end - 2 +.short S_PROC_ID_END + +.gproc8: +.short .gproc8_end - .gproc8 - 2 +.short S_GPROC32_ID +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc8_end - proc8 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x100b # func ID +.secrel32 proc8 +.secidx proc8 +.byte 0 # flags +.asciz "foo::method2" + +.gproc8_end: +.short .gproc9 - .gproc8_end - 2 +.short S_PROC_ID_END + +.gproc9: +.short .gproc9_end - .gproc9 - 2 +.short S_LPROC32_ID +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc9_end - proc9 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x100c # func ID +.secrel32 proc9 +.secidx proc9 +.byte 0 # flags +.asciz "proc9" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +.gproc9_end: +.short .gproc10 - .gproc9_end - 2 +.short S_PROC_ID_END + +.gproc10: +.short .gproc10_end - .gproc10 - 2 +.short S_GPROC32_ID +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc10_end - proc10 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x100d # func ID +.secrel32 proc10 +.secidx proc10 +.byte 0 # flags +.asciz "proc10" +.byte 0xf2 # padding +.byte 0xf1 # padding + +.gproc10_end: +.short .gproc11 - .gproc10_end - 2 +.short S_PROC_ID_END + +.gproc11: +.short .gproc11_end - .gproc11 - 2 +.short S_LPROC32_ID +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc11_end - proc11 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x100e # func ID +.secrel32 proc11 +.secidx proc11 +.byte 0 # flags +.asciz "foo::method3" + +.gproc11_end: +.short .gproc12 - .gproc11_end - 2 +.short S_PROC_ID_END + +.gproc12: +.short .gproc12_end - .gproc12 - 2 +.short S_LPROC32_ID +.long 0 # parent +.long 0 # end +.long 0 # next symbol +.long .proc12_end - proc12 # length +.long 0 # debug start offset +.long 0 # debug end offset +.long 0x100f # func ID +.secrel32 proc12 +.secidx proc12 +.byte 0 # flags +.asciz "foo::method4" + +.gproc12_end: +.short .udt2 - .gproc12_end - 2 +.short S_PROC_ID_END + +.udt2: +.short .constant1 - .udt2 - 2 +.short S_UDT +.long 0x1009 # class foo +.asciz "foo" + +.constant1: +.short .constant2 - .constant1 - 2 +.short S_CONSTANT +.long T_UINT4 +.short 42 +.asciz "answer" +.byte 0xf3 # padding +.byte 0xf2 # padding +.byte 0xf1 # padding + +.constant2: +.short .lthread2 - .constant2 - 2 +.short S_CONSTANT +.long T_UQUAD +.short LF_UQUADWORD +.quad 0x0123456789abcdef +.asciz "answer2" +.byte 0xf2 # padding +.byte 0xf1 # padding + +.lthread2: +.short .lthread3 - .lthread2 - 2 +.short S_LTHREAD32 +.long 0x1000 # const uint32_t +.secrel32 lvar5 +.secidx lvar5 +.asciz "lvar5" + +.lthread3: +.short .gthread1 - .lthread3 - 2 +.short S_LTHREAD32 +.long 0x1000 # const uint32_t +.secrel32 lvar6 +.secidx lvar6 +.asciz "lvar6" + +.gthread1: +.short .gthread2 - .gthread1 - 2 +.short S_GTHREAD32 +.long 0x1000 # const uint32_t +.secrel32 gvar3 +.secidx gvar3 +.asciz "gvar3" + +.gthread2: +.short .syms_end - .gthread2 - 2 +.short S_GTHREAD32 +.long 0x1000 # const uint32_t +.secrel32 gvar4 +.secidx gvar4 +.asciz "gvar4" + +.p2align 2 +.syms_end: + +.section ".debug$T", "rn" + +.long CV_SIGNATURE_C13 + +# Type 1000, const uint32_t +.mod1: +.short .arglist1 - .mod1 - 2 +.short LF_MODIFIER +.long T_UINT4 +.short 1 # const +.p2align 2 + +# Type 1001, arglist (uint32_t) +.arglist1: +.short .proctype1 - .arglist1 - 2 +.short LF_ARGLIST +.long 1 # no. entries +.long T_UINT4 + +# Type 1002, procedure (return type T_VOID, arglist 1001) +.proctype1: +.short .funcid1 - .proctype1 - 2 +.short LF_PROCEDURE +.long T_VOID +.byte 0 # calling convention +.byte 0 # attributes +.short 1 # no. parameters +.long 0x1001 + +# Type 1003, func ID for proc5 +.funcid1: +.short .funcid2 - .funcid1 - 2 +.short LF_FUNC_ID +.long 0 # parent scope +.long 0x1002 # type +.asciz "proc5" +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1004, func ID for proc6 +.funcid2: +.short .class1 - .funcid2 - 2 +.short LF_FUNC_ID +.long 0 # parent scope +.long 0x1002 # type +.asciz "proc6" +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1005, forward declaration of class foo +.class1: +.short .ptr1 - .class1 - 2 +.short LF_CLASS +.short 0 # no. members +.short 0x80 # property (forward declaration) +.long 0 # field list +.long 0 # type derived from +.long 0 # type of vshape table +.short 0 # size +.asciz "foo" # name +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 1006, pointer to 1005 +.ptr1: +.short .mfunction1 - .ptr1 - 2 +.short LF_POINTER +.long 0x1005 +.long (8 << 13) | CV_PTR_64 + +# Type 1007, member function of 1005, return type void, arg list 1001 +.mfunction1: +.short .fieldlist1 - .mfunction1 - 2 +.short LF_MFUNCTION +.long T_VOID +.long 0x1005 +.long 0x1006 # type of "this" pointer +.byte 0 # calling convention +.byte 0 # attributes +.short 1 # no. parameters +.long 0x1001 # arg list +.long 0 # "this" adjustment + +# Type 1008, field list for class foo +.fieldlist1: +.short .class2 - .fieldlist1 - 2 +.short LF_FIELDLIST +.short LF_ONEMETHOD +.short 0 # method attribute +.long 0x1007 # method type +.asciz "method" +.byte 0xf1 # padding +.short LF_ONEMETHOD +.short 0 # method attribute +.long 0x1007 # method type +.asciz "method2" +.short LF_ONEMETHOD +.short 0 # method attribute +.long 0x1007 # method type +.asciz "method3" +.short LF_ONEMETHOD +.short 0 # method attribute +.long 0x1007 # method type +.asciz "method4" + +# Type 1009, actual declaration of class foo +.class2: +.short .mfunc1 - .class2 - 2 +.short LF_CLASS +.short 0 # no. members +.short 0 # property +.long 0x1008 # field list +.long 0 # type derived from +.long 0 # type of vshape table +.short 0 # size +.asciz "foo" # name +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 100a, function "method" within class "foo" +.mfunc1: +.short .mfunc2 - .mfunc1 - 2 +.short LF_MFUNC_ID +.long 0x1009 # parent class +.long 0x1002 # function type +.asciz "method" +.byte 0xf1 # padding + +# Type 100b, function "method2" within class "foo" +.mfunc2: +.short .funcid3 - .mfunc2 - 2 +.short LF_MFUNC_ID +.long 0x1009 # parent class +.long 0x1002 # function type +.asciz "method2" + +# Type 100c, func ID for proc9 +.funcid3: +.short .funcid4 - .funcid3 - 2 +.short LF_FUNC_ID +.long 0 # parent scope +.long 0x1002 # type +.asciz "proc9" +.byte 0xf2 # padding +.byte 0xf1 # padding + +# Type 100d, func ID for proc10 +.funcid4: +.short .mfunc3 - .funcid4 - 2 +.short LF_FUNC_ID +.long 0 # parent scope +.long 0x1002 # type +.asciz "proc10" +.byte 0xf1 # padding + +# Type 100e, function "method3" within class "foo" +.mfunc3: +.short .mfunc4 - .mfunc3 - 2 +.short LF_MFUNC_ID +.long 0x1009 # parent class +.long 0x1002 # function type +.asciz "method3" + +# Type 100f, function "method4" within class "foo" +.mfunc4: +.short .fieldlist2 - .mfunc4 - 2 +.short LF_MFUNC_ID +.long 0x1009 # parent class +.long 0x1002 # function type +.asciz "method4" + +# Type 1010, field list for struct bar +.fieldlist2: +.short .struct1 - .fieldlist2 - 2 +.short LF_FIELDLIST +.short LF_MEMBER +.short 3 # public +.long T_UINT4 +.short 0 # offset +.asciz "num1" +.byte 0xf1 # padding + +# Type 1011, declaration of struct bar +.struct1: +.short .types_end - .struct1 - 2 +.short LF_STRUCTURE +.short 1 # no. members +.short 0 # property +.long 0x1010 # field list +.long 0 # type derived from +.long 0 # type of vshape table +.short 4 # size +.asciz "bar" # name +.byte 0xf2 # padding +.byte 0xf1 # padding + +.types_end: + +.data + +.long 0x12345678 +.long 0x12345678 + +lvar1: +.long 42 + +lvar1a: +.long 0x12345678 + +lvar3: +.long 84 + +lvar4: +.long 85 + +.global gvar1 +gvar1: +.long 43 + +.global gvar3 +gvar3: +.long 41 + +lvar5: +.long 86 + +.text + +.global main +main: + jmp main + .secrel32 .data + +.global proc2 +proc2: + nop +.proc2_end: + +.global proc4 +proc4: + nop +.proc4_end: + +.global proc6 +proc6: + nop +.proc6_end: + +.global proc8 +proc8: + nop +.proc8_end: + +.global proc10 +proc10: + nop +.proc10_end: + +.global proc12 +proc12: + nop +.proc12_end: + +.section "gcsect" + +lvar2: +.long 84 + +.global gvar2 +gvar2: +.long 85 + +.global gvar4 +gvar4: +.long 86 + +.global proc1 +proc1: + nop +.proc1_end: + +.global proc3 +proc3: + nop +.proc3_end: + +.global proc5 +proc5: + nop +.proc5_end: + +.global proc7 +proc7: + nop +.proc7_end: + +.global proc9 +proc9: + nop +.proc9_end: + +.global proc11 +proc11: + nop +.proc11_end: + +lvar6: +.long 86 diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp index 1a5416890d1..34eafc142a7 100644 --- a/ld/testsuite/ld-pe/pdb.exp +++ b/ld/testsuite/ld-pe/pdb.exp @@ -1451,6 +1451,169 @@ proc test7 { } { } } +proc test8 { } { + global as + global ar + global ld + global objdump + global srcdir + global subdir + + if ![ld_assemble $as $srcdir/$subdir/pdb-syms1a.s tmpdir/pdb-syms1a.o] { + unsupported "Build pdb-syms1a.o" + return + } + + if ![ld_assemble $as $srcdir/$subdir/pdb-syms1b.s tmpdir/pdb-syms1b.o] { + unsupported "Build pdb-syms1b.o" + return + } + + if ![ld_link $ld "tmpdir/pdb-syms1.exe" "--pdb=tmpdir/pdb-syms1.pdb tmpdir/pdb-syms1a.o tmpdir/pdb-syms1b.o"] { + unsupported "Create PE image with PDB file" + return + } + + # get index of globals stream and records stream + + set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb 0003"] + + if ![string match "" $exec_output] { + fail "Could not extract DBI stream" + return + } else { + pass "Extracted DBI stream" + } + + set fi [open tmpdir/0003] + fconfigure $fi -translation binary + + seek $fi 12 + set data [read $fi 2] + binary scan $data s globals_index + + seek $fi 6 current + set data [read $fi 2] + binary scan $data s records_index + + seek $fi 2 current + set data [read $fi 4] + binary scan $data i mod_info_size + + seek $fi 36 current + set mod_info [read $fi $mod_info_size] + + close $fi + + # get index of first and second module streams + + binary scan [string range $mod_info 34 35] s mod1_index + + set off 64 + + set obj1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] + incr off [expr [string length $obj1] + 1] + + set ar1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]] + incr off [expr [string length $ar1] + 1] + + if { [expr $off % 4] != 0 } { + set off [expr $off + 4 - ($off % 4)] + } + + incr off 34 + + binary scan [string range $mod_info $off [expr $off + 1]] s mod2_index + + # check globals stream + + set index_str [format "%04x" $globals_index] + + set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"] + + if ![string match "" $exec_output] { + fail "Could not extract globals stream" + return + } else { + pass "Extracted globals stream" + } + + set exp [file_contents "$srcdir/$subdir/pdb-syms1-globals.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] + + if [string match $exp $got] { + pass "Correct globals stream" + } else { + fail "Incorrect globals stream" + } + + # check records stream + + set index_str [format "%04x" $records_index] + + set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"] + + if ![string match "" $exec_output] { + fail "Could not extract records stream" + return + } else { + pass "Extracted records stream" + } + + set exp [file_contents "$srcdir/$subdir/pdb-syms1-records.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] + + if [string match $exp $got] { + pass "Correct records stream" + } else { + fail "Incorrect records stream" + } + + # check symbols in first module + + set index_str [format "%04x" $mod1_index] + + set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"] + + if ![string match "" $exec_output] { + fail "Could not extract first module's symbols" + return + } else { + pass "Extracted first module's symbols" + } + + set exp [file_contents "$srcdir/$subdir/pdb-syms1-symbols1.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] + + if [string match $exp $got] { + pass "Correct symbols in first module's stream" + } else { + fail "Incorrect symbols in first module's stream" + } + + # check symbols in second module + + set index_str [format "%04x" $mod2_index] + + set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"] + + if ![string match "" $exec_output] { + fail "Could not extract second module's symbols" + return + } else { + pass "Extracted second module's symbols" + } + + set exp [file_contents "$srcdir/$subdir/pdb-syms1-symbols2.d"] + set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"] + + if [string match $exp $got] { + pass "Correct symbols in second module's stream" + } else { + fail "Incorrect symbols in second module's stream" + } +} + test1 test2 test3 @@ -1458,3 +1621,4 @@ test4 test5 test6 test7 +test8 -- 2.37.4