From: Mark Harmstone Date: Fri, 9 Dec 2022 01:52:38 +0000 (+0000) Subject: ld: Write globals stream in PDB X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=81814b6f5bd7be2ef719181286b375dcdd8a0f00;p=binutils-gdb.git ld: Write globals stream in PDB --- diff --git a/ld/pdb.c b/ld/pdb.c index 6149b49dd47..375ff35d04b 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, @@ -757,67 +775,688 @@ hash_string_table_entry (const void *p) { const struct string *s = (const struct string *) p; - return s->hash; -} + 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); -/* 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 (section != 0) + { + *sym_size += len; + } + else + { + uint8_t *endptr = find_end_of_scope (data, size); - if (s2_len != s1->len) - return 0; + if (!endptr) + { + einfo (_("%P: warning: could not find end of" + " S_GPROC32/S_LPROC32 record\n")); + return false; + } - return memcmp (s1->s, s2, s2_len) == 0; -} + /* Skip to after S_END. */ -/* 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); + size -= endptr - data; + data = endptr; - add_string ((char *) data, len, strings); + len = bfd_getl16 (data) + sizeof (uint16_t); - data += len + 1; + data += len; + size -= len; - if (size <= len + 1) - break; + continue; + } - size -= len + 1; - } -} + scope_level++; -/* 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; + break; + } - case LF_SHORT: - case LF_USHORT: - return 2; + case S_UDT: + if (scope_level != 0) /* only goes in symbols if local */ + *sym_size += len; + break; - case LF_LONG: - case LF_ULONG: - return 4; + case S_END: /* always copied */ + case S_PROC_ID_END: + *sym_size += len; + scope_level--; + break; - case LF_QUADWORD: - case LF_UQUADWORD: - return 8; + default: + einfo (_("%P: warning: unrecognized CodeView record %v\n"), type); + return false; + } + + data += len; + size -= len; } - return 0; + return true; } /* Parse the .debug$S section within an object file. */ @@ -825,13 +1464,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; @@ -970,6 +1613,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; @@ -978,7 +1631,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; @@ -986,9 +1642,16 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, /* copy data */ - buf = xmalloc (c13_size); + buf = NULL; + if (c13_size != 0) + buf = xmalloc (c13_size); bufptr = buf; + symbuf = NULL; + if (sym_size != 0) + symbuf = xmalloc (sym_size); + symbufptr = symbuf; + off = sizeof (uint32_t); while (off + sizeof (uint32_t) <= s->size) @@ -1008,6 +1671,7 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings, strings, bufptr, mod_source)) { free (data); + free (symbuf); return false; } @@ -1036,6 +1700,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; @@ -1046,22 +1721,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; } @@ -2280,12 +2975,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)) @@ -2316,17 +3010,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; @@ -2337,11 +3031,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; } @@ -2351,7 +3045,6 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types, } free (data); - free (map); return true; } @@ -2364,39 +3057,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); @@ -2404,9 +3115,22 @@ 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) != *c13_info_size) @@ -2433,7 +3157,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; @@ -2522,7 +3247,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++) { @@ -2550,7 +3276,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); @@ -2838,6 +3564,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, @@ -2846,20 +3736,50 @@ 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)) { @@ -2884,7 +3804,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); @@ -3531,7 +4451,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..9048b5fa37c 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