2022-10-17 07:24:19 +08:00
|
|
|
/* Support for generating PDB CodeView debugging files.
|
|
|
|
Copyright (C) 2022 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
This file is part of the GNU Binutils.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
|
|
MA 02110-1301, USA. */
|
|
|
|
|
|
|
|
#include "pdb.h"
|
|
|
|
#include "bfdlink.h"
|
|
|
|
#include "ld.h"
|
|
|
|
#include "ldmisc.h"
|
|
|
|
#include "libbfd.h"
|
|
|
|
#include "libiberty.h"
|
|
|
|
#include "coff/i386.h"
|
|
|
|
#include "coff/external.h"
|
|
|
|
#include "coff/internal.h"
|
|
|
|
#include "coff/pe.h"
|
|
|
|
#include "libcoff.h"
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
struct public
|
|
|
|
{
|
|
|
|
struct public *next;
|
|
|
|
uint32_t offset;
|
|
|
|
uint32_t hash;
|
|
|
|
unsigned int index;
|
|
|
|
uint16_t section;
|
|
|
|
uint32_t address;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
bfd *stream;
|
|
|
|
uint16_t num;
|
|
|
|
|
|
|
|
stream = bfd_create (name ? name : "", pdb);
|
|
|
|
if (!stream)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!bfd_make_writable (stream))
|
|
|
|
{
|
|
|
|
bfd_close (stream);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pdb->archive_head)
|
|
|
|
{
|
|
|
|
bfd_set_archive_head (pdb, stream);
|
|
|
|
num = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bfd *b = pdb->archive_head;
|
|
|
|
|
|
|
|
num = 1;
|
|
|
|
|
|
|
|
while (b->archive_next)
|
|
|
|
{
|
|
|
|
num++;
|
|
|
|
b = b->archive_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
b->archive_next = stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stream_num)
|
|
|
|
*stream_num = num;
|
|
|
|
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stream 0 ought to be a copy of the MSF directory from the last
|
|
|
|
time the PDB file was written. Because we don't do incremental
|
|
|
|
writes this isn't applicable to us, but we fill it with a dummy
|
|
|
|
value so as not to confuse radare. */
|
|
|
|
static bool
|
|
|
|
create_old_directory_stream (bfd *pdb)
|
|
|
|
{
|
|
|
|
bfd *stream;
|
|
|
|
char buf[sizeof (uint32_t)];
|
|
|
|
|
|
|
|
stream = add_stream (pdb, NULL, NULL);
|
|
|
|
if (!stream)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bfd_putl32 (0, buf);
|
|
|
|
|
|
|
|
return bfd_bwrite (buf, sizeof (uint32_t), stream) == sizeof (uint32_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate the hash of a given string. */
|
|
|
|
static uint32_t
|
|
|
|
calc_hash (const char *data, size_t len)
|
|
|
|
{
|
|
|
|
uint32_t hash = 0;
|
|
|
|
|
|
|
|
while (len >= 4)
|
|
|
|
{
|
|
|
|
hash ^= data[0];
|
|
|
|
hash ^= data[1] << 8;
|
|
|
|
hash ^= data[2] << 16;
|
|
|
|
hash ^= data[3] << 24;
|
|
|
|
|
|
|
|
data += 4;
|
|
|
|
len -= 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len >= 2)
|
|
|
|
{
|
|
|
|
hash ^= data[0];
|
|
|
|
hash ^= data[1] << 8;
|
|
|
|
|
|
|
|
data += 2;
|
|
|
|
len -= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len != 0)
|
|
|
|
hash ^= *data;
|
|
|
|
|
|
|
|
hash |= 0x20202020;
|
|
|
|
hash ^= (hash >> 11);
|
|
|
|
|
|
|
|
return hash ^ (hash >> 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stream 1 is the PDB info stream - see
|
|
|
|
https://llvm.org/docs/PDB/PdbStream.html. */
|
|
|
|
static bool
|
|
|
|
populate_info_stream (bfd *pdb, bfd *info_stream, const unsigned char *guid)
|
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
struct pdb_stream_70 h;
|
|
|
|
uint32_t num_entries, num_buckets;
|
|
|
|
uint32_t names_length, stream_num;
|
|
|
|
char int_buf[sizeof (uint32_t)];
|
|
|
|
|
|
|
|
struct hash_entry
|
|
|
|
{
|
|
|
|
uint32_t offset;
|
|
|
|
uint32_t value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct hash_entry **buckets = NULL;
|
|
|
|
|
|
|
|
/* Write header. */
|
|
|
|
|
|
|
|
bfd_putl32 (PDB_STREAM_VERSION_VC70, &h.version);
|
|
|
|
bfd_putl32 (time (NULL), &h.signature);
|
|
|
|
bfd_putl32 (1, &h.age);
|
|
|
|
|
|
|
|
bfd_putl32 (bfd_getb32 (guid), h.guid);
|
|
|
|
bfd_putl16 (bfd_getb16 (&guid[4]), &h.guid[4]);
|
|
|
|
bfd_putl16 (bfd_getb16 (&guid[6]), &h.guid[6]);
|
|
|
|
memcpy (&h.guid[8], &guid[8], 8);
|
|
|
|
|
|
|
|
if (bfd_bwrite (&h, sizeof (h), info_stream) != sizeof (h))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Write hash list of named streams. This is a "rollover" hash, i.e.
|
|
|
|
if a bucket is filled an entry gets placed in the next free
|
|
|
|
slot. */
|
|
|
|
|
|
|
|
num_entries = 0;
|
|
|
|
for (bfd *b = pdb->archive_head; b; b = b->archive_next)
|
|
|
|
{
|
|
|
|
if (strcmp (b->filename, ""))
|
|
|
|
num_entries++;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_buckets = num_entries * 2;
|
|
|
|
|
|
|
|
names_length = 0;
|
|
|
|
stream_num = 0;
|
|
|
|
|
|
|
|
if (num_buckets > 0)
|
|
|
|
{
|
|
|
|
buckets = xmalloc (sizeof (struct hash_entry *) * num_buckets);
|
|
|
|
memset (buckets, 0, sizeof (struct hash_entry *) * num_buckets);
|
|
|
|
|
|
|
|
for (bfd *b = pdb->archive_head; b; b = b->archive_next)
|
|
|
|
{
|
|
|
|
if (strcmp (b->filename, ""))
|
|
|
|
{
|
|
|
|
size_t len = strlen (b->filename);
|
|
|
|
uint32_t hash = (uint16_t) calc_hash (b->filename, len);
|
|
|
|
uint32_t bucket_num = hash % num_buckets;
|
|
|
|
|
|
|
|
while (buckets[bucket_num])
|
|
|
|
{
|
|
|
|
bucket_num++;
|
|
|
|
|
|
|
|
if (bucket_num == num_buckets)
|
|
|
|
bucket_num = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
buckets[bucket_num] = xmalloc (sizeof (struct hash_entry));
|
|
|
|
|
|
|
|
buckets[bucket_num]->offset = names_length;
|
|
|
|
buckets[bucket_num]->value = stream_num;
|
|
|
|
|
|
|
|
names_length += len + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream_num++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the strings list - the hash keys are indexes into this. */
|
|
|
|
|
|
|
|
bfd_putl32 (names_length, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
for (bfd *b = pdb->archive_head; b; b = b->archive_next)
|
|
|
|
{
|
|
|
|
if (!strcmp (b->filename, ""))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
size_t len = strlen (b->filename) + 1;
|
|
|
|
|
|
|
|
if (bfd_bwrite (b->filename, len, info_stream) != len)
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the number of entries and buckets. */
|
|
|
|
|
|
|
|
bfd_putl32 (num_entries, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
bfd_putl32 (num_buckets, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* Write the present bitmap. */
|
|
|
|
|
|
|
|
bfd_putl32 ((num_buckets + 31) / 32, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < num_buckets; i += 32)
|
|
|
|
{
|
|
|
|
uint32_t v = 0;
|
|
|
|
|
|
|
|
for (unsigned int j = 0; j < 32; j++)
|
|
|
|
{
|
|
|
|
if (i + j >= num_buckets)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (buckets[i + j])
|
|
|
|
v |= 1 << j;
|
|
|
|
}
|
|
|
|
|
|
|
|
bfd_putl32 (v, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the (empty) deleted bitmap. */
|
|
|
|
|
|
|
|
bfd_putl32 (0, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* Write the buckets. */
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < num_buckets; i++)
|
|
|
|
{
|
|
|
|
if (buckets[i])
|
|
|
|
{
|
|
|
|
bfd_putl32 (buckets[i]->offset, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
bfd_putl32 (buckets[i]->value, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bfd_putl32 (0, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
bfd_putl32 (PDB_STREAM_VERSION_VC140, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), info_stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
ret = true;
|
|
|
|
|
|
|
|
end:
|
|
|
|
for (unsigned int i = 0; i < num_buckets; i++)
|
|
|
|
{
|
|
|
|
if (buckets[i])
|
|
|
|
free (buckets[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
free (buckets);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stream 2 is the type information (TPI) stream, and stream 4 is
|
|
|
|
the ID information (IPI) stream. They differ only in which records
|
|
|
|
go in which stream. */
|
|
|
|
static bool
|
|
|
|
create_type_stream (bfd *pdb)
|
|
|
|
{
|
|
|
|
bfd *stream;
|
|
|
|
struct pdb_tpi_stream_header h;
|
|
|
|
|
|
|
|
stream = add_stream (pdb, NULL, NULL);
|
|
|
|
if (!stream)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bfd_putl32 (TPI_STREAM_VERSION_80, &h.version);
|
|
|
|
bfd_putl32 (sizeof (h), &h.header_size);
|
|
|
|
bfd_putl32 (TPI_FIRST_INDEX, &h.type_index_begin);
|
|
|
|
bfd_putl32 (TPI_FIRST_INDEX, &h.type_index_end);
|
|
|
|
bfd_putl32 (0, &h.type_record_bytes);
|
|
|
|
bfd_putl16 (0xffff, &h.hash_stream_index);
|
|
|
|
bfd_putl16 (0xffff, &h.hash_aux_stream_index);
|
|
|
|
bfd_putl32 (4, &h.hash_key_size);
|
|
|
|
bfd_putl32 (0x3ffff, &h.num_hash_buckets);
|
|
|
|
bfd_putl32 (0, &h.hash_value_buffer_offset);
|
|
|
|
bfd_putl32 (0, &h.hash_value_buffer_length);
|
|
|
|
bfd_putl32 (0, &h.index_offset_buffer_offset);
|
|
|
|
bfd_putl32 (0, &h.index_offset_buffer_length);
|
|
|
|
bfd_putl32 (0, &h.hash_adj_buffer_offset);
|
|
|
|
bfd_putl32 (0, &h.hash_adj_buffer_length);
|
|
|
|
|
|
|
|
if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the PE architecture number for the image. */
|
|
|
|
static uint16_t
|
|
|
|
get_arch_number (bfd *abfd)
|
|
|
|
{
|
|
|
|
if (abfd->arch_info->arch != bfd_arch_i386)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (abfd->arch_info->mach & bfd_mach_x86_64)
|
|
|
|
return IMAGE_FILE_MACHINE_AMD64;
|
|
|
|
|
|
|
|
return IMAGE_FILE_MACHINE_I386;
|
|
|
|
}
|
|
|
|
|
2022-11-03 10:46:04 +08:00
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
uint8_t int_buf[sizeof (uint32_t)];
|
|
|
|
|
|
|
|
*sym_byte_size = sizeof (uint32_t);
|
|
|
|
|
|
|
|
/* Write the signature. */
|
|
|
|
|
|
|
|
bfd_putl32 (CV_SIGNATURE_C13, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Write the global refs size. */
|
|
|
|
|
|
|
|
bfd_putl32 (0, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the module info substream within the DBI. */
|
|
|
|
static bool
|
|
|
|
create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
|
|
|
|
uint32_t *size)
|
|
|
|
{
|
|
|
|
uint8_t *ptr;
|
|
|
|
|
|
|
|
static const char linker_fn[] = "* Linker *";
|
|
|
|
|
|
|
|
*size = 0;
|
|
|
|
|
|
|
|
for (bfd *in = coff_data (abfd)->link_info->input_bfds; in;
|
|
|
|
in = in->link.next)
|
|
|
|
{
|
|
|
|
size_t len = sizeof (struct module_info);
|
|
|
|
|
|
|
|
if (!strcmp (bfd_get_filename (in), "dll stuff"))
|
|
|
|
{
|
|
|
|
len += sizeof (linker_fn); /* Object name. */
|
|
|
|
len++; /* Empty module name. */
|
|
|
|
}
|
|
|
|
else if (in->my_archive)
|
|
|
|
{
|
|
|
|
char *name = lrealpath (bfd_get_filename (in));
|
|
|
|
|
|
|
|
len += strlen (name) + 1; /* Object name. */
|
|
|
|
|
|
|
|
free (name);
|
|
|
|
|
|
|
|
name = lrealpath (bfd_get_filename (in->my_archive));
|
|
|
|
|
|
|
|
len += strlen (name) + 1; /* Archive name. */
|
|
|
|
|
|
|
|
free (name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *name = lrealpath (bfd_get_filename (in));
|
|
|
|
size_t name_len = strlen (name) + 1;
|
|
|
|
|
|
|
|
len += name_len; /* Object name. */
|
|
|
|
len += name_len; /* And again as the archive name. */
|
|
|
|
|
|
|
|
free (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len % 4)
|
|
|
|
len += 4 - (len % 4);
|
|
|
|
|
|
|
|
*size += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
*data = xmalloc (*size);
|
|
|
|
|
|
|
|
ptr = *data;
|
|
|
|
|
|
|
|
for (bfd *in = coff_data (abfd)->link_info->input_bfds; in;
|
|
|
|
in = in->link.next)
|
|
|
|
{
|
|
|
|
struct module_info *mod = (struct module_info *) ptr;
|
|
|
|
uint16_t stream_num;
|
|
|
|
bfd *stream;
|
|
|
|
uint32_t sym_byte_size;
|
|
|
|
uint8_t *start = ptr;
|
|
|
|
|
|
|
|
stream = add_stream (pdb, NULL, &stream_num);
|
|
|
|
|
|
|
|
if (!stream)
|
|
|
|
{
|
|
|
|
free (*data);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!populate_module_stream (stream, &sym_byte_size))
|
|
|
|
{
|
|
|
|
free (*data);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bfd_putl32 (0, &mod->unused1);
|
|
|
|
|
|
|
|
/* These are dummy values - MSVC copies the first section contribution
|
|
|
|
entry here, but doesn't seem to use it for anything. */
|
|
|
|
bfd_putl16 (0xffff, &mod->sc.section);
|
|
|
|
bfd_putl16 (0, &mod->sc.padding1);
|
|
|
|
bfd_putl32 (0, &mod->sc.offset);
|
|
|
|
bfd_putl32 (0xffffffff, &mod->sc.size);
|
|
|
|
bfd_putl32 (0, &mod->sc.characteristics);
|
|
|
|
bfd_putl16 (0xffff, &mod->sc.module_index);
|
|
|
|
bfd_putl16 (0, &mod->sc.padding2);
|
|
|
|
bfd_putl32 (0, &mod->sc.data_crc);
|
|
|
|
bfd_putl32 (0, &mod->sc.reloc_crc);
|
|
|
|
|
|
|
|
bfd_putl16 (0, &mod->flags);
|
|
|
|
bfd_putl16 (stream_num, &mod->module_sym_stream);
|
|
|
|
bfd_putl32 (sym_byte_size, &mod->sym_byte_size);
|
|
|
|
bfd_putl32 (0, &mod->c11_byte_size);
|
|
|
|
bfd_putl32 (0, &mod->c13_byte_size);
|
|
|
|
bfd_putl16 (0, &mod->source_file_count);
|
|
|
|
bfd_putl16 (0, &mod->padding);
|
|
|
|
bfd_putl32 (0, &mod->unused2);
|
|
|
|
bfd_putl32 (0, &mod->source_file_name_index);
|
|
|
|
bfd_putl32 (0, &mod->pdb_file_path_name_index);
|
|
|
|
|
|
|
|
ptr += sizeof (struct module_info);
|
|
|
|
|
|
|
|
if (!strcmp (bfd_get_filename (in), "dll stuff"))
|
|
|
|
{
|
|
|
|
/* Object name. */
|
|
|
|
memcpy (ptr, linker_fn, sizeof (linker_fn));
|
|
|
|
ptr += sizeof (linker_fn);
|
|
|
|
|
|
|
|
/* Empty module name. */
|
|
|
|
*ptr = 0;
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
else if (in->my_archive)
|
|
|
|
{
|
|
|
|
char *name = lrealpath (bfd_get_filename (in));
|
|
|
|
size_t name_len = strlen (name) + 1;
|
|
|
|
|
|
|
|
/* Object name. */
|
|
|
|
memcpy (ptr, name, name_len);
|
|
|
|
ptr += name_len;
|
|
|
|
|
|
|
|
free (name);
|
|
|
|
|
|
|
|
name = lrealpath (bfd_get_filename (in->my_archive));
|
|
|
|
name_len = strlen (name) + 1;
|
|
|
|
|
|
|
|
/* Archive name. */
|
|
|
|
memcpy (ptr, name, name_len);
|
|
|
|
ptr += name_len;
|
|
|
|
|
|
|
|
free (name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *name = lrealpath (bfd_get_filename (in));
|
|
|
|
size_t name_len = strlen (name) + 1;
|
|
|
|
|
|
|
|
/* Object name. */
|
|
|
|
memcpy (ptr, name, name_len);
|
|
|
|
ptr += name_len;
|
|
|
|
|
|
|
|
/* Object name again as archive name. */
|
|
|
|
memcpy (ptr, name, name_len);
|
|
|
|
ptr += name_len;
|
|
|
|
|
|
|
|
free (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pad to next four-byte boundary. */
|
|
|
|
|
|
|
|
if ((ptr - start) % 4)
|
|
|
|
{
|
|
|
|
memset (ptr, 0, 4 - ((ptr - start) % 4));
|
|
|
|
ptr += 4 - ((ptr - start) % 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-31 08:15:54 +08:00
|
|
|
/* Return the index of a given output section. */
|
|
|
|
static uint16_t
|
|
|
|
find_section_number (bfd *abfd, asection *sect)
|
|
|
|
{
|
|
|
|
uint16_t i = 1;
|
|
|
|
|
|
|
|
for (asection *s = abfd->sections; s; s = s->next)
|
|
|
|
{
|
|
|
|
if (s == sect)
|
|
|
|
return i;
|
|
|
|
|
|
|
|
/* Empty sections aren't output. */
|
|
|
|
if (s->size != 0)
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-11-11 11:30:40 +08:00
|
|
|
/* Create the substream which maps addresses in the image file to locations
|
|
|
|
in the original object files. */
|
|
|
|
static bool
|
|
|
|
create_section_contrib_substream (bfd *abfd, void **data, uint32_t *size)
|
|
|
|
{
|
|
|
|
unsigned int num_sc = 0;
|
|
|
|
struct section_contribution *sc;
|
|
|
|
uint16_t mod_index;
|
|
|
|
char *sect_flags;
|
|
|
|
file_ptr offset;
|
|
|
|
|
|
|
|
for (bfd *in = coff_data (abfd)->link_info->input_bfds; in;
|
|
|
|
in = in->link.next)
|
|
|
|
{
|
|
|
|
for (asection *s = in->sections; s; s = s->next)
|
|
|
|
{
|
|
|
|
if (s->size == 0 || discarded_section (s))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
num_sc++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*size = sizeof (uint32_t) + (num_sc * sizeof (struct section_contribution));
|
|
|
|
*data = xmalloc (*size);
|
|
|
|
|
|
|
|
bfd_putl32 (SECTION_CONTRIB_VERSION_60, *data);
|
|
|
|
|
|
|
|
/* Read characteristics of outputted sections. */
|
|
|
|
|
|
|
|
sect_flags = xmalloc (sizeof (uint32_t) * abfd->section_count);
|
|
|
|
|
|
|
|
offset = bfd_coff_filhsz (abfd) + bfd_coff_aoutsz (abfd);
|
|
|
|
offset += offsetof (struct external_scnhdr, s_flags);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < abfd->section_count; i++)
|
|
|
|
{
|
|
|
|
bfd_seek (abfd, offset, SEEK_SET);
|
|
|
|
|
|
|
|
if (bfd_bread (sect_flags + (i * sizeof (uint32_t)), sizeof (uint32_t),
|
|
|
|
abfd) != sizeof (uint32_t))
|
|
|
|
{
|
|
|
|
free (*data);
|
|
|
|
free (sect_flags);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += sizeof (struct external_scnhdr);
|
|
|
|
}
|
|
|
|
|
|
|
|
sc =
|
|
|
|
(struct section_contribution *) ((uint8_t *) *data + sizeof (uint32_t));
|
|
|
|
|
|
|
|
mod_index = 0;
|
|
|
|
for (bfd *in = coff_data (abfd)->link_info->input_bfds; in;
|
|
|
|
in = in->link.next)
|
|
|
|
{
|
|
|
|
for (asection *s = in->sections; s; s = s->next)
|
|
|
|
{
|
|
|
|
uint16_t sect_num;
|
|
|
|
|
|
|
|
if (s->size == 0 || discarded_section (s))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sect_num = find_section_number (abfd, s->output_section);
|
|
|
|
|
|
|
|
memcpy (&sc->characteristics,
|
|
|
|
sect_flags + ((sect_num - 1) * sizeof (uint32_t)),
|
|
|
|
sizeof (uint32_t));
|
|
|
|
|
|
|
|
bfd_putl16 (sect_num, &sc->section);
|
|
|
|
bfd_putl16 (0, &sc->padding1);
|
|
|
|
bfd_putl32 (s->output_offset, &sc->offset);
|
|
|
|
bfd_putl32 (s->size, &sc->size);
|
|
|
|
bfd_putl16 (mod_index, &sc->module_index);
|
|
|
|
bfd_putl16 (0, &sc->padding2);
|
|
|
|
bfd_putl32 (0, &sc->data_crc);
|
|
|
|
bfd_putl32 (0, &sc->reloc_crc);
|
|
|
|
|
|
|
|
sc++;
|
|
|
|
}
|
|
|
|
|
|
|
|
mod_index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
free (sect_flags);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-17 07:24:19 +08:00
|
|
|
/* Stream 4 is the debug information (DBI) stream. */
|
|
|
|
static bool
|
2022-11-03 10:46:04 +08:00
|
|
|
populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
|
2022-10-31 08:15:54 +08:00
|
|
|
uint16_t section_header_stream_num,
|
|
|
|
uint16_t sym_rec_stream_num,
|
|
|
|
uint16_t publics_stream_num)
|
2022-10-17 07:24:19 +08:00
|
|
|
{
|
|
|
|
struct pdb_dbi_stream_header h;
|
|
|
|
struct optional_dbg_header opt;
|
2022-11-11 11:30:40 +08:00
|
|
|
void *mod_info, *sc;
|
|
|
|
uint32_t mod_info_size, sc_size;
|
2022-11-03 10:46:04 +08:00
|
|
|
|
|
|
|
if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size))
|
|
|
|
return false;
|
2022-10-17 07:24:19 +08:00
|
|
|
|
2022-11-11 11:30:40 +08:00
|
|
|
if (!create_section_contrib_substream (abfd, &sc, &sc_size))
|
|
|
|
{
|
|
|
|
free (mod_info);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-10-17 07:24:19 +08:00
|
|
|
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 (0x8e1d, &h.build_number); // MSVC 14.29
|
2022-10-31 08:15:54 +08:00
|
|
|
bfd_putl16 (publics_stream_num, &h.public_stream_index);
|
2022-10-17 07:24:19 +08:00
|
|
|
bfd_putl16 (0, &h.pdb_dll_version);
|
2022-10-31 08:15:54 +08:00
|
|
|
bfd_putl16 (sym_rec_stream_num, &h.sym_record_stream);
|
2022-10-17 07:24:19 +08:00
|
|
|
bfd_putl16 (0, &h.pdb_dll_rbld);
|
2022-11-03 10:46:04 +08:00
|
|
|
bfd_putl32 (mod_info_size, &h.mod_info_size);
|
2022-11-11 11:30:40 +08:00
|
|
|
bfd_putl32 (sc_size, &h.section_contribution_size);
|
2022-10-17 07:24:19 +08:00
|
|
|
bfd_putl32 (0, &h.section_map_size);
|
|
|
|
bfd_putl32 (0, &h.source_info_size);
|
|
|
|
bfd_putl32 (0, &h.type_server_map_size);
|
|
|
|
bfd_putl32 (0, &h.mfc_type_server_index);
|
|
|
|
bfd_putl32 (sizeof (opt), &h.optional_dbg_header_size);
|
|
|
|
bfd_putl32 (0, &h.ec_substream_size);
|
|
|
|
bfd_putl16 (0, &h.flags);
|
|
|
|
bfd_putl16 (get_arch_number (abfd), &h.machine);
|
|
|
|
bfd_putl32 (0, &h.padding);
|
|
|
|
|
|
|
|
if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
|
2022-11-03 10:46:04 +08:00
|
|
|
{
|
2022-11-11 11:30:40 +08:00
|
|
|
free (sc);
|
2022-11-03 10:46:04 +08:00
|
|
|
free (mod_info);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bfd_bwrite (mod_info, mod_info_size, stream) != mod_info_size)
|
|
|
|
{
|
2022-11-11 11:30:40 +08:00
|
|
|
free (sc);
|
2022-11-03 10:46:04 +08:00
|
|
|
free (mod_info);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
free (mod_info);
|
2022-10-17 07:24:19 +08:00
|
|
|
|
2022-11-11 11:30:40 +08:00
|
|
|
if (bfd_bwrite (sc, sc_size, stream) != sc_size)
|
|
|
|
{
|
|
|
|
free (sc);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
free (sc);
|
|
|
|
|
2022-10-17 07:24:19 +08:00
|
|
|
bfd_putl16 (0xffff, &opt.fpo_stream);
|
|
|
|
bfd_putl16 (0xffff, &opt.exception_stream);
|
|
|
|
bfd_putl16 (0xffff, &opt.fixup_stream);
|
|
|
|
bfd_putl16 (0xffff, &opt.omap_to_src_stream);
|
|
|
|
bfd_putl16 (0xffff, &opt.omap_from_src_stream);
|
2022-10-31 08:15:53 +08:00
|
|
|
bfd_putl16 (section_header_stream_num, &opt.section_header_stream);
|
2022-10-17 07:24:19 +08:00
|
|
|
bfd_putl16 (0xffff, &opt.token_map_stream);
|
|
|
|
bfd_putl16 (0xffff, &opt.xdata_stream);
|
|
|
|
bfd_putl16 (0xffff, &opt.pdata_stream);
|
|
|
|
bfd_putl16 (0xffff, &opt.new_fpo_stream);
|
|
|
|
bfd_putl16 (0xffff, &opt.orig_section_header_stream);
|
|
|
|
|
|
|
|
if (bfd_bwrite (&opt, sizeof (opt), stream) != sizeof (opt))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-31 08:15:54 +08:00
|
|
|
/* Used as parameter to qsort, to sort publics by hash. */
|
|
|
|
static int
|
|
|
|
public_compare_hash (const void *s1, const void *s2)
|
|
|
|
{
|
|
|
|
const struct public *p1 = *(const struct public **) s1;
|
|
|
|
const struct public *p2 = *(const struct public **) s2;
|
|
|
|
|
|
|
|
if (p1->hash < p2->hash)
|
|
|
|
return -1;
|
|
|
|
if (p1->hash > p2->hash)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Used as parameter to qsort, to sort publics by address. */
|
|
|
|
static int
|
|
|
|
public_compare_addr (const void *s1, const void *s2)
|
|
|
|
{
|
|
|
|
const struct public *p1 = *(const struct public **) s1;
|
|
|
|
const struct public *p2 = *(const struct public **) s2;
|
|
|
|
|
|
|
|
if (p1->section < p2->section)
|
|
|
|
return -1;
|
|
|
|
if (p1->section > p2->section)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (p1->address < p2->address)
|
|
|
|
return -1;
|
|
|
|
if (p1->address > p2->address)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The publics stream is a hash map of S_PUB32 records, which are stored
|
|
|
|
in the symbol record stream. Each S_PUB32 entry represents a symbol
|
|
|
|
from the point of view of the linker: a section index, an offset within
|
|
|
|
the section, and a mangled name. Compare with S_GDATA32 and S_GPROC32,
|
|
|
|
which are the same thing but generated by the compiler. */
|
|
|
|
static bool
|
|
|
|
populate_publics_stream (bfd *stream, bfd *abfd, bfd *sym_rec_stream)
|
|
|
|
{
|
|
|
|
struct publics_header header;
|
|
|
|
struct globals_hash_header hash_header;
|
|
|
|
const unsigned int num_buckets = 4096;
|
|
|
|
unsigned int num_entries = 0, filled_buckets = 0;
|
|
|
|
unsigned int buckets_size, sym_hash_size;
|
|
|
|
char int_buf[sizeof (uint32_t)];
|
|
|
|
struct public *publics_head = NULL, *publics_tail = NULL;
|
|
|
|
struct public **buckets;
|
|
|
|
struct public **sorted = NULL;
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
buckets = xmalloc (sizeof (struct public *) * num_buckets);
|
|
|
|
memset (buckets, 0, sizeof (struct public *) * num_buckets);
|
|
|
|
|
|
|
|
/* Loop through the global symbols in our input files, and write S_PUB32
|
|
|
|
records in the symbol record stream for those that make it into the
|
|
|
|
final image. */
|
|
|
|
for (bfd *in = coff_data (abfd)->link_info->input_bfds; in;
|
|
|
|
in = in->link.next)
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < in->symcount; i++)
|
|
|
|
{
|
|
|
|
struct bfd_symbol *sym = in->outsymbols[i];
|
|
|
|
|
|
|
|
if (sym->flags & BSF_GLOBAL)
|
|
|
|
{
|
|
|
|
struct pubsym ps;
|
|
|
|
uint16_t record_length;
|
|
|
|
const char *name = sym->name;
|
|
|
|
size_t name_len = strlen (name);
|
|
|
|
struct public *p = xmalloc (sizeof (struct public));
|
|
|
|
unsigned int padding = 0;
|
|
|
|
uint16_t section;
|
|
|
|
uint32_t flags = 0;
|
|
|
|
|
|
|
|
section =
|
|
|
|
find_section_number (abfd, sym->section->output_section);
|
|
|
|
|
|
|
|
if (section == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
p->next = NULL;
|
|
|
|
p->offset = bfd_tell (sym_rec_stream);
|
|
|
|
p->hash = calc_hash (name, name_len) % num_buckets;
|
|
|
|
p->section = section;
|
|
|
|
p->address = sym->section->output_offset + sym->value;
|
|
|
|
|
|
|
|
record_length = sizeof (struct pubsym) + name_len + 1;
|
|
|
|
|
|
|
|
if (record_length % 4)
|
|
|
|
padding = 4 - (record_length % 4);
|
|
|
|
|
|
|
|
/* Assume that all global symbols in executable sections
|
|
|
|
are functions. */
|
|
|
|
if (sym->section->flags & SEC_CODE)
|
|
|
|
flags = PUBSYM_FUNCTION;
|
|
|
|
|
|
|
|
bfd_putl16 (record_length + padding - sizeof (uint16_t),
|
|
|
|
&ps.record_length);
|
|
|
|
bfd_putl16 (S_PUB32, &ps.record_type);
|
|
|
|
bfd_putl32 (flags, &ps.flags);
|
|
|
|
bfd_putl32 (p->address, &ps.offset);
|
|
|
|
bfd_putl16 (p->section, &ps.section);
|
|
|
|
|
|
|
|
if (bfd_bwrite (&ps, sizeof (struct pubsym), sym_rec_stream) !=
|
|
|
|
sizeof (struct pubsym))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
if (bfd_bwrite (name, name_len + 1, sym_rec_stream) !=
|
|
|
|
name_len + 1)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
for (unsigned int j = 0; j < padding; j++)
|
|
|
|
{
|
|
|
|
uint8_t b = 0;
|
|
|
|
|
|
|
|
if (bfd_bwrite (&b, sizeof (uint8_t), sym_rec_stream) !=
|
|
|
|
sizeof (uint8_t))
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!publics_head)
|
|
|
|
publics_head = p;
|
|
|
|
else
|
|
|
|
publics_tail->next = p;
|
|
|
|
|
|
|
|
publics_tail = p;
|
|
|
|
num_entries++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (num_entries > 0)
|
|
|
|
{
|
|
|
|
/* Create an array of pointers, sorted by hash value. */
|
|
|
|
|
|
|
|
sorted = xmalloc (sizeof (struct public *) * num_entries);
|
|
|
|
|
|
|
|
struct public *p = publics_head;
|
|
|
|
for (unsigned int i = 0; i < num_entries; i++)
|
|
|
|
{
|
|
|
|
sorted[i] = p;
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort (sorted, num_entries, sizeof (struct public *),
|
|
|
|
public_compare_hash);
|
|
|
|
|
|
|
|
/* Populate the buckets. */
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < num_entries; i++)
|
|
|
|
{
|
|
|
|
if (!buckets[sorted[i]->hash])
|
|
|
|
{
|
|
|
|
buckets[sorted[i]->hash] = sorted[i];
|
|
|
|
filled_buckets++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sorted[i]->index = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
buckets_size = num_buckets / 8;
|
|
|
|
buckets_size += sizeof (uint32_t);
|
|
|
|
buckets_size += filled_buckets * sizeof (uint32_t);
|
|
|
|
|
|
|
|
sym_hash_size = sizeof (hash_header);
|
|
|
|
sym_hash_size += num_entries * sizeof (struct hash_record);
|
|
|
|
sym_hash_size += buckets_size;
|
|
|
|
|
|
|
|
/* Output the publics header. */
|
|
|
|
|
|
|
|
bfd_putl32 (sym_hash_size, &header.sym_hash_size);
|
|
|
|
bfd_putl32 (num_entries * sizeof (uint32_t), &header.addr_map_size);
|
|
|
|
bfd_putl32 (0, &header.num_thunks);
|
|
|
|
bfd_putl32 (0, &header.thunks_size);
|
|
|
|
bfd_putl32 (0, &header.thunk_table);
|
|
|
|
bfd_putl32 (0, &header.thunk_table_offset);
|
|
|
|
bfd_putl32 (0, &header.num_sects);
|
|
|
|
|
|
|
|
if (bfd_bwrite (&header, sizeof (header), stream) != sizeof (header))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* Output the global hash header. */
|
|
|
|
|
|
|
|
bfd_putl32 (GLOBALS_HASH_SIGNATURE, &hash_header.signature);
|
|
|
|
bfd_putl32 (GLOBALS_HASH_VERSION_70, &hash_header.version);
|
|
|
|
bfd_putl32 (num_entries * sizeof (struct hash_record),
|
|
|
|
&hash_header.entries_size);
|
|
|
|
bfd_putl32 (buckets_size, &hash_header.buckets_size);
|
|
|
|
|
|
|
|
if (bfd_bwrite (&hash_header, sizeof (hash_header), stream) !=
|
|
|
|
sizeof (hash_header))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* Write the entries in hash order. */
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < num_entries; i++)
|
|
|
|
{
|
|
|
|
struct hash_record hr;
|
|
|
|
|
|
|
|
bfd_putl32 (sorted[i]->offset + 1, &hr.offset);
|
|
|
|
bfd_putl32 (1, &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_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_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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the address map: offsets into the symbol record stream of
|
|
|
|
S_PUB32 records, ordered by address. */
|
|
|
|
|
|
|
|
if (num_entries > 0)
|
|
|
|
{
|
|
|
|
qsort (sorted, num_entries, sizeof (struct public *),
|
|
|
|
public_compare_addr);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < num_entries; i++)
|
|
|
|
{
|
|
|
|
bfd_putl32 (sorted[i]->offset, int_buf);
|
|
|
|
|
|
|
|
if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) !=
|
|
|
|
sizeof (uint32_t))
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = true;
|
|
|
|
|
|
|
|
end:
|
|
|
|
free (buckets);
|
|
|
|
|
|
|
|
while (publics_head)
|
|
|
|
{
|
|
|
|
struct public *p = publics_head->next;
|
|
|
|
|
|
|
|
free (publics_head);
|
|
|
|
publics_head = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
free (sorted);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-10-31 08:15:53 +08:00
|
|
|
/* The section header stream contains a copy of the section headers
|
|
|
|
from the PE file, in the same format. */
|
|
|
|
static bool
|
|
|
|
create_section_header_stream (bfd *pdb, bfd *abfd, uint16_t *num)
|
|
|
|
{
|
|
|
|
bfd *stream;
|
|
|
|
unsigned int section_count;
|
|
|
|
file_ptr scn_base;
|
|
|
|
size_t len;
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
stream = add_stream (pdb, NULL, num);
|
|
|
|
if (!stream)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
section_count = abfd->section_count;
|
|
|
|
|
|
|
|
/* Empty sections aren't output. */
|
|
|
|
for (asection *sect = abfd->sections; sect; sect = sect->next)
|
|
|
|
{
|
|
|
|
if (sect->size == 0)
|
|
|
|
section_count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (section_count == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Copy section table from output - it's already been written at this
|
|
|
|
point. */
|
|
|
|
|
|
|
|
scn_base = bfd_coff_filhsz (abfd) + bfd_coff_aoutsz (abfd);
|
|
|
|
|
|
|
|
bfd_seek (abfd, scn_base, SEEK_SET);
|
|
|
|
|
|
|
|
len = section_count * sizeof (struct external_scnhdr);
|
|
|
|
buf = xmalloc (len);
|
|
|
|
|
|
|
|
if (bfd_bread (buf, len, abfd) != len)
|
|
|
|
{
|
|
|
|
free (buf);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bfd_bwrite (buf, len, stream) != len)
|
|
|
|
{
|
|
|
|
free (buf);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
free (buf);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-17 07:24:19 +08:00
|
|
|
/* Create a PDB debugging file for the PE image file abfd with the build ID
|
|
|
|
guid, stored at pdb_name. */
|
|
|
|
bool
|
|
|
|
create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
|
|
|
|
{
|
|
|
|
bfd *pdb;
|
|
|
|
bool ret = false;
|
2022-10-31 08:15:54 +08:00
|
|
|
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;
|
2022-10-17 07:24:19 +08:00
|
|
|
|
|
|
|
pdb = bfd_openw (pdb_name, "pdb");
|
|
|
|
if (!pdb)
|
|
|
|
{
|
2022-10-31 08:15:52 +08:00
|
|
|
einfo (_("%P: warning: cannot create PDB file: %E\n"));
|
2022-10-17 07:24:19 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bfd_set_format (pdb, bfd_archive);
|
|
|
|
|
|
|
|
if (!create_old_directory_stream (pdb))
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot create old directory stream "
|
2022-10-31 08:15:52 +08:00
|
|
|
"in PDB file: %E\n"));
|
2022-10-17 07:24:19 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
info_stream = add_stream (pdb, NULL, NULL);
|
|
|
|
|
|
|
|
if (!info_stream)
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot create info stream "
|
2022-10-31 08:15:52 +08:00
|
|
|
"in PDB file: %E\n"));
|
2022-10-17 07:24:19 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!create_type_stream (pdb))
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot create TPI stream "
|
2022-10-31 08:15:52 +08:00
|
|
|
"in PDB file: %E\n"));
|
2022-10-17 07:24:19 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbi_stream = add_stream (pdb, NULL, NULL);
|
|
|
|
|
|
|
|
if (!dbi_stream)
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot create DBI stream "
|
2022-10-31 08:15:52 +08:00
|
|
|
"in PDB file: %E\n"));
|
2022-10-17 07:24:19 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!create_type_stream (pdb))
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot create IPI stream "
|
2022-10-31 08:15:52 +08:00
|
|
|
"in PDB file: %E\n"));
|
2022-10-17 07:24:19 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
names_stream = add_stream (pdb, "/names", NULL);
|
|
|
|
|
|
|
|
if (!names_stream)
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot create /names stream "
|
2022-10-31 08:15:52 +08:00
|
|
|
"in PDB file: %E\n"));
|
2022-10-17 07:24:19 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2022-10-31 08:15:54 +08:00
|
|
|
sym_rec_stream = add_stream (pdb, NULL, &sym_rec_stream_num);
|
|
|
|
|
|
|
|
if (!sym_rec_stream)
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot create symbol record stream "
|
|
|
|
"in PDB file: %E\n"));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
publics_stream = add_stream (pdb, NULL, &publics_stream_num);
|
|
|
|
|
|
|
|
if (!publics_stream)
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot create publics stream "
|
|
|
|
"in PDB file: %E\n"));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2022-10-31 08:15:53 +08:00
|
|
|
if (!create_section_header_stream (pdb, abfd, §ion_header_stream_num))
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot create section header stream "
|
|
|
|
"in PDB file: %E\n"));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2022-11-03 10:46:04 +08:00
|
|
|
if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num,
|
2022-10-31 08:15:54 +08:00
|
|
|
sym_rec_stream_num, publics_stream_num))
|
2022-10-17 07:24:19 +08:00
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot populate DBI stream "
|
2022-10-31 08:15:52 +08:00
|
|
|
"in PDB file: %E\n"));
|
2022-10-17 07:24:19 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2022-10-31 08:15:54 +08:00
|
|
|
if (!populate_publics_stream (publics_stream, abfd, sym_rec_stream))
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot populate publics stream "
|
|
|
|
"in PDB file: %E\n"));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2022-10-17 07:24:19 +08:00
|
|
|
if (!populate_info_stream (pdb, info_stream, guid))
|
|
|
|
{
|
|
|
|
einfo (_("%P: warning: cannot populate info stream "
|
2022-10-31 08:15:52 +08:00
|
|
|
"in PDB file: %E\n"));
|
2022-10-17 07:24:19 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = true;
|
|
|
|
|
|
|
|
end:
|
|
|
|
bfd_close (pdb);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|