From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wr1-x435.google.com (mail-wr1-x435.google.com [IPv6:2a00:1450:4864:20::435]) by sourceware.org (Postfix) with ESMTPS id EC75E3858C54 for ; Wed, 2 Nov 2022 02:07:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org EC75E3858C54 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=harmstone.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wr1-x435.google.com with SMTP id bs21so22550215wrb.4 for ; Tue, 01 Nov 2022 19:07:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:sender:from:to:cc:subject:date:message-id:reply-to; bh=cPMiRINIuRC7x37JihRxsrjgZAQlDcScC8jXx91p8PY=; b=V5G/2hzXTWX9EQ6wyR2CIAI5SH5TR7V0PmabXhYKLIEc88xQOsuGlt67+rKJkXYau0 ASLChuClpsHphiOlwkvhBVVQWfK8njI80CdFegqPKFxL21x2SrtXg7u35Xuw/P+FtNYv M8+ZECN2jiEwQAKIBrKcEdNTgMvJ3+pY2zeJJS7r12ha8bOX+M1fwxOzn/BgevyBER0q cb6jpa7vNS2GGWX1uqQJzvNCyqVD+JRlRYITCYRzyVYUelUmkw1MG/TVq0zwy3SgpxPZ Ts6RiYxzl79yvCxsQvUVaGTsYc3r2n4dP4ISbPT75322I7hKPhDC5NySidQ2ZU+b2ovJ dudg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:sender:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=cPMiRINIuRC7x37JihRxsrjgZAQlDcScC8jXx91p8PY=; b=PnmGatAD0NIypfXvXWbuZcAQkNXJQfJFrzYl6PDTmEtQcXkvmHnmoruexYQvgHMaEG DcNKe+cD621G/BDpgzJXGTTwzlfCO7FdHad7sAe8bkuLUHaRZXC3JxEGMyUTnMHlkFcx 68n9jAnFCAvvj36uxbG/vt361KxHi3QJ2pEUVBsWX6GHeaR8tqWMADNElnSuXoLoZ+Zz aE+/xBdNU+xP5s8MQ6s9rpbwkwWaLqD1361vVcOe5Y0s2Ju2+cWL70n/tR/kQygd28C0 cRCynigP/fg0bN8FO52XLAwJ1toa0kSOzz+IkVP4ADuQ1t7FiACZSLzRbkFahOUBBrfE EsGA== X-Gm-Message-State: ACrzQf0H3h7fNGZlEwxlKcafStb4Kq6d0TBim39qZ4jem2ZHjTA5VL2j e8nzR4zuAzXNZZkiDK6R6LPLW7V2MpI= X-Google-Smtp-Source: AMsMyM4FiJ4C01tBYEneM4e4T2K9pTKHoPV2yDIwmpu9ahBmFRPRNqGHxnDl9CbbznclCWIneX7knQ== X-Received: by 2002:a05:6000:142:b0:236:839f:927b with SMTP id r2-20020a056000014200b00236839f927bmr13297478wrx.554.1667354876141; Tue, 01 Nov 2022 19:07:56 -0700 (PDT) Received: from beren.harmstone.com ([2a02:8010:64ea:0:8eb8:7eff:fe53:9d5f]) by smtp.gmail.com with ESMTPSA id g4-20020a5d5544000000b002366fb99cdasm11366152wrw.50.2022.11.01.19.07.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 01 Nov 2022 19:07:55 -0700 (PDT) Sender: Mark Harmstone From: Mark Harmstone To: binutils@sourceware.org Cc: Mark Harmstone Subject: [PATCH] ld: Add module information substream to PDB files Date: Wed, 2 Nov 2022 02:07:52 +0000 Message-Id: <20221102020752.24441-1-mark@harmstone.com> X-Mailer: git-send-email 2.37.4 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_EF,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM,GIT_PATCH_0,HEADER_FROM_DIFFERENT_DOMAINS,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: This adds the module information substream to the DBI stream in PDB files, which among other things contains the filenames of the input object files. --- ld/pdb.c | 214 ++++++++++++++++++++++++++++++++++++- ld/pdb.h | 33 ++++++ ld/testsuite/ld-pe/pdb.exp | 214 ++++++++++++++++++++++++++++++------- ld/testsuite/ld-pe/pdb2a.s | 5 + ld/testsuite/ld-pe/pdb2b.s | 5 + 5 files changed, 427 insertions(+), 44 deletions(-) create mode 100644 ld/testsuite/ld-pe/pdb2a.s create mode 100644 ld/testsuite/ld-pe/pdb2b.s diff --git a/ld/pdb.c b/ld/pdb.c index a2cdb84e271..a56dffc2a19 100644 --- a/ld/pdb.c +++ b/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 " diff --git a/ld/pdb.h b/ld/pdb.h index 1a80101d288..a44618578b7 100644 --- a/ld/pdb.h +++ b/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 diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp index ee314c41f9b..ab22506d0f2 100644 --- a/ld/testsuite/ld-pe/pdb.exp +++ b/ld/testsuite/ld-pe/pdb.exp @@ -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_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 ![ld_assemble $as $srcdir/$subdir/pdb1.s tmpdir/pdb1.o] { + unsupported "Build pdb1.o" + return + } -if ![string equal [get_pdb_name "tmpdir/pdb1.exe"] "pdb1.pdb"] { - fail "PDB filename not found in CodeView debug info" - 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 + } -pass "PDB filename present in CodeView debug info" + if ![string equal [get_pdb_name "tmpdir/pdb1.exe"] "pdb1.pdb"] { + fail "PDB filename not found in CodeView debug info" + return + } -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" -} + pass "PDB filename present in CodeView debug info" -if [check_type_stream tmpdir/pdb1.pdb "0002"] { - pass "Valid TPI stream" -} else { - fail "Invalid TPI stream" -} + 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 "0004"] { - pass "Valid IPI stream" -} else { - fail "Invalid IPI 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_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 [check_section_stream tmpdir/pdb1.exe tmpdir/pdb1.pdb] { - pass "Valid section stream" -} else { - fail "Invalid section stream" +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 [check_publics_stream tmpdir/pdb1.pdb] { - pass "Valid publics stream" -} else { - fail "Invalid publics stream" +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 } + +test1 +test2 diff --git a/ld/testsuite/ld-pe/pdb2a.s b/ld/testsuite/ld-pe/pdb2a.s new file mode 100644 index 00000000000..414edeb2eec --- /dev/null +++ b/ld/testsuite/ld-pe/pdb2a.s @@ -0,0 +1,5 @@ +.text + +.global foo +foo: + .secrel32 bar diff --git a/ld/testsuite/ld-pe/pdb2b.s b/ld/testsuite/ld-pe/pdb2b.s new file mode 100644 index 00000000000..64010237975 --- /dev/null +++ b/ld/testsuite/ld-pe/pdb2b.s @@ -0,0 +1,5 @@ +.text + +.global bar +bar: + .long 0x12345678 -- 2.37.4