ld: Generate PDB string table

This commit is contained in:
Mark Harmstone
2022-12-09 01:52:31 +00:00
committed by Alan Modra
parent f27d07a1c2
commit f559276dc5
6 changed files with 472 additions and 6 deletions

296
ld/pdb.c
View File

@ -41,6 +41,23 @@ struct public
uint32_t address;
};
struct string
{
struct string *next;
uint32_t hash;
uint32_t offset;
size_t len;
char s[];
};
struct string_table
{
struct string *strings_head;
struct string *strings_tail;
uint32_t strings_len;
htab_t hashmap;
};
/* Add a new stream to the PDB archive, and return its BFD. */
static bfd *
add_stream (bfd *pdb, const char *name, uint16_t *stream_num)
@ -383,15 +400,170 @@ get_arch_number (bfd *abfd)
return IMAGE_FILE_MACHINE_I386;
}
/* Add a string to the strings table, if it's not already there. */
static void
add_string (char *str, size_t len, struct string_table *strings)
{
uint32_t hash = calc_hash (str, len);
void **slot;
slot = htab_find_slot_with_hash (strings->hashmap, str, hash, INSERT);
if (!*slot)
{
struct string *s;
*slot = xmalloc (offsetof (struct string, s) + len);
s = (struct string *) *slot;
s->next = NULL;
s->hash = hash;
s->offset = strings->strings_len;
s->len = len;
memcpy (s->s, str, len);
if (strings->strings_tail)
strings->strings_tail->next = s;
else
strings->strings_head = s;
strings->strings_tail = s;
strings->strings_len += len + 1;
}
}
/* 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;
}
}
/* Parse the .debug$S section within an object file. */
static bool
handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
{
bfd_byte *data = NULL;
size_t off;
if (!bfd_get_full_section_contents (mod, s, &data))
return false;
if (!data)
return false;
if (bfd_getl32 (data) != CV_SIGNATURE_C13)
{
free (data);
return true;
}
off = sizeof (uint32_t);
while (off + sizeof (uint32_t) <= s->size)
{
uint32_t type, size;
type = bfd_getl32 (data + off);
off += sizeof (uint32_t);
if (off + sizeof (uint32_t) > s->size)
{
free (data);
bfd_set_error (bfd_error_bad_value);
return false;
}
size = bfd_getl32 (data + off);
off += sizeof (uint32_t);
if (off + size > s->size)
{
free (data);
bfd_set_error (bfd_error_bad_value);
return false;
}
switch (type)
{
case DEBUG_S_STRINGTABLE:
parse_string_table (data + off, size, strings);
break;
}
off += size;
if (off % sizeof (uint32_t))
off += sizeof (uint32_t) - (off % sizeof (uint32_t));
}
free (data);
return true;
}
/* Populate the module stream, which consists of the transformed .debug$S
data for each object file. */
static bool
populate_module_stream (bfd *stream, uint32_t *sym_byte_size)
populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
struct string_table *strings)
{
uint8_t int_buf[sizeof (uint32_t)];
*sym_byte_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_debugs_section (s, mod, strings))
return false;
}
}
/* Write the signature. */
bfd_putl32 (CV_SIGNATURE_C13, int_buf);
@ -412,7 +584,7 @@ populate_module_stream (bfd *stream, uint32_t *sym_byte_size)
/* Create the module info substream within the DBI. */
static bool
create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
uint32_t *size)
uint32_t *size, struct string_table *strings)
{
uint8_t *ptr;
@ -482,7 +654,8 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
return false;
}
if (!populate_module_stream (stream, &sym_byte_size))
if (!populate_module_stream (stream, in, &sym_byte_size,
strings))
{
free (*data);
return false;
@ -687,14 +860,16 @@ static bool
populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
uint16_t section_header_stream_num,
uint16_t sym_rec_stream_num,
uint16_t publics_stream_num)
uint16_t publics_stream_num,
struct string_table *strings)
{
struct pdb_dbi_stream_header h;
struct optional_dbg_header opt;
void *mod_info, *sc;
uint32_t mod_info_size, sc_size;
if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size))
if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
strings))
return false;
if (!create_section_contrib_substream (abfd, &sc, &sc_size))
@ -1107,6 +1282,95 @@ create_section_header_stream (bfd *pdb, bfd *abfd, uint16_t *num)
return true;
}
/* Populate the "/names" named stream, which contains the string table. */
static bool
populate_names_stream (bfd *stream, struct string_table *strings)
{
char int_buf[sizeof (uint32_t)];
struct string_table_header h;
uint32_t num_strings = 0, num_buckets;
struct string **buckets;
bfd_putl32 (STRING_TABLE_SIGNATURE, &h.signature);
bfd_putl32 (STRING_TABLE_VERSION, &h.version);
if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
return false;
bfd_putl32 (strings->strings_len, int_buf);
if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
return false;
int_buf[0] = 0;
if (bfd_bwrite (int_buf, 1, stream) != 1)
return false;
for (struct string *s = strings->strings_head; s; s = s->next)
{
if (bfd_bwrite (s->s, s->len, stream) != s->len)
return false;
if (bfd_bwrite (int_buf, 1, stream) != 1)
return false;
num_strings++;
}
num_buckets = num_strings * 2;
buckets = xmalloc (sizeof (struct string *) * num_buckets);
memset (buckets, 0, sizeof (struct string *) * num_buckets);
for (struct string *s = strings->strings_head; s; s = s->next)
{
uint32_t bucket_num = s->hash % num_buckets;
while (buckets[bucket_num])
{
bucket_num++;
if (bucket_num == num_buckets)
bucket_num = 0;
}
buckets[bucket_num] = s;
}
bfd_putl32 (num_buckets, int_buf);
if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
{
free (buckets);
return false;
}
for (unsigned int i = 0; i < num_buckets; i++)
{
if (buckets[i])
bfd_putl32 (buckets[i]->offset, int_buf);
else
bfd_putl32 (0, int_buf);
if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) !=
sizeof (uint32_t))
{
free (buckets);
return false;
}
}
free (buckets);
bfd_putl32 (num_strings, int_buf);
if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
return false;
return true;
}
/* Create a PDB debugging file for the PE image file abfd with the build ID
guid, stored at pdb_name. */
bool
@ -1117,6 +1381,7 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
bfd *info_stream, *dbi_stream, *names_stream, *sym_rec_stream,
*publics_stream;
uint16_t section_header_stream_num, sym_rec_stream_num, publics_stream_num;
struct string_table strings;
pdb = bfd_openw (pdb_name, "pdb");
if (!pdb)
@ -1125,6 +1390,13 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
return false;
}
strings.strings_head = NULL;
strings.strings_tail = NULL;
strings.strings_len = 1;
strings.hashmap = htab_create_alloc (0, hash_string_table_entry,
eq_string_table_entry, free,
xcalloc, free);
bfd_set_format (pdb, bfd_archive);
if (!create_old_directory_stream (pdb))
@ -1201,13 +1473,23 @@ 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))
sym_rec_stream_num, publics_stream_num,
&strings))
{
einfo (_("%P: warning: cannot populate DBI stream "
"in PDB file: %E\n"));
goto end;
}
add_string ("", 0, &strings);
if (!populate_names_stream (names_stream, &strings))
{
einfo (_("%P: warning: cannot populate names stream "
"in PDB file: %E\n"));
goto end;
}
if (!populate_publics_stream (publics_stream, abfd, sym_rec_stream))
{
einfo (_("%P: warning: cannot populate publics stream "
@ -1227,5 +1509,7 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
end:
bfd_close (pdb);
htab_delete (strings.hashmap);
return ret;
}