From patchwork Sun Oct 16 23:24:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Harmstone X-Patchwork-Id: 3152 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:4ac7:0:0:0:0:0 with SMTP id y7csp1188500wrs; Sun, 16 Oct 2022 16:25:13 -0700 (PDT) X-Google-Smtp-Source: AMsMyM5QAhXwmX72iwhMfWP42RT2OgDq5/Xb3Ze9YL0AWMXFKKkz4VGNESgpjMuQy0gCfN9HcHh3 X-Received: by 2002:a17:907:270f:b0:78d:728b:ff4 with SMTP id w15-20020a170907270f00b0078d728b0ff4mr6573295ejk.501.1665962713281; Sun, 16 Oct 2022 16:25:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1665962713; cv=none; d=google.com; s=arc-20160816; b=T/rLi0PFXAhl4Yt9iPc3S7JkzYEO5jlDEFeDERNQ5A1YZQkBJrBaZtGT65zsBLj8Jb GL2y7RXXyyGuxtdX5XqJtke3whhBZvZrgW3JH5FOP+XnxQ9zkrs42bwPiHwtuC5ZNuKn fn+9lPBrA7X33mvNoHeu7yhcQhN+4+2uZeO71gI1JA25tiQq7JPudne6Y2StDvVhc3WC ueilnpkIE0Aexi0c7J6MHJgM2NcEsz3TI0//iyw8py9hHi0K7a+vphzjFFTyXrmZO/ow P7cveFoZkYnIk2xhq1mpGGtpx00kSJs9WZ4hYzF8Gi/tw9c5uePYXULbO8u0tBRmxs0Y r5sQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:message-id:date:subject:to:from:dkim-signature :dmarc-filter:delivered-to; bh=+l9FiUo76nLolHaEub4TomNciYXAousmxjGyBXejQrE=; b=dJxSeYiMomTBXo4zdHntIiAXjd8Vi8k4NNl1j3yUH8HEJ7ozHDBWSh4sYb2vL7pzDy nN99KAR+H31UGSuULBWG4zYDX6JWhVUmVVHWYE48tME+rQxVIbgTu3eH7khaSnXHMQ96 emoR1LBz/F+qPvb6SqekGtdSjoPozBxGiuLkAYpJZJzIrtKBKa567UfrGqyvVI6gHsQ7 e6PyWuEyKk0n5OLfeQX2zxi55kbBFbMYieAXqifiwa7eYkd5Pw8SQNRkOEZxELr4IBvq z+H7u2U1GAhrtkBNkvuHYw5UYqN666uKpgdUKaxud+z7i7ZHOiEB1qqw1lMkfzgP3CWS AtvA== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20210112 header.b=gC4CXnwE; spf=pass (google.com: domain of binutils-bounces+ouuuleilei=gmail.com@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="binutils-bounces+ouuuleilei=gmail.com@sourceware.org" Received: from sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id ne40-20020a1709077ba800b0078da42195c2si7650753ejc.547.2022.10.16.16.25.12 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 Oct 2022 16:25:13 -0700 (PDT) Received-SPF: pass (google.com: domain of binutils-bounces+ouuuleilei=gmail.com@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=fail header.i=@gmail.com header.s=20210112 header.b=gC4CXnwE; spf=pass (google.com: domain of binutils-bounces+ouuuleilei=gmail.com@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="binutils-bounces+ouuuleilei=gmail.com@sourceware.org" Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2FD353857428 for ; Sun, 16 Oct 2022 23:24:57 +0000 (GMT) X-Original-To: binutils@sourceware.org Delivered-To: binutils@sourceware.org Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by sourceware.org (Postfix) with ESMTPS id 78741385828B for ; Sun, 16 Oct 2022 23:24:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 78741385828B 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-wm1-x32e.google.com with SMTP id r3-20020a05600c35c300b003b4b5f6c6bdso8769906wmq.2 for ; Sun, 16 Oct 2022 16:24:36 -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=+l9FiUo76nLolHaEub4TomNciYXAousmxjGyBXejQrE=; b=gC4CXnwE8cT3POgNs01i2PDin2Rjmg3jYlLeayXTa6o+phR9s7COrQDPK2WXm5Omgp YRLaYfcpC1TziYO18IH229+VnJPHHYnnokHQeSu8EsXr7KBjF86XnZUFckHTB9s1HOeb fZ5jNxnr0E2lDnZp5yUiNK6mm8hdJgMlpebF69vAP7WMpvI8dbArkb8CPta/JYseABMe fjA+Ja3IjFviN9Gj9JiVodqCAcDmlAzLCk4oOT96u/AxNYm55i007hzjqeww3g4i0jAu dWK/luNTOV4EFaAUAc+rvXVt2OBLkkbLqYTc6vZw1qSupJPrlnxF6dvgKYKqEF+7QGRB /hOA== 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=+l9FiUo76nLolHaEub4TomNciYXAousmxjGyBXejQrE=; b=wCsNg7GHpdu+3BykvDSB591vaTvroHJVN6HJOIqUsxCPfgsu64yoc72XJzuuomrrrL UrwEiaEx7xETL7+q51CR5uT4cmEPA1SLMmC8m3zNZW22lH/ZjfNhoeGuJkP3S6RPXuPb 9CRcUBvp3k0tpHmgDEOkoWntOK4klA6UIwTs8bI9iAr9gqSwQEfQRenfYcF10iGd7DbI ONYSCJJ5VNn0U2QLF40ZXfq660nUYP64EkoLaNumnoIELUZ/iK9CNANxJ7EAda4b682Z bZz+Jx8faXWNDTekSTX5msaZ8tOWHIXC9o4feNuTZvmUjbm+vvNFkeGbR0yABhTHRQOX pOsQ== X-Gm-Message-State: ACrzQf2AdXPsTNkD/HmDyQswYQp87yXxklXohfQsDD0OswFDzUiZsUwG YNTqiwfYO4IJPDfCB9/QhqwErFt3VLQ= X-Received: by 2002:a05:600c:4e51:b0:3c0:55e0:7719 with SMTP id e17-20020a05600c4e5100b003c055e07719mr5542297wmq.3.1665962674100; Sun, 16 Oct 2022 16:24:34 -0700 (PDT) Received: from beren.harmstone.com ([2a02:8010:64ea:0:8eb8:7eff:fe53:9d5f]) by smtp.gmail.com with ESMTPSA id u5-20020a5d5145000000b00232251d71c7sm7000405wrt.68.2022.10.16.16.24.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 Oct 2022 16:24:33 -0700 (PDT) From: Mark Harmstone To: binutils@sourceware.org Subject: [PATCH v5 1/2] ld: Add --pdb option Date: Mon, 17 Oct 2022 00:24:18 +0100 Message-Id: <20221016232419.1135-1-mark@harmstone.com> X-Mailer: git-send-email 2.35.1 MIME-Version: 1.0 X-Spam-Status: No, score=-11.4 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 X-BeenThere: binutils@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Binutils mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Harmstone Errors-To: binutils-bounces+ouuuleilei=gmail.com@sourceware.org Sender: "Binutils" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1746888518067302539?= X-GMAIL-MSGID: =?utf-8?q?1746888518067302539?= Second patch incorporates fixes for endian and UB issues in calc_hash, as per https://sourceware.org/pipermail/binutils/2022-October/123514.html. --- bfd/libpei.h | 6 +++-- bfd/peXXigen.c | 30 ++++++++++++++++----- bfd/peicode.h | 2 +- ld/emultempl/pe.em | 50 +++++++++++++++++++++++++++++++---- ld/emultempl/pep.em | 48 +++++++++++++++++++++++++++++++--- ld/testsuite/ld-pe/pdb.exp | 53 ++++++++++++++++++++++++++++++++++++++ ld/testsuite/ld-pe/pdb1.s | 5 ++++ 7 files changed, 175 insertions(+), 19 deletions(-) create mode 100644 ld/testsuite/ld-pe/pdb.exp create mode 100644 ld/testsuite/ld-pe/pdb1.s diff --git a/bfd/libpei.h b/bfd/libpei.h index 4aca024192c..8b53bd90e84 100644 --- a/bfd/libpei.h +++ b/bfd/libpei.h @@ -388,9 +388,11 @@ void _bfd_XX_get_symbol_info (bfd *, asymbol *, symbol_info *); bool _bfd_XXi_final_link_postscript (bfd *, struct coff_final_link_info *); void _bfd_XXi_swap_debugdir_in (bfd *, void *, void *); unsigned _bfd_XXi_swap_debugdir_out (bfd *, void *, void *); -unsigned _bfd_XXi_write_codeview_record (bfd *, file_ptr, CODEVIEW_INFO *); +unsigned _bfd_XXi_write_codeview_record + (bfd *, file_ptr, CODEVIEW_INFO *, const char *); CODEVIEW_INFO *_bfd_XXi_slurp_codeview_record - (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo); + (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo, + char **pdb); /* The following are needed only for ONE of pe or pei, but don't otherwise vary; peicode.h fixes up ifdefs but we provide the diff --git a/bfd/peXXigen.c b/bfd/peXXigen.c index 5ab09387e72..8db188ce036 100644 --- a/bfd/peXXigen.c +++ b/bfd/peXXigen.c @@ -1134,7 +1134,8 @@ _bfd_XXi_swap_debugdir_out (bfd * abfd, void * inp, void * extp) } CODEVIEW_INFO * -_bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo) +_bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo, + char **pdb) { char buffer[256+1]; bfd_size_type nread; @@ -1174,6 +1175,9 @@ _bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length cvinfo->SignatureLength = CV_INFO_SIGNATURE_LENGTH; /* cvinfo->PdbFileName = cvinfo70->PdbFileName; */ + if (pdb) + *pdb = xstrdup (cvinfo70->PdbFileName); + return cvinfo; } else if ((cvinfo->CVSignature == CVINFO_PDB20_CVSIGNATURE) @@ -1185,6 +1189,9 @@ _bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length cvinfo->SignatureLength = 4; /* cvinfo->PdbFileName = cvinfo20->PdbFileName; */ + if (pdb) + *pdb = xstrdup (cvinfo20->PdbFileName); + return cvinfo; } @@ -1192,9 +1199,11 @@ _bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length } unsigned int -_bfd_XXi_write_codeview_record (bfd * abfd, file_ptr where, CODEVIEW_INFO *cvinfo) +_bfd_XXi_write_codeview_record (bfd * abfd, file_ptr where, CODEVIEW_INFO *cvinfo, + const char *pdb) { - const bfd_size_type size = sizeof (CV_INFO_PDB70) + 1; + size_t pdb_len = pdb ? strlen (pdb) : 0; + const bfd_size_type size = sizeof (CV_INFO_PDB70) + pdb_len + 1; bfd_size_type written; CV_INFO_PDB70 *cvinfo70; char * buffer; @@ -1217,7 +1226,11 @@ _bfd_XXi_write_codeview_record (bfd * abfd, file_ptr where, CODEVIEW_INFO *cvinf memcpy (&(cvinfo70->Signature[8]), &(cvinfo->Signature[8]), 8); H_PUT_32 (abfd, cvinfo->Age, cvinfo70->Age); - cvinfo70->PdbFileName[0] = '\0'; + + if (pdb == NULL) + cvinfo70->PdbFileName[0] = '\0'; + else + memcpy (cvinfo70->PdbFileName, pdb, pdb_len + 1); written = bfd_bwrite (buffer, size, abfd); @@ -2615,22 +2628,25 @@ pe_print_debugdata (bfd * abfd, void * vfile) We need to use a 32-bit aligned buffer to safely read in a codeview record. */ char buffer[256 + 1] ATTRIBUTE_ALIGNED_ALIGNOF (CODEVIEW_INFO); + char *pdb; CODEVIEW_INFO *cvinfo = (CODEVIEW_INFO *) buffer; /* The debug entry doesn't have to have to be in a section, in which case AddressOfRawData is 0, so always use PointerToRawData. */ if (!_bfd_XXi_slurp_codeview_record (abfd, (file_ptr) idd.PointerToRawData, - idd.SizeOfData, cvinfo)) + idd.SizeOfData, cvinfo, &pdb)) continue; for (j = 0; j < cvinfo->SignatureLength; j++) sprintf (&signature[j*2], "%02x", cvinfo->Signature[j] & 0xff); /* xgettext:c-format */ - fprintf (file, _("(format %c%c%c%c signature %s age %ld)\n"), + fprintf (file, _("(format %c%c%c%c signature %s age %ld pdb %s)\n"), buffer[0], buffer[1], buffer[2], buffer[3], - signature, cvinfo->Age); + signature, cvinfo->Age, pdb[0] ? pdb : "(none)"); + + free (pdb); } } diff --git a/bfd/peicode.h b/bfd/peicode.h index 02573c84694..326e9f9a8ca 100644 --- a/bfd/peicode.h +++ b/bfd/peicode.h @@ -1383,7 +1383,7 @@ pe_bfd_read_buildid (bfd *abfd) */ if (_bfd_XXi_slurp_codeview_record (abfd, (file_ptr) idd.PointerToRawData, - idd.SizeOfData, cvinfo)) + idd.SizeOfData, cvinfo, NULL)) { struct bfd_build_id* build_id = bfd_alloc (abfd, sizeof (struct bfd_build_id) + cvinfo->SignatureLength); diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em index 892bf70b7a6..5df818c746c 100644 --- a/ld/emultempl/pe.em +++ b/ld/emultempl/pe.em @@ -146,6 +146,8 @@ static lang_assignment_statement_type *image_base_statement = 0; static unsigned short pe_dll_characteristics = DEFAULT_DLL_CHARACTERISTICS; static bool insert_timestamp = true; static const char *emit_build_id; +static int pdb; +static char *pdb_name; #ifdef DLL_SUPPORT static int pe_enable_stdcall_fixup = -1; /* 0=disable 1=enable. */ @@ -284,7 +286,8 @@ fragment <sections; asec != NULL; asec = asec->next) @@ -1295,6 +1309,9 @@ write_build_id (bfd *abfd) bfd_vma ib = pe_data (link_info.output_bfd)->pe_opthdr.ImageBase; + if (pdb_name) + pdb_base_name = lbasename (pdb_name); + /* Construct a debug directory entry which points to an immediately following CodeView record. */ struct internal_IMAGE_DEBUG_DIRECTORY idd; idd.Characteristics = 0; @@ -1302,7 +1319,7 @@ write_build_id (bfd *abfd) idd.MajorVersion = 0; idd.MinorVersion = 0; idd.Type = PE_IMAGE_DEBUG_TYPE_CODEVIEW; - idd.SizeOfData = sizeof (CV_INFO_PDB70) + 1; + idd.SizeOfData = sizeof (CV_INFO_PDB70) + (pdb_base_name ? strlen (pdb_base_name) : 0) + 1; idd.AddressOfRawData = asec->vma - ib + link_order->offset + sizeof (struct external_IMAGE_DEBUG_DIRECTORY); idd.PointerToRawData = asec->filepos + link_order->offset @@ -1331,7 +1348,8 @@ write_build_id (bfd *abfd) free (build_id); /* Write the codeview record. */ - if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo) == 0) + if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo, + pdb_base_name) == 0) return 0; /* Record the location of the debug directory in the data directory. */ @@ -1368,11 +1386,14 @@ setup_build_id (bfd *ibfd) /* Section is a fixed size: One IMAGE_DEBUG_DIRECTORY entry, of type IMAGE_DEBUG_TYPE_CODEVIEW, - pointing at a CV_INFO_PDB70 record containing the build-id, with a - null byte for PdbFileName. */ + pointing at a CV_INFO_PDB70 record containing the build-id, followed by + PdbFileName if relevant. */ s->size = sizeof (struct external_IMAGE_DEBUG_DIRECTORY) + sizeof (CV_INFO_PDB70) + 1; + if (pdb_name) + s->size += strlen (pdb_name); + return true; } @@ -1403,6 +1424,25 @@ gld${EMULATION_NAME}_after_open (void) } #endif + if (pdb && !pdb_name) + { + const char *base = lbasename (bfd_get_filename (link_info.output_bfd)); + size_t len = strlen (base); + static const char suffix[] = ".pdb"; + + while (len > 0 && base[len] != '.') + { + len--; + } + + if (len == 0) + len = strlen (base); + + pdb_name = xmalloc (len + sizeof (suffix)); + memcpy (pdb_name, base, len); + memcpy (pdb_name + len, suffix, sizeof (suffix)); + } + if (emit_build_id != NULL) { bfd *abfd; diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em index e68d1e69f17..5a31ffc64c0 100644 --- a/ld/emultempl/pep.em +++ b/ld/emultempl/pep.em @@ -157,6 +157,8 @@ static lang_assignment_statement_type *image_base_statement = 0; static unsigned short pe_dll_characteristics = DEFAULT_DLL_CHARACTERISTICS; static bool insert_timestamp = true; static const char *emit_build_id; +static int pdb; +static char *pdb_name; #ifdef DLL_SUPPORT static int pep_enable_stdcall_fixup = 1; /* 0=disable 1=enable (default). */ @@ -255,6 +257,7 @@ enum options OPTION_NO_INSERT_TIMESTAMP, OPTION_TERMINAL_SERVER_AWARE, OPTION_BUILD_ID, + OPTION_PDB, OPTION_ENABLE_RELOC_SECTION, OPTION_DISABLE_RELOC_SECTION, OPTION_DISABLE_HIGH_ENTROPY_VA, @@ -343,6 +346,7 @@ gld${EMULATION_NAME}_add_options {"insert-timestamp", no_argument, NULL, OPTION_INSERT_TIMESTAMP}, {"no-insert-timestamp", no_argument, NULL, OPTION_NO_INSERT_TIMESTAMP}, {"build-id", optional_argument, NULL, OPTION_BUILD_ID}, + {"pdb", required_argument, NULL, OPTION_PDB}, {"enable-reloc-section", no_argument, NULL, OPTION_ENABLE_RELOC_SECTION}, {"disable-reloc-section", no_argument, NULL, OPTION_DISABLE_RELOC_SECTION}, {"disable-high-entropy-va", no_argument, NULL, OPTION_DISABLE_HIGH_ENTROPY_VA}, @@ -490,6 +494,7 @@ gld${EMULATION_NAME}_list_options (FILE *file) fprintf (file, _(" --[disable-]wdmdriver Driver uses the WDM model\n")); fprintf (file, _(" --[disable-]tsaware Image is Terminal Server aware\n")); fprintf (file, _(" --build-id[=STYLE] Generate build ID\n")); + fprintf (file, _(" --pdb=[FILENAME] Generate PDB file\n")); #endif } @@ -898,6 +903,11 @@ gld${EMULATION_NAME}_handle_option (int optc) if (strcmp (optarg, "none")) emit_build_id = xstrdup (optarg); break; + case OPTION_PDB: + pdb = 1; + if (optarg && optarg[0]) + pdb_name = xstrdup (optarg); + break; } /* Set DLLCharacteristics bits */ @@ -1020,6 +1030,9 @@ gld${EMULATION_NAME}_after_parse (void) einfo (_("%P: warning: --export-dynamic is not supported for PE+ " "targets, did you mean --export-all-symbols?\n")); + if (pdb && emit_build_id == NULL) + emit_build_id = xstrdup (DEFAULT_BUILD_ID_STYLE); + set_entry_point (); after_parse_default (); @@ -1240,6 +1253,7 @@ write_build_id (bfd *abfd) bfd_size_type size; bfd_size_type build_id_size; unsigned char *build_id; + const char *pdb_base_name = NULL; /* Find the section the .buildid output section has been merged info. */ for (asec = abfd->sections; asec != NULL; asec = asec->next) @@ -1279,6 +1293,9 @@ write_build_id (bfd *abfd) bfd_vma ib = pe_data (link_info.output_bfd)->pe_opthdr.ImageBase; + if (pdb_name) + pdb_base_name = lbasename (pdb_name); + /* Construct a debug directory entry which points to an immediately following CodeView record. */ struct internal_IMAGE_DEBUG_DIRECTORY idd; idd.Characteristics = 0; @@ -1286,7 +1303,7 @@ write_build_id (bfd *abfd) idd.MajorVersion = 0; idd.MinorVersion = 0; idd.Type = PE_IMAGE_DEBUG_TYPE_CODEVIEW; - idd.SizeOfData = sizeof (CV_INFO_PDB70) + 1; + idd.SizeOfData = sizeof (CV_INFO_PDB70) + (pdb_base_name ? strlen (pdb_base_name) : 0) + 1; idd.AddressOfRawData = asec->vma - ib + link_order->offset + sizeof (struct external_IMAGE_DEBUG_DIRECTORY); idd.PointerToRawData = asec->filepos + link_order->offset @@ -1315,7 +1332,8 @@ write_build_id (bfd *abfd) free (build_id); /* Write the codeview record. */ - if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo) == 0) + if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo, + pdb_base_name) == 0) return 0; /* Record the location of the debug directory in the data directory. */ @@ -1352,11 +1370,14 @@ setup_build_id (bfd *ibfd) /* Section is a fixed size: One IMAGE_DEBUG_DIRECTORY entry, of type IMAGE_DEBUG_TYPE_CODEVIEW, - pointing at a CV_INFO_PDB70 record containing the build-id, with a - null byte for PdbFileName. */ + pointing at a CV_INFO_PDB70 record containing the build-id, followed by + PdbFileName if relevant. */ s->size = sizeof (struct external_IMAGE_DEBUG_DIRECTORY) + sizeof (CV_INFO_PDB70) + 1; + if (pdb_name) + s->size += strlen (pdb_name); + return true; } @@ -1388,6 +1409,25 @@ gld${EMULATION_NAME}_after_open (void) } #endif + if (pdb && !pdb_name) + { + const char *base = lbasename (bfd_get_filename (link_info.output_bfd)); + size_t len = strlen (base); + static const char suffix[] = ".pdb"; + + while (len > 0 && base[len] != '.') + { + len--; + } + + if (len == 0) + len = strlen (base); + + pdb_name = xmalloc (len + sizeof (suffix)); + memcpy (pdb_name, base, len); + memcpy (pdb_name + len, suffix, sizeof (suffix)); + } + if (emit_build_id != NULL) { bfd *abfd; diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp new file mode 100644 index 00000000000..1560241cdb8 --- /dev/null +++ b/ld/testsuite/ld-pe/pdb.exp @@ -0,0 +1,53 @@ +# Expect script for creating PDB files when linking. +# 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. + +if {![istarget i*86-*-mingw*] + && ![istarget x86_64-*-mingw*]} { + return +} + +proc get_pdb_name { 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 $pdb +} + +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 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" diff --git a/ld/testsuite/ld-pe/pdb1.s b/ld/testsuite/ld-pe/pdb1.s new file mode 100644 index 00000000000..30a8cfcca2c --- /dev/null +++ b/ld/testsuite/ld-pe/pdb1.s @@ -0,0 +1,5 @@ +.text + +.global foo +foo: + .long 0x12345678 From patchwork Sun Oct 16 23:24:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Harmstone X-Patchwork-Id: 3151 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:4ac7:0:0:0:0:0 with SMTP id y7csp1188427wrs; Sun, 16 Oct 2022 16:24:55 -0700 (PDT) X-Google-Smtp-Source: AMsMyM6rKlcuYXRDqYYxkopPiMuD5zg4xTeapt+fElXhROsua6P4Xpe7YF810Gi+UQLOUqTnNYZD X-Received: by 2002:a05:6402:320c:b0:45c:9ec4:8083 with SMTP id g12-20020a056402320c00b0045c9ec48083mr7876364eda.42.1665962695602; Sun, 16 Oct 2022 16:24:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1665962695; cv=none; d=google.com; s=arc-20160816; b=VoITAtF0HeBeqrua3TBWr7E9vSMgHn8YlAplQ717ZcrrnvxVnk+kH0Z4ZFHb00wsuP bCduJ88CeSghDnVDxR4nr0r8CSDGrEEEh/6d9R+6RkPj34U1JQ+lkxdjHnlS6UDC8et9 CH2wtybYCM05kU8hEoQdcQE37AINxVBb2Ei06G13DESwFNyZx3EMbMIISiyXgazkT3N+ JgUseYBGQUPJC6e4ROWwlw+pBnt2sMjNA7e56bzARTBS7mhVoQQkb5eRlZHqzISwo/k8 QszYrRdKXIT5qJwETyAJHNGuttlX5fdcT/nLrV4IB6DViNUw4UKH/sLY624APi0IkA8i swHg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:to:from :dkim-signature:dmarc-filter:delivered-to; bh=hCyBfIe5i4M3ZPGlaLZj74MrOoLAQthZ78pMzOWqlvI=; b=Z9/F5IdCTR9N3foFRUpVQPleA9KaERHJJXWf9pXWz7l3JUsX6UK/UjDhZ+BbKzhkXo 5o9BUbkMTivoit2N5jkNIndsp3tH9fgppX1eden+Gi+UGIrnzgrDWV0vRqXAUsI8iVHv 4n6TE53JSqAt89g2n6MBSQRjhhvDRID+3SoGwYwlcFgDpgwZexjFZ9MkHQlvhzAmrDpk jb47STe5bHyW8JcmpWZTHsn5J/GeOhbhgjlXnZK8C9rx/3h5LPGGt0tJZxQyPKR7eczX XFWge3/N1noYl+Il34be6SQoq5Z1LipYVjWYraOTS712Mw+UiDKx6T2AxgwtPbd015SD UPyQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20210112 header.b=X6nIjJxZ; spf=pass (google.com: domain of binutils-bounces+ouuuleilei=gmail.com@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="binutils-bounces+ouuuleilei=gmail.com@sourceware.org" Received: from sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id kg19-20020a17090776f300b00783489338b5si6547832ejc.148.2022.10.16.16.24.55 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 Oct 2022 16:24:55 -0700 (PDT) Received-SPF: pass (google.com: domain of binutils-bounces+ouuuleilei=gmail.com@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=fail header.i=@gmail.com header.s=20210112 header.b=X6nIjJxZ; spf=pass (google.com: domain of binutils-bounces+ouuuleilei=gmail.com@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="binutils-bounces+ouuuleilei=gmail.com@sourceware.org" Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 04BD238582A6 for ; Sun, 16 Oct 2022 23:24:49 +0000 (GMT) X-Original-To: binutils@sourceware.org Delivered-To: binutils@sourceware.org Received: from mail-wm1-x330.google.com (mail-wm1-x330.google.com [IPv6:2a00:1450:4864:20::330]) by sourceware.org (Postfix) with ESMTPS id 004B53858400 for ; Sun, 16 Oct 2022 23:24:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 004B53858400 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-wm1-x330.google.com with SMTP id r3-20020a05600c35c300b003b4b5f6c6bdso8769940wmq.2 for ; Sun, 16 Oct 2022 16:24:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=hCyBfIe5i4M3ZPGlaLZj74MrOoLAQthZ78pMzOWqlvI=; b=X6nIjJxZ05w12F5GnbJRWKQ/bxzfXVwFAmZmeV63HYkGiasx6AX8qMxsDwEuL3q56Y QnnVlKyKSgrosTVdM6kzlovENPg3H5OxCOa5cMhASFFLlBLhBO3/fu6Q2ZKAipYwmJoH e+abwvzfzoEfojZsG933Cd3OKHObznWUI+kCuF33diNsGk2neVy2nqfL7EE6AbwTj3Hy XWMgl58FZ6rZZszIpD6jI0wv8o53n3bgfFmnNha9dVIm0UDteV5fY0JAbRRoDMEh213Y H6RBSpJVxsfLx58c+t3meZpu5o9dXTwAmlXO6z5L9c73R9wbuCI4ZL4LeWLCD5pe/KAB Na1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=hCyBfIe5i4M3ZPGlaLZj74MrOoLAQthZ78pMzOWqlvI=; b=nSwMpq/UkOL2iniT7kfQ7KRzqLeLGuIqxyrfmhp/TgAntofdxShaw7rd557FkKJa8P Uzlp17DT0eeBjD3ClR9vwTMpu1VmucAOLJQSbtdqtUFFLdWgw4wvRq0vECaM1Ma4ROxk UnFlsb8Y7gojsxzHO+cM2WUDwQ2Z82kRl37hNfP7MEcCT+UjO9pq5FseReL5y/cUIZuw NyHApYze1cz4r6i53grIYV1LooY2BbOW2v6nGwpekoVXdRl47QRE8U1YPQ2mNpb4xQHV 9OIZA/wgV3zZ99ehtkQAZmma2ONCioGfyLI1t85C87jZU4UirUaCA4NlTco7Rx85y2WX 6A0g== X-Gm-Message-State: ACrzQf3/YCpEykOZMhdCodjVmL15mRxNKRCJ8bP95dIM97VvUqjucdjW I18hPzeDzuRBR8l7jkdvK1l3l3hScxg= X-Received: by 2002:a05:600c:4f10:b0:3c6:dcc6:51d7 with SMTP id l16-20020a05600c4f1000b003c6dcc651d7mr5362968wmq.91.1665962675168; Sun, 16 Oct 2022 16:24:35 -0700 (PDT) Received: from beren.harmstone.com ([2a02:8010:64ea:0:8eb8:7eff:fe53:9d5f]) by smtp.gmail.com with ESMTPSA id u5-20020a5d5145000000b00232251d71c7sm7000405wrt.68.2022.10.16.16.24.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 Oct 2022 16:24:34 -0700 (PDT) From: Mark Harmstone To: binutils@sourceware.org Subject: [PATCH v5 2/2] ld: Add minimal pdb generation Date: Mon, 17 Oct 2022 00:24:19 +0100 Message-Id: <20221016232419.1135-2-mark@harmstone.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221016232419.1135-1-mark@harmstone.com> References: <20221016232419.1135-1-mark@harmstone.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.4 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, KAM_SHORT, 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 X-BeenThere: binutils@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Binutils mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Harmstone Errors-To: binutils-bounces+ouuuleilei=gmail.com@sourceware.org Sender: "Binutils" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1746888499502364438?= X-GMAIL-MSGID: =?utf-8?q?1746888499502364438?= --- ld/Makefile.am | 10 +- ld/Makefile.in | 13 +- ld/emultempl/pe.em | 7 + ld/emultempl/pep.em | 7 + ld/pdb.c | 522 +++++++++++++++++++++++++++++++++++++ ld/pdb.h | 111 ++++++++ ld/testsuite/ld-pe/pdb.exp | 267 +++++++++++++++++++ 7 files changed, 928 insertions(+), 9 deletions(-) create mode 100644 ld/pdb.c create mode 100644 ld/pdb.h diff --git a/ld/Makefile.am b/ld/Makefile.am index d31021c13e2..43114372989 100644 --- a/ld/Makefile.am +++ b/ld/Makefile.am @@ -475,12 +475,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 @@ -493,7 +495,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 @@ -956,7 +958,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) $(LIBIBERTY) $(LIBINTL) $(ZLIB) $(JANSSON_LIBS) diff --git a/ld/Makefile.in b/ld/Makefile.in index ee0c98f65b0..7ce4704c66f 100644 --- a/ld/Makefile.in +++ b/ld/Makefile.in @@ -211,7 +211,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 @@ -970,12 +970,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 @@ -987,7 +989,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) @@ -1006,7 +1008,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) @@ -1569,6 +1571,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@ diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em index 5df818c746c..b324b629965 100644 --- a/ld/emultempl/pe.em +++ b/ld/emultempl/pe.em @@ -66,6 +66,7 @@ fragment < + +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; +} diff --git a/ld/pdb.h b/ld/pdb.h new file mode 100644 index 00000000000..e5f53b44f39 --- /dev/null +++ b/ld/pdb.h @@ -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 + +/* 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 diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp index 1560241cdb8..b62ce6da6f8 100644 --- a/ld/testsuite/ld-pe/pdb.exp +++ b/ld/testsuite/ld-pe/pdb.exp @@ -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" +}