ld: Add module information substream to PDB files
This commit is contained in:
parent
76a95facf1
commit
5967ca921c
214
ld/pdb.c
214
ld/pdb.c
@ -383,6 +383,196 @@ get_arch_number (bfd *abfd)
|
||||
return IMAGE_FILE_MACHINE_I386;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Return the index of a given output section. */
|
||||
static uint16_t
|
||||
find_section_number (bfd *abfd, asection *sect)
|
||||
@ -404,13 +594,18 @@ find_section_number (bfd *abfd, asection *sect)
|
||||
|
||||
/* Stream 4 is the debug information (DBI) stream. */
|
||||
static bool
|
||||
populate_dbi_stream (bfd *stream, bfd *abfd,
|
||||
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)
|
||||
{
|
||||
struct pdb_dbi_stream_header h;
|
||||
struct optional_dbg_header opt;
|
||||
void *mod_info;
|
||||
uint32_t mod_info_size;
|
||||
|
||||
if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size))
|
||||
return false;
|
||||
|
||||
bfd_putl32 (0xffffffff, &h.version_signature);
|
||||
bfd_putl32 (DBI_STREAM_VERSION_70, &h.version_header);
|
||||
@ -421,7 +616,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd,
|
||||
bfd_putl16 (0, &h.pdb_dll_version);
|
||||
bfd_putl16 (sym_rec_stream_num, &h.sym_record_stream);
|
||||
bfd_putl16 (0, &h.pdb_dll_rbld);
|
||||
bfd_putl32 (0, &h.mod_info_size);
|
||||
bfd_putl32 (mod_info_size, &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);
|
||||
@ -434,7 +629,18 @@ populate_dbi_stream (bfd *stream, bfd *abfd,
|
||||
bfd_putl32 (0, &h.padding);
|
||||
|
||||
if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
|
||||
return false;
|
||||
{
|
||||
free (mod_info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bfd_bwrite (mod_info, mod_info_size, stream) != mod_info_size)
|
||||
{
|
||||
free (mod_info);
|
||||
return false;
|
||||
}
|
||||
|
||||
free (mod_info);
|
||||
|
||||
bfd_putl16 (0xffff, &opt.fpo_stream);
|
||||
bfd_putl16 (0xffff, &opt.exception_stream);
|
||||
@ -888,7 +1094,7 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!populate_dbi_stream (dbi_stream, abfd, section_header_stream_num,
|
||||
if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num,
|
||||
sym_rec_stream_num, publics_stream_num))
|
||||
{
|
||||
einfo (_("%P: warning: cannot populate DBI stream "
|
||||
|
33
ld/pdb.h
33
ld/pdb.h
@ -153,6 +153,39 @@ struct optional_dbg_header
|
||||
uint16_t orig_section_header_stream;
|
||||
};
|
||||
|
||||
#define CV_SIGNATURE_C13 4
|
||||
|
||||
/* SC in dbicommon.h */
|
||||
struct section_contribution
|
||||
{
|
||||
uint16_t section;
|
||||
uint16_t padding1;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t characteristics;
|
||||
uint16_t module_index;
|
||||
uint16_t padding2;
|
||||
uint32_t data_crc;
|
||||
uint32_t reloc_crc;
|
||||
};
|
||||
|
||||
/* MODI_60_Persist in dbi.h */
|
||||
struct module_info
|
||||
{
|
||||
uint32_t unused1;
|
||||
struct section_contribution sc;
|
||||
uint16_t flags;
|
||||
uint16_t module_sym_stream;
|
||||
uint32_t sym_byte_size;
|
||||
uint32_t c11_byte_size;
|
||||
uint32_t c13_byte_size;
|
||||
uint16_t source_file_count;
|
||||
uint16_t padding;
|
||||
uint32_t unused2;
|
||||
uint32_t source_file_name_index;
|
||||
uint32_t pdb_file_path_name_index;
|
||||
};
|
||||
|
||||
extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
|
||||
|
||||
#endif
|
||||
|
@ -494,55 +494,189 @@ proc check_publics_stream { pdb } {
|
||||
return 1
|
||||
}
|
||||
|
||||
if ![ld_assemble $as $srcdir/$subdir/pdb1.s tmpdir/pdb1.o] {
|
||||
unsupported "Build pdb1.o"
|
||||
return
|
||||
proc test1 { } {
|
||||
global as
|
||||
global ld
|
||||
global srcdir
|
||||
global subdir
|
||||
|
||||
if ![ld_assemble $as $srcdir/$subdir/pdb1.s tmpdir/pdb1.o] {
|
||||
unsupported "Build pdb1.o"
|
||||
return
|
||||
}
|
||||
|
||||
if ![ld_link $ld "tmpdir/pdb1.exe" "--pdb=tmpdir/pdb1.pdb --gc-sections -e foo tmpdir/pdb1.o"] {
|
||||
fail "Could not create a PE image with a PDB file"
|
||||
return
|
||||
}
|
||||
|
||||
if ![string equal [get_pdb_name "tmpdir/pdb1.exe"] "pdb1.pdb"] {
|
||||
fail "PDB filename not found in CodeView debug info"
|
||||
return
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
if [check_section_stream tmpdir/pdb1.exe tmpdir/pdb1.pdb] {
|
||||
pass "Valid section stream"
|
||||
} else {
|
||||
fail "Invalid section stream"
|
||||
}
|
||||
|
||||
if [check_publics_stream tmpdir/pdb1.pdb] {
|
||||
pass "Valid publics stream"
|
||||
} else {
|
||||
fail "Invalid publics stream"
|
||||
}
|
||||
}
|
||||
|
||||
if ![ld_link $ld "tmpdir/pdb1.exe" "--pdb=tmpdir/pdb1.pdb --gc-sections -e foo tmpdir/pdb1.o"] {
|
||||
fail "Could not create a PE image with a PDB file"
|
||||
return
|
||||
proc test_mod_info { mod_info } {
|
||||
# check filenames in mod_info
|
||||
|
||||
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 [string match "*pdb2a.o" $obj1] {
|
||||
pass "Correct name for first object file"
|
||||
} else {
|
||||
fail "Incorrect name for first object file"
|
||||
}
|
||||
|
||||
if [string equal $obj1 $ar1] {
|
||||
pass "Correct archive name for first object file"
|
||||
} else {
|
||||
fail "Incorrect archive name for first object file"
|
||||
}
|
||||
|
||||
if { [expr $off % 4] != 0 } {
|
||||
set off [expr $off + 4 - ($off % 4)]
|
||||
}
|
||||
|
||||
incr off 64
|
||||
|
||||
set obj2 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
|
||||
incr off [expr [string length $obj2] + 1]
|
||||
|
||||
set ar2 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
|
||||
incr off [expr [string length $ar2] + 1]
|
||||
|
||||
if [string match "*pdb2b.o" $obj2] {
|
||||
pass "Correct name for second object file"
|
||||
} else {
|
||||
fail "Incorrect name for second object file"
|
||||
}
|
||||
|
||||
if [string match "*pdb2b.a" $ar2] {
|
||||
pass "Correct archive name for second object file"
|
||||
} else {
|
||||
fail "Incorrect archive name for second object file"
|
||||
}
|
||||
|
||||
if { [expr $off % 4] != 0 } {
|
||||
set off [expr $off + 4 - ($off % 4)]
|
||||
}
|
||||
|
||||
incr off 64
|
||||
|
||||
set obj3 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
|
||||
incr off [expr [string length $obj3] + 1]
|
||||
|
||||
set ar3 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
|
||||
incr off [expr [string length $ar3] + 1]
|
||||
|
||||
if [string equal $obj3 "* Linker *"] {
|
||||
pass "Correct name for dummy object file"
|
||||
} else {
|
||||
fail "Incorrect name for dummy object file"
|
||||
}
|
||||
|
||||
if [string equal $ar3 ""] {
|
||||
pass "Correct archive name for dummy object file"
|
||||
} else {
|
||||
fail "Incorrect archive name for dummy object file"
|
||||
}
|
||||
}
|
||||
|
||||
if ![string equal [get_pdb_name "tmpdir/pdb1.exe"] "pdb1.pdb"] {
|
||||
fail "PDB filename not found in CodeView debug info"
|
||||
return
|
||||
proc test2 { } {
|
||||
global as
|
||||
global ar
|
||||
global ld
|
||||
global srcdir
|
||||
global subdir
|
||||
|
||||
if ![ld_assemble $as $srcdir/$subdir/pdb2a.s tmpdir/pdb2a.o] {
|
||||
unsupported "Build pdb2a.o"
|
||||
return
|
||||
}
|
||||
|
||||
if ![ld_assemble $as $srcdir/$subdir/pdb2b.s tmpdir/pdb2b.o] {
|
||||
unsupported "Build pdb2b.o"
|
||||
return
|
||||
}
|
||||
|
||||
set exec_output [run_host_cmd "$ar" "cr tmpdir/pdb2b.a tmpdir/pdb2b.o"]
|
||||
|
||||
if ![string match "" $exec_output] {
|
||||
unsupported "Create pdb2b.a"
|
||||
return
|
||||
}
|
||||
|
||||
if ![ld_link $ld "tmpdir/pdb2.exe" "--pdb=tmpdir/pdb2.pdb -e foo tmpdir/pdb2a.o tmpdir/pdb2b.a"] {
|
||||
unsupported "Create PE image with PDB file"
|
||||
return
|
||||
}
|
||||
|
||||
set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb2.pdb 0003"]
|
||||
|
||||
if ![string match "" $exec_output] {
|
||||
return 0
|
||||
}
|
||||
|
||||
set fi [open tmpdir/0003]
|
||||
fconfigure $fi -translation binary
|
||||
|
||||
seek $fi 24
|
||||
|
||||
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
|
||||
|
||||
test_mod_info $mod_info
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
if [check_section_stream tmpdir/pdb1.exe tmpdir/pdb1.pdb] {
|
||||
pass "Valid section stream"
|
||||
} else {
|
||||
fail "Invalid section stream"
|
||||
}
|
||||
|
||||
if [check_publics_stream tmpdir/pdb1.pdb] {
|
||||
pass "Valid publics stream"
|
||||
} else {
|
||||
fail "Invalid publics stream"
|
||||
}
|
||||
test1
|
||||
test2
|
||||
|
5
ld/testsuite/ld-pe/pdb2a.s
Normal file
5
ld/testsuite/ld-pe/pdb2a.s
Normal file
@ -0,0 +1,5 @@
|
||||
.text
|
||||
|
||||
.global foo
|
||||
foo:
|
||||
.secrel32 bar
|
5
ld/testsuite/ld-pe/pdb2b.s
Normal file
5
ld/testsuite/ld-pe/pdb2b.s
Normal file
@ -0,0 +1,5 @@
|
||||
.text
|
||||
|
||||
.global bar
|
||||
bar:
|
||||
.long 0x12345678
|
Loading…
x
Reference in New Issue
Block a user