ld: Add minimal pdb generation
This commit is contained in:
parent
f6f30f347b
commit
b41a65333f
8 changed files with 930 additions and 9 deletions
|
@ -476,12 +476,14 @@ ALL_64_EMUL_EXTRA_OFILES = \
|
|||
CFILES = ldctor.c ldemul.c ldexp.c ldfile.c ldlang.c \
|
||||
ldmain.c ldmisc.c ldver.c ldwrite.c lexsup.c \
|
||||
mri.c ldcref.c pe-dll.c pep-dll.c ldlex-wrapper.c \
|
||||
plugin.c ldbuildid.c ldelf.c ldelfgen.c
|
||||
plugin.c ldbuildid.c ldelf.c ldelfgen.c \
|
||||
pdb.c
|
||||
|
||||
HFILES = ld.h ldctor.h ldemul.h ldexp.h ldfile.h \
|
||||
ldlang.h ldlex.h ldmain.h ldmisc.h ldver.h \
|
||||
ldwrite.h mri.h deffile.h pe-dll.h pep-dll.h \
|
||||
elf-hints-local.h plugin.h ldbuildid.h ldelf.h ldelfgen.h
|
||||
elf-hints-local.h plugin.h ldbuildid.h ldelf.h ldelfgen.h \
|
||||
pdb.h
|
||||
|
||||
GENERATED_CFILES = ldgram.c ldlex.c deffilep.c
|
||||
GENERATED_HFILES = ldgram.h ldemul-list.h deffilep.h
|
||||
|
@ -494,7 +496,7 @@ OFILES = ldgram.@OBJEXT@ ldlex-wrapper.@OBJEXT@ lexsup.@OBJEXT@ ldlang.@OBJEXT@
|
|||
mri.@OBJEXT@ ldctor.@OBJEXT@ ldmain.@OBJEXT@ plugin.@OBJEXT@ \
|
||||
ldwrite.@OBJEXT@ ldexp.@OBJEXT@ ldemul.@OBJEXT@ ldver.@OBJEXT@ ldmisc.@OBJEXT@ \
|
||||
ldfile.@OBJEXT@ ldcref.@OBJEXT@ ${EMULATION_OFILES} ${EMUL_EXTRA_OFILES} \
|
||||
ldbuildid.@OBJEXT@
|
||||
ldbuildid.@OBJEXT@ pdb.@OBJEXT@
|
||||
|
||||
STAGESTUFF = *.@OBJEXT@ ldscripts/* e*.c
|
||||
|
||||
|
@ -957,7 +959,7 @@ EXTRA_ld_new_SOURCES += pep-dll.c pe-dll.c ldelf.c ldelfgen.c
|
|||
|
||||
ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmain.c \
|
||||
ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c plugin.c \
|
||||
ldbuildid.c
|
||||
ldbuildid.c pdb.c
|
||||
ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) \
|
||||
$(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL_DEP) $(JANSSON_LIBS)
|
||||
ld_new_LDADD = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBCTF) \
|
||||
|
|
|
@ -212,7 +212,7 @@ am_ld_new_OBJECTS = ldgram.$(OBJEXT) ldlex-wrapper.$(OBJEXT) \
|
|||
ldctor.$(OBJEXT) ldmain.$(OBJEXT) ldwrite.$(OBJEXT) \
|
||||
ldexp.$(OBJEXT) ldemul.$(OBJEXT) ldver.$(OBJEXT) \
|
||||
ldmisc.$(OBJEXT) ldfile.$(OBJEXT) ldcref.$(OBJEXT) \
|
||||
plugin.$(OBJEXT) ldbuildid.$(OBJEXT)
|
||||
plugin.$(OBJEXT) ldbuildid.$(OBJEXT) pdb.$(OBJEXT)
|
||||
ld_new_OBJECTS = $(am_ld_new_OBJECTS)
|
||||
am__DEPENDENCIES_1 =
|
||||
@ENABLE_LIBCTF_TRUE@am__DEPENDENCIES_2 = ../libctf/libctf.la
|
||||
|
@ -974,12 +974,14 @@ ALL_64_EMUL_EXTRA_OFILES = \
|
|||
CFILES = ldctor.c ldemul.c ldexp.c ldfile.c ldlang.c \
|
||||
ldmain.c ldmisc.c ldver.c ldwrite.c lexsup.c \
|
||||
mri.c ldcref.c pe-dll.c pep-dll.c ldlex-wrapper.c \
|
||||
plugin.c ldbuildid.c ldelf.c ldelfgen.c
|
||||
plugin.c ldbuildid.c ldelf.c ldelfgen.c \
|
||||
pdb.c
|
||||
|
||||
HFILES = ld.h ldctor.h ldemul.h ldexp.h ldfile.h \
|
||||
ldlang.h ldlex.h ldmain.h ldmisc.h ldver.h \
|
||||
ldwrite.h mri.h deffile.h pe-dll.h pep-dll.h \
|
||||
elf-hints-local.h plugin.h ldbuildid.h ldelf.h ldelfgen.h
|
||||
elf-hints-local.h plugin.h ldbuildid.h ldelf.h ldelfgen.h \
|
||||
pdb.h
|
||||
|
||||
GENERATED_CFILES = ldgram.c ldlex.c deffilep.c
|
||||
GENERATED_HFILES = ldgram.h ldemul-list.h deffilep.h
|
||||
|
@ -991,7 +993,7 @@ OFILES = ldgram.@OBJEXT@ ldlex-wrapper.@OBJEXT@ lexsup.@OBJEXT@ ldlang.@OBJEXT@
|
|||
mri.@OBJEXT@ ldctor.@OBJEXT@ ldmain.@OBJEXT@ plugin.@OBJEXT@ \
|
||||
ldwrite.@OBJEXT@ ldexp.@OBJEXT@ ldemul.@OBJEXT@ ldver.@OBJEXT@ ldmisc.@OBJEXT@ \
|
||||
ldfile.@OBJEXT@ ldcref.@OBJEXT@ ${EMULATION_OFILES} ${EMUL_EXTRA_OFILES} \
|
||||
ldbuildid.@OBJEXT@
|
||||
ldbuildid.@OBJEXT@ pdb.@OBJEXT@
|
||||
|
||||
STAGESTUFF = *.@OBJEXT@ ldscripts/* e*.c
|
||||
SRC_POTFILES = $(CFILES) $(HFILES)
|
||||
|
@ -1010,7 +1012,7 @@ EXTRA_ld_new_SOURCES = deffilep.y ldlex.l pep-dll.c pe-dll.c ldelf.c \
|
|||
$(ALL_64_EMULATION_SOURCES)
|
||||
ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmain.c \
|
||||
ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c plugin.c \
|
||||
ldbuildid.c
|
||||
ldbuildid.c pdb.c
|
||||
|
||||
ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) \
|
||||
$(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL_DEP) $(JANSSON_LIBS)
|
||||
|
@ -1575,6 +1577,7 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libldtestplug4_la-testplug4.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libldtestplug_la-testplug.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mri.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdb.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pe-dll.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pep-dll.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Po@am__quote@
|
||||
|
|
|
@ -66,6 +66,7 @@ fragment <<EOF
|
|||
#include "ldctor.h"
|
||||
#include "ldbuildid.h"
|
||||
#include "coff/internal.h"
|
||||
#include "pdb.h"
|
||||
|
||||
/* FIXME: See bfd/peXXigen.c for why we include an architecture specific
|
||||
header in generic PE code. */
|
||||
|
@ -1353,6 +1354,12 @@ write_build_id (bfd *abfd)
|
|||
if (bfd_bwrite (contents, size, abfd) != size)
|
||||
return 0;
|
||||
|
||||
if (pdb)
|
||||
{
|
||||
if (!create_pdb_file (abfd, pdb_name, build_id))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Construct the CodeView record. */
|
||||
CODEVIEW_INFO cvinfo;
|
||||
cvinfo.CVSignature = CVINFO_PDB70_CVSIGNATURE;
|
||||
|
|
|
@ -73,6 +73,7 @@ fragment <<EOF
|
|||
#include "ldctor.h"
|
||||
#include "ldbuildid.h"
|
||||
#include "coff/internal.h"
|
||||
#include "pdb.h"
|
||||
|
||||
/* FIXME: See bfd/peXXigen.c for why we include an architecture specific
|
||||
header in generic PE code. */
|
||||
|
@ -1345,6 +1346,12 @@ write_build_id (bfd *abfd)
|
|||
if (bfd_bwrite (contents, size, abfd) != size)
|
||||
return 0;
|
||||
|
||||
if (pdb)
|
||||
{
|
||||
if (!create_pdb_file (abfd, pdb_name, build_id))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Construct the CodeView record. */
|
||||
CODEVIEW_INFO cvinfo;
|
||||
cvinfo.CVSignature = CVINFO_PDB70_CVSIGNATURE;
|
||||
|
|
522
ld/pdb.c
Normal file
522
ld/pdb.c
Normal file
|
@ -0,0 +1,522 @@
|
|||
/* 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;
|
||||
}
|
||||
|
||||
/* Stream 4 is the debug information (DBI) stream. */
|
||||
static bool
|
||||
populate_dbi_stream (bfd *stream, bfd *abfd)
|
||||
{
|
||||
struct pdb_dbi_stream_header h;
|
||||
struct optional_dbg_header opt;
|
||||
|
||||
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
|
||||
bfd_putl16 (0xffff, &h.public_stream_index);
|
||||
bfd_putl16 (0, &h.pdb_dll_version);
|
||||
bfd_putl16 (0xffff, &h.sym_record_stream);
|
||||
bfd_putl16 (0, &h.pdb_dll_rbld);
|
||||
bfd_putl32 (0, &h.mod_info_size);
|
||||
bfd_putl32 (0, &h.section_contribution_size);
|
||||
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))
|
||||
return false;
|
||||
|
||||
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);
|
||||
bfd_putl16 (0xffff, &opt.section_header_stream);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
bfd *info_stream, *dbi_stream, *names_stream;
|
||||
|
||||
pdb = bfd_openw (pdb_name, "pdb");
|
||||
if (!pdb)
|
||||
{
|
||||
einfo (_("%P: warning: cannot create PDB file: %s\n"),
|
||||
bfd_errmsg (bfd_get_error ()));
|
||||
return false;
|
||||
}
|
||||
|
||||
bfd_set_format (pdb, bfd_archive);
|
||||
|
||||
if (!create_old_directory_stream (pdb))
|
||||
{
|
||||
einfo (_("%P: warning: cannot create old directory stream "
|
||||
"in PDB file: %s\n"), bfd_errmsg (bfd_get_error ()));
|
||||
goto end;
|
||||
}
|
||||
|
||||
info_stream = add_stream (pdb, NULL, NULL);
|
||||
|
||||
if (!info_stream)
|
||||
{
|
||||
einfo (_("%P: warning: cannot create info stream "
|
||||
"in PDB file: %s\n"), bfd_errmsg (bfd_get_error ()));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!create_type_stream (pdb))
|
||||
{
|
||||
einfo (_("%P: warning: cannot create TPI stream "
|
||||
"in PDB file: %s\n"), bfd_errmsg (bfd_get_error ()));
|
||||
goto end;
|
||||
}
|
||||
|
||||
dbi_stream = add_stream (pdb, NULL, NULL);
|
||||
|
||||
if (!dbi_stream)
|
||||
{
|
||||
einfo (_("%P: warning: cannot create DBI stream "
|
||||
"in PDB file: %s\n"), bfd_errmsg (bfd_get_error ()));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!create_type_stream (pdb))
|
||||
{
|
||||
einfo (_("%P: warning: cannot create IPI stream "
|
||||
"in PDB file: %s\n"), bfd_errmsg (bfd_get_error ()));
|
||||
goto end;
|
||||
}
|
||||
|
||||
names_stream = add_stream (pdb, "/names", NULL);
|
||||
|
||||
if (!names_stream)
|
||||
{
|
||||
einfo (_("%P: warning: cannot create /names stream "
|
||||
"in PDB file: %s\n"), bfd_errmsg (bfd_get_error ()));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!populate_dbi_stream (dbi_stream, abfd))
|
||||
{
|
||||
einfo (_("%P: warning: cannot populate DBI stream "
|
||||
"in PDB file: %s\n"), bfd_errmsg (bfd_get_error ()));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!populate_info_stream (pdb, info_stream, guid))
|
||||
{
|
||||
einfo (_("%P: warning: cannot populate info stream "
|
||||
"in PDB file: %s\n"), bfd_errmsg (bfd_get_error ()));
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = true;
|
||||
|
||||
end:
|
||||
bfd_close (pdb);
|
||||
|
||||
return ret;
|
||||
}
|
111
ld/pdb.h
Normal file
111
ld/pdb.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
/* pdb.h - header file 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. */
|
||||
|
||||
/* Header files referred to below can be found in Microsoft's PDB
|
||||
repository: https://github.com/microsoft/microsoft-pdb. */
|
||||
|
||||
#ifndef PDB_H
|
||||
#define PDB_H
|
||||
|
||||
#include "sysdep.h"
|
||||
#include "bfd.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
/* PDBStream70 in pdb1.h */
|
||||
struct pdb_stream_70
|
||||
{
|
||||
uint32_t version;
|
||||
uint32_t signature;
|
||||
uint32_t age;
|
||||
uint8_t guid[16];
|
||||
};
|
||||
|
||||
#define PDB_STREAM_VERSION_VC70 20000404
|
||||
#define PDB_STREAM_VERSION_VC140 20140508
|
||||
|
||||
/* HDR in tpi.h */
|
||||
struct pdb_tpi_stream_header
|
||||
{
|
||||
uint32_t version;
|
||||
uint32_t header_size;
|
||||
uint32_t type_index_begin;
|
||||
uint32_t type_index_end;
|
||||
uint32_t type_record_bytes;
|
||||
uint16_t hash_stream_index;
|
||||
uint16_t hash_aux_stream_index;
|
||||
uint32_t hash_key_size;
|
||||
uint32_t num_hash_buckets;
|
||||
uint32_t hash_value_buffer_offset;
|
||||
uint32_t hash_value_buffer_length;
|
||||
uint32_t index_offset_buffer_offset;
|
||||
uint32_t index_offset_buffer_length;
|
||||
uint32_t hash_adj_buffer_offset;
|
||||
uint32_t hash_adj_buffer_length;
|
||||
};
|
||||
|
||||
#define TPI_STREAM_VERSION_80 20040203
|
||||
|
||||
#define TPI_FIRST_INDEX 0x1000
|
||||
|
||||
/* NewDBIHdr in dbi.h */
|
||||
struct pdb_dbi_stream_header
|
||||
{
|
||||
uint32_t version_signature;
|
||||
uint32_t version_header;
|
||||
uint32_t age;
|
||||
uint16_t global_stream_index;
|
||||
uint16_t build_number;
|
||||
uint16_t public_stream_index;
|
||||
uint16_t pdb_dll_version;
|
||||
uint16_t sym_record_stream;
|
||||
uint16_t pdb_dll_rbld;
|
||||
uint32_t mod_info_size;
|
||||
uint32_t section_contribution_size;
|
||||
uint32_t section_map_size;
|
||||
uint32_t source_info_size;
|
||||
uint32_t type_server_map_size;
|
||||
uint32_t mfc_type_server_index;
|
||||
uint32_t optional_dbg_header_size;
|
||||
uint32_t ec_substream_size;
|
||||
uint16_t flags;
|
||||
uint16_t machine;
|
||||
uint32_t padding;
|
||||
};
|
||||
|
||||
#define DBI_STREAM_VERSION_70 19990903
|
||||
|
||||
struct optional_dbg_header
|
||||
{
|
||||
uint16_t fpo_stream;
|
||||
uint16_t exception_stream;
|
||||
uint16_t fixup_stream;
|
||||
uint16_t omap_to_src_stream;
|
||||
uint16_t omap_from_src_stream;
|
||||
uint16_t section_header_stream;
|
||||
uint16_t token_map_stream;
|
||||
uint16_t xdata_stream;
|
||||
uint16_t pdata_stream;
|
||||
uint16_t new_fpo_stream;
|
||||
uint16_t orig_section_header_stream;
|
||||
};
|
||||
|
||||
extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
|
||||
|
||||
#endif
|
|
@ -31,6 +31,8 @@ ldwrite.h
|
|||
lexsup.c
|
||||
mri.c
|
||||
mri.h
|
||||
pdb.c
|
||||
pdb.h
|
||||
pe-dll.c
|
||||
pe-dll.h
|
||||
pep-dll.c
|
||||
|
|
|
@ -35,6 +35,249 @@ proc get_pdb_name { pe } {
|
|||
return $pdb
|
||||
}
|
||||
|
||||
proc get_pdb_guid { pe } {
|
||||
global OBJDUMP
|
||||
|
||||
set exec_output [run_host_cmd "$OBJDUMP" "-p $pe"]
|
||||
|
||||
if ![regexp -line "^\\(format RSDS signature (\[0-9a-fA-F\]{32}) age 1 pdb (.*)\\)$" $exec_output full sig pdb] {
|
||||
return ""
|
||||
}
|
||||
|
||||
return $sig
|
||||
}
|
||||
|
||||
proc check_pdb_info_stream { pdb guid } {
|
||||
global ar
|
||||
|
||||
set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0001"]
|
||||
|
||||
if ![string match "" $exec_output] {
|
||||
return 0
|
||||
}
|
||||
|
||||
set fi [open tmpdir/0001]
|
||||
fconfigure $fi -translation binary
|
||||
|
||||
# check version
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i version
|
||||
|
||||
if { $version != 20000404 } {
|
||||
close $fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# skip signature (timestamp)
|
||||
read $fi 4
|
||||
|
||||
# check age
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i age
|
||||
|
||||
if { $age != 1 } {
|
||||
close $fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# check GUID
|
||||
|
||||
set data [read $fi 16]
|
||||
binary scan $data H2H2H2H2H2H2H2H2H* guid1 guid2 guid3 guid4 guid5 guid6 guid7 guid8 guid9
|
||||
|
||||
set data "$guid4$guid3$guid2$guid1$guid6$guid5$guid8$guid7$guid9"
|
||||
|
||||
if { $data ne $guid } {
|
||||
close $fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# skip names string
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i names_length
|
||||
read $fi $names_length
|
||||
|
||||
# read number of names entries
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i num_entries
|
||||
|
||||
# skip number of buckets
|
||||
read $fi 4
|
||||
|
||||
# skip present bitmap
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i bitmap_length
|
||||
read $fi [expr $bitmap_length * 4]
|
||||
|
||||
# skip deleted bitmap
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i bitmap_length
|
||||
read $fi [expr $bitmap_length * 4]
|
||||
|
||||
# skip names entries
|
||||
read $fi [expr $num_entries * 8]
|
||||
|
||||
# skip uint32_t
|
||||
read $fi 4
|
||||
|
||||
# read second version
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i version2
|
||||
|
||||
if { $version2 != 20140508 } {
|
||||
close $fi
|
||||
return 0
|
||||
}
|
||||
|
||||
close $fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
proc check_type_stream { pdb stream } {
|
||||
global ar
|
||||
|
||||
set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb $stream"]
|
||||
|
||||
if ![string match "" $exec_output] {
|
||||
return 0
|
||||
}
|
||||
|
||||
set fi [open tmpdir/$stream]
|
||||
fconfigure $fi -translation binary
|
||||
|
||||
# check version
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i version
|
||||
|
||||
if { $version != 20040203 } {
|
||||
close $fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# check header size
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i header_size
|
||||
|
||||
if { $header_size != 0x38 } {
|
||||
close $fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# skip type_index_begin and type_index_end
|
||||
read $fi 8
|
||||
|
||||
# read type_record_bytes
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i type_record_bytes
|
||||
|
||||
close $fi
|
||||
|
||||
# check stream length
|
||||
|
||||
set stream_length [file size tmpdir/$stream]
|
||||
|
||||
if { $stream_length != [ expr $header_size + $type_record_bytes ] } {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
proc check_dbi_stream { pdb } {
|
||||
global ar
|
||||
|
||||
set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0003"]
|
||||
|
||||
if ![string match "" $exec_output] {
|
||||
return 0
|
||||
}
|
||||
|
||||
set fi [open tmpdir/0003]
|
||||
fconfigure $fi -translation binary
|
||||
|
||||
# check signature
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i signature
|
||||
|
||||
if { $signature != -1 } {
|
||||
close $fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# check version
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i version
|
||||
|
||||
if { $version != 19990903 } {
|
||||
close $fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# check age
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i age
|
||||
|
||||
if { $age != 1 } {
|
||||
close $fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# skip fields
|
||||
read $fi 12
|
||||
|
||||
# read substream sizes
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i mod_info_size
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i section_contribution_size
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i section_map_size
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i source_info_size
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i type_server_map_size
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i mfc_type_server_index
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i optional_dbg_header_size
|
||||
|
||||
set data [read $fi 4]
|
||||
binary scan $data i ec_substream_size
|
||||
|
||||
close $fi
|
||||
|
||||
# check stream length
|
||||
|
||||
set stream_length [file size tmpdir/0003]
|
||||
|
||||
if { $stream_length != [expr 0x40 + $mod_info_size + $section_contribution_size + $section_map_size + $source_info_size + $type_server_map_size + $mfc_type_server_index + $optional_dbg_header_size + $ec_substream_size] } {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
if ![ld_assemble $as $srcdir/$subdir/pdb1.s tmpdir/pdb1.o] {
|
||||
unsupported "Build pdb1.o"
|
||||
return
|
||||
|
@ -51,3 +294,27 @@ if ![string equal [get_pdb_name "tmpdir/pdb1.exe"] "pdb1.pdb"] {
|
|||
}
|
||||
|
||||
pass "PDB filename present in CodeView debug info"
|
||||
|
||||
if [check_pdb_info_stream tmpdir/pdb1.pdb [get_pdb_guid "tmpdir/pdb1.exe"]] {
|
||||
pass "Valid PDB info stream"
|
||||
} else {
|
||||
fail "Invalid PDB info stream"
|
||||
}
|
||||
|
||||
if [check_type_stream tmpdir/pdb1.pdb "0002"] {
|
||||
pass "Valid TPI stream"
|
||||
} else {
|
||||
fail "Invalid TPI stream"
|
||||
}
|
||||
|
||||
if [check_type_stream tmpdir/pdb1.pdb "0004"] {
|
||||
pass "Valid IPI stream"
|
||||
} else {
|
||||
fail "Invalid IPI stream"
|
||||
}
|
||||
|
||||
if [check_dbi_stream tmpdir/pdb1.pdb] {
|
||||
pass "Valid DBI stream"
|
||||
} else {
|
||||
fail "Invalid DBI stream"
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue