From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by sourceware.org (Postfix) with ESMTPS id 273C1388C5D6 for ; Fri, 9 Dec 2022 01:52:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 273C1388C5D6 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-x32c.google.com with SMTP id ay40so2567651wmb.2 for ; Thu, 08 Dec 2022 17:52:54 -0800 (PST) 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=6bLTlEnzoBB6Y2IxVoFyaTNZ1FQt835ExMAmY7MwfX0=; b=lXiFvO+5bG/wtiWiwkkUQw3zadi+Pc0IqQP2cvRQYW+d8PGY0V1VuTatr10ZTMoR3C jOoWp21OFrn5vPsAB7AZWTo9wVzzqvJMNO1YZm6wCM5HDwCXdHiZOjqZCvrCkmvV0ELy QV7vVCqCpjkd+aTf+SBlvsvgmWGcFraM8VobfecB/mahk9ijlIEAM6SN2FIBuHTcbpun 7dSvPO76emCbkarXTIpJR22GiKPn5uOOu2GxUeLD1BMOalANIC73VoVCJxfqQ32kmZqk oCSFaXAOuQ6JfPSIXo0q+jESjNoM+p2egM7UXQj6a0NQl/O1c6dGgHcBDWJUTaqpQ8pM uNWw== 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=6bLTlEnzoBB6Y2IxVoFyaTNZ1FQt835ExMAmY7MwfX0=; b=yzeLmLLem6D9hnqO5hCIiW0Al9l+U9uwUrFzOcdp+QMmS3fSYAKl6k//ww26w/QVMo PILWmcQmuH1hQ21RzKKeOpW+nnbgn+zuAALsrguGQCcNbo1HQrvjxL5D07YOqt5ywOQ1 epdRLFL3RoPGe3vhNp2lM4WM+jIpDd5ORBy8O8YxBGt+uObgmkrSGIcyF4tcjL/x3yuT TQZKMynS53gSGS7FygTlS0r/tKePxktkcibz8XP4LJ16/cOkRLRjNv9X+YugnG2M3DPa hgLrGwhu5HTul7qJqce0mZU0vAlqTaeRmlS7l3zgjfC/paIniBFwf102HYZpN+93eGGN 4yrQ== X-Gm-Message-State: ANoB5pmLVZWOsCvJutD0SgxBuswSzeiW2+b/EvKoGmO+3zJ7P1D54+dG mmNDsEz9c7Rx75k+EqCfrXf4w8zwIvA= X-Google-Smtp-Source: AA0mqf5IrvlRjXWQUHWHBD/rIedFwdzIovr1S/o0Wvpbgxb3GSphmZye0dy/pRatM6AfDKr3mug2GA== X-Received: by 2002:a05:600c:1c91:b0:3d2:640:c4e5 with SMTP id k17-20020a05600c1c9100b003d20640c4e5mr1690417wms.8.1670550773436; Thu, 08 Dec 2022 17:52:53 -0800 (PST) Received: from beren.harmstone.com ([2a02:8010:64ea:0:8eb8:7eff:fe53:9d5f]) by smtp.gmail.com with ESMTPSA id fc18-20020a05600c525200b003d04e4ed873sm7473139wmb.22.2022.12.08.17.52.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Dec 2022 17:52:52 -0800 (PST) Sender: Mark Harmstone From: Mark Harmstone To: binutils@sourceware.org, nickc@redhat.com Cc: Mark Harmstone Subject: [PATCH 10/10] ld: Write linker symbols in PDB Date: Fri, 9 Dec 2022 01:52:40 +0000 Message-Id: <20221209015240.6348-10-mark@harmstone.com> X-Mailer: git-send-email 2.37.4 In-Reply-To: <20221209015240.6348-1-mark@harmstone.com> References: <20221209015240.6348-1-mark@harmstone.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.2 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: --- ld/pdb.c | 242 +++++++++++++++++++++++++++++++------ ld/pdb.h | 42 +++++++ ld/testsuite/ld-pe/pdb.exp | 77 ++++++++++++ 3 files changed, 327 insertions(+), 34 deletions(-) diff --git a/ld/pdb.c b/ld/pdb.c index 09377a02e52..0346ccb388c 100644 --- a/ld/pdb.c +++ b/ld/pdb.c @@ -21,6 +21,7 @@ #include "pdb.h" #include "bfdlink.h" #include "ld.h" +#include "ldmain.h" #include "ldmisc.h" #include "libbfd.h" #include "libiberty.h" @@ -3420,6 +3421,168 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types, return true; } +/* Return the CodeView constant for the selected architecture. */ +static uint16_t +target_processor (bfd *abfd) +{ + if (abfd->arch_info->arch != bfd_arch_i386) + return 0; + + if (abfd->arch_info->mach & bfd_mach_x86_64) + return CV_CFL_X64; + else + return CV_CFL_80386; +} + +/* Create the symbols that go in "* Linker *", the dummy module created + for the linker itself. */ +static bool +create_linker_symbols (bfd *abfd, uint8_t **syms, uint32_t *sym_byte_size, + const char *pdb_name) +{ + uint8_t *ptr; + struct objname *name; + struct compile3 *comp; + struct envblock *env; + size_t padding1, padding2, env_size; + char *cwdval, *exeval, *pdbval; + + /* extra NUL for padding */ + static const char linker_fn[] = "* Linker *\0"; + static const char linker_name[] = "GNU LD " VERSION; + + static const char cwd[] = "cwd"; + static const char exe[] = "exe"; + static const char pdb[] = "pdb"; + + cwdval = getcwd (NULL, 0); + if (!cwdval) + { + einfo (_("%P: warning: unable to get working directory\n")); + return false; + } + + exeval = lrealpath (program_name); + + if (!exeval) + { + einfo (_("%P: warning: unable to get program name\n")); + free (cwdval); + return false; + } + + pdbval = lrealpath (pdb_name); + + if (!pdbval) + { + einfo (_("%P: warning: unable to get full path to PDB\n")); + free (exeval); + free (cwdval); + return false; + } + + *sym_byte_size += offsetof (struct objname, name) + sizeof (linker_fn); + *sym_byte_size += offsetof (struct compile3, compiler) + sizeof (linker_name); + + if (*sym_byte_size % 4) + padding1 = 4 - (*sym_byte_size % 4); + else + padding1 = 0; + + *sym_byte_size += padding1; + + env_size = offsetof (struct envblock, strings); + env_size += sizeof (cwd); + env_size += strlen (cwdval) + 1; + env_size += sizeof (exe); + env_size += strlen (exeval) + 1; + env_size += sizeof (pdb); + env_size += strlen (pdbval) + 1; + + if (env_size % 4) + padding2 = 4 - (env_size % 4); + else + padding2 = 0; + + env_size += padding2; + + *sym_byte_size += env_size; + + *syms = xmalloc (*sym_byte_size); + ptr = *syms; + + /* Write S_OBJNAME */ + + name = (struct objname *) ptr; + bfd_putl16 (offsetof (struct objname, name) + + sizeof (linker_fn) - sizeof (uint16_t), &name->size); + bfd_putl16 (S_OBJNAME, &name->kind); + bfd_putl32 (0, &name->signature); + memcpy (name->name, linker_fn, sizeof (linker_fn)); + + ptr += offsetof (struct objname, name) + sizeof (linker_fn); + + /* Write S_COMPILE3 */ + + comp = (struct compile3 *) ptr; + + bfd_putl16 (offsetof (struct compile3, compiler) + sizeof (linker_name) + + padding1 - sizeof (uint16_t), &comp->size); + bfd_putl16 (S_COMPILE3, &comp->kind); + bfd_putl32 (CV_CFL_LINK, &comp->flags); + bfd_putl16 (target_processor (abfd), &comp->machine); + bfd_putl16 (0, &comp->frontend_major); + bfd_putl16 (0, &comp->frontend_minor); + bfd_putl16 (0, &comp->frontend_build); + bfd_putl16 (0, &comp->frontend_qfe); + bfd_putl16 (0, &comp->backend_major); + bfd_putl16 (0, &comp->backend_minor); + bfd_putl16 (0, &comp->backend_build); + bfd_putl16 (0, &comp->backend_qfe); + memcpy (comp->compiler, linker_name, sizeof (linker_name)); + + memset (comp->compiler + sizeof (linker_name), 0, padding1); + + ptr += offsetof (struct compile3, compiler) + sizeof (linker_name) + padding1; + + /* Write S_ENVBLOCK */ + + env = (struct envblock *) ptr; + + bfd_putl16 (env_size - sizeof (uint16_t), &env->size); + bfd_putl16 (S_ENVBLOCK, &env->kind); + env->flags = 0; + + ptr += offsetof (struct envblock, strings); + + memcpy (ptr, cwd, sizeof (cwd)); + ptr += sizeof (cwd); + memcpy (ptr, cwdval, strlen (cwdval) + 1); + ptr += strlen (cwdval) + 1; + + memcpy (ptr, exe, sizeof (exe)); + ptr += sizeof (exe); + memcpy (ptr, exeval, strlen (exeval) + 1); + ptr += strlen (exeval) + 1; + + memcpy (ptr, pdb, sizeof (pdb)); + ptr += sizeof (pdb); + memcpy (ptr, pdbval, strlen (pdbval) + 1); + ptr += strlen (pdbval) + 1; + + /* Microsoft's LINK also includes "cmd", the command-line options passed + to the linker, but unfortunately we don't have access to argc and argv + at this stage. */ + + memset (ptr, 0, padding2); + + free (pdbval); + free (exeval); + free (cwdval); + + return true; +} + /* Populate the module stream, which consists of the transformed .debug$S data for each object file. */ static bool @@ -3429,55 +3592,65 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size, struct mod_source_files *mod_source, bfd *abfd, struct types *types, struct types *ids, uint16_t mod_num, - bfd *sym_rec_stream, struct globals *glob) + bfd *sym_rec_stream, struct globals *glob, + const char *pdb_name) { uint8_t int_buf[sizeof (uint32_t)]; uint8_t *c13_info = NULL; uint8_t *syms = NULL; - struct type_entry **map = NULL; - uint32_t num_types = 0; *sym_byte_size = 0; *c13_info_size = 0; - /* Process .debug$T section. */ - - for (asection *s = mod->sections; s; s = s->next) + if (!strcmp (bfd_get_filename (mod), "dll stuff")) { - if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t)) + if (!create_linker_symbols (mod, &syms, sym_byte_size, pdb_name)) + return false; + } + else + { + struct type_entry **map = NULL; + uint32_t num_types = 0; + + /* Process .debug$T section. */ + + for (asection *s = mod->sections; s; s = s->next) { - if (!handle_debugt_section (s, mod, types, ids, mod_num, strings, - &map, &num_types)) + if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t)) { - free (mod_source->files); - return false; - } + if (!handle_debugt_section (s, mod, types, ids, mod_num, strings, + &map, &num_types)) + { + free (mod_source->files); + return false; + } - break; + break; + } } - } - /* Process .debug$S section(s). */ + /* Process .debug$S section(s). */ - for (asection *s = mod->sections; s; s = s->next) - { - if (!strcmp (s->name, ".debug$S") && s->size >= sizeof (uint32_t)) + for (asection *s = mod->sections; s; s = s->next) { - if (!handle_debugs_section (s, mod, strings, &c13_info, - c13_info_size, mod_source, abfd, - &syms, sym_byte_size, map, num_types, - sym_rec_stream, glob, mod_num)) + if (!strcmp (s->name, ".debug$S") && s->size >= sizeof (uint32_t)) { - free (c13_info); - free (syms); - free (mod_source->files); - free (map); - return false; + if (!handle_debugs_section (s, mod, strings, &c13_info, + c13_info_size, mod_source, abfd, + &syms, sym_byte_size, map, num_types, + sym_rec_stream, glob, mod_num)) + { + free (c13_info); + free (syms); + free (mod_source->files); + free (map); + return false; + } } } - } - free (map); + free (map); + } /* Write the signature. */ @@ -3529,7 +3702,8 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data, uint32_t *size, struct string_table *strings, struct source_files_info *source, struct types *types, struct types *ids, - bfd *sym_rec_stream, struct globals *glob) + bfd *sym_rec_stream, struct globals *glob, + const char *pdb_name) { uint8_t *ptr; unsigned int mod_num; @@ -3619,7 +3793,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data, strings, &c13_info_size, &source->mods[mod_num], abfd, types, ids, mod_num, - sym_rec_stream, glob)) + sym_rec_stream, glob, pdb_name)) { for (unsigned int i = 0; i < source->mod_count; i++) { @@ -4108,7 +4282,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb, struct string_table *strings, struct types *types, struct types *ids, - bfd *sym_rec_stream) + bfd *sym_rec_stream, const char *pdb_name) { struct pdb_dbi_stream_header h; struct optional_dbg_header opt; @@ -4130,7 +4304,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb, if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size, strings, &source, types, ids, - sym_rec_stream, &glob)) + sym_rec_stream, &glob, pdb_name)) { htab_delete (glob.hashmap); return false; @@ -4822,7 +4996,7 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid) if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num, sym_rec_stream_num, publics_stream_num, - &strings, &types, &ids, sym_rec_stream)) + &strings, &types, &ids, sym_rec_stream, pdb_name)) { einfo (_("%P: warning: cannot populate DBI stream " "in PDB file: %E\n")); diff --git a/ld/pdb.h b/ld/pdb.h index ddb9b86ce85..749a60249df 100644 --- a/ld/pdb.h +++ b/ld/pdb.h @@ -93,6 +93,7 @@ #define S_LPROCREF 0x1127 #define S_FRAMECOOKIE 0x113a #define S_COMPILE3 0x113c +#define S_ENVBLOCK 0x113d #define S_LOCAL 0x113e #define S_DEFRANGE_REGISTER 0x1141 #define S_DEFRANGE_FRAMEPOINTER_REL 0x1142 @@ -796,6 +797,47 @@ struct heap_alloc_site uint32_t type; } ATTRIBUTE_PACKED; +/* OBJNAMESYM in cvinfo.h */ +struct objname +{ + uint16_t size; + uint16_t kind; + uint32_t signature; + char name[]; +} ATTRIBUTE_PACKED; + +#define CV_CFL_80386 0x03 +#define CV_CFL_X64 0xD0 + +#define CV_CFL_LINK 0x07 + +/* COMPILESYM3 in cvinfo.h */ +struct compile3 +{ + uint16_t size; + uint16_t kind; + uint32_t flags; + uint16_t machine; + uint16_t frontend_major; + uint16_t frontend_minor; + uint16_t frontend_build; + uint16_t frontend_qfe; + uint16_t backend_major; + uint16_t backend_minor; + uint16_t backend_build; + uint16_t backend_qfe; + char compiler[]; +} ATTRIBUTE_PACKED; + +/* ENVBLOCKSYM in cvinfo.h */ +struct envblock +{ + uint16_t size; + uint16_t kind; + uint8_t flags; + char strings[]; +} ATTRIBUTE_PACKED; + 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 5df1583c247..bd50b2fb076 100644 --- a/ld/testsuite/ld-pe/pdb.exp +++ b/ld/testsuite/ld-pe/pdb.exp @@ -1678,6 +1678,83 @@ proc test9 { } { } else { fail "Incorrect symbols in module stream" } + + # check linker symbols + + 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 { [expr $off % 4] != 0 } { + set off [expr $off + 4 - ($off % 4)] + } + + incr off 34 + + binary scan [string range $mod_info $off [expr $off + 1]] s linker_syms_index + + set index_str [format "%04x" $linker_syms_index] + + set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms2.pdb $index_str"] + + if ![string match "" $exec_output] { + fail "Could not extract linker symbols" + return + } else { + pass "Extracted linker symbols" + } + + set syms [file_contents "tmpdir/$index_str"] + + # check S_OBJNAME + + set off 4 + binary scan [string range $syms $off [expr $off + 1]] s sym_len + binary scan [string range $syms [expr $off + 2] [expr $off + 3]] s sym_type + + if { $sym_type != 0x1101 } { + fail "First linker symbol was not S_OBJNAME" + } else { + pass "First linker symbol was S_OBJNAME" + + set linker_fn [string range $syms [expr $off + 8] [expr [string first \000 $syms [expr $off + 8]] - 1]] + + if ![string equal $linker_fn "* Linker *"] { + fail "Incorrect linker object name" + } else { + pass "Correct linker object name" + } + } + + incr off [expr $sym_len + 2] + + # check S_COMPILE3 + + binary scan [string range $syms $off [expr $off + 1]] s sym_len + binary scan [string range $syms [expr $off + 2] [expr $off + 3]] s sym_type + + if { $sym_type != 0x113c } { + fail "Second linker symbol was not S_COMPILE3" + } else { + pass "Second linker symbol was S_COMPILE3" + } + + incr off [expr $sym_len + 2] + + # check S_ENVBLOCK + + binary scan [string range $syms $off [expr $off + 1]] s sym_len + binary scan [string range $syms [expr $off + 2] [expr $off + 3]] s sym_type + + if { $sym_type != 0x113d } { + fail "Third linker symbol was not S_ENVBLOCK" + } else { + pass "Third linker symbol was S_ENVBLOCK" + } } test1 -- 2.37.4