PE+: Remove the stdcall fixup machinery

Message ID 20260829170532.691-2-oleg.tolmatcev@gmail.com
State New
Headers
Series PE+: Remove the stdcall fixup machinery |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Oleg Tolmatcev Aug. 29, 2026, 5:05 p.m. UTC
  Win64 has a single calling convention, so symbols never carry the @nn
stdcall decoration.  The stdcall fixup pass in pep.em can therefore
never find a match, and neither can --kill-at or --add-stdcall-alias,
both of which only act on names containing '@'.

Remove the stdcall fixup pass.  Besides being dead code, it traversed
the whole link hash table once per undefined cdecl symbol, so it was
quadratic.

The four affected options are still parsed, and now simply ignored, so
that command lines shared with i386 PE targets keep working.  They are
no longer listed in --help, and ld.texi records that they have no
effect on PE+ targets.

ld/ChangeLog:

	* emultempl/pep.em (pep_enable_stdcall_fixup, pep_undef_found_sym)
	(pep_undef_cdecl_match, set_decoration, pep_fixup_stdcalls):
	Remove.
	(gld${EMULATION_NAME}_list_options): Don't list --add-stdcall-alias,
	--disable-stdcall-fixup, --enable-stdcall-fixup or --kill-at.
	(gld${EMULATION_NAME}_handle_option): Accept and ignore
	OPTION_KILL_ATS, OPTION_STDCALL_ALIASES,
	OPTION_ENABLE_STDCALL_FIXUP and OPTION_DISABLE_STDCALL_FIXUP.
	(gld${EMULATION_NAME}_after_open): Don't call pep_fixup_stdcalls.
	* ld.texi (--add-stdcall-alias, --enable-stdcall-fixup)
	(--kill-at): Note that these have no effect on PE+ targets.

Signed-off-by: Oleg Tolmatcev <oleg.tolmatcev@gmail.com>
---
 ld/emultempl/pep.em | 145 +-------------------------------------------
 ld/ld.texi          |   6 ++
 2 files changed, 7 insertions(+), 144 deletions(-)
  

Comments

Jan Beulich Sept. 1, 2026, 8:49 a.m. UTC | #1
On 29.08.2026 19:05, Oleg Tolmatcev wrote:
> Win64 has a single calling convention, so symbols never carry the @nn
> stdcall decoration.  The stdcall fixup pass in pep.em can therefore
> never find a match, and neither can --kill-at or --add-stdcall-alias,
> both of which only act on names containing '@'.
> 
> Remove the stdcall fixup pass.  Besides being dead code, it traversed
> the whole link hash table once per undefined cdecl symbol, so it was
> quadratic.
> 
> The four affected options are still parsed, and now simply ignored, so
> that command lines shared with i386 PE targets keep working.  They are
> no longer listed in --help, and ld.texi records that they have no
> effect on PE+ targets.
> 
> ld/ChangeLog:
> 
> 	* emultempl/pep.em (pep_enable_stdcall_fixup, pep_undef_found_sym)
> 	(pep_undef_cdecl_match, set_decoration, pep_fixup_stdcalls):
> 	Remove.
> 	(gld${EMULATION_NAME}_list_options): Don't list --add-stdcall-alias,
> 	--disable-stdcall-fixup, --enable-stdcall-fixup or --kill-at.
> 	(gld${EMULATION_NAME}_handle_option): Accept and ignore
> 	OPTION_KILL_ATS, OPTION_STDCALL_ALIASES,
> 	OPTION_ENABLE_STDCALL_FIXUP and OPTION_DISABLE_STDCALL_FIXUP.
> 	(gld${EMULATION_NAME}_after_open): Don't call pep_fixup_stdcalls.
> 	* ld.texi (--add-stdcall-alias, --enable-stdcall-fixup)
> 	(--kill-at): Note that these have no effect on PE+ targets.
> 
> Signed-off-by: Oleg Tolmatcev <oleg.tolmatcev@gmail.com>

Looks largely good to me, just one remark:

> @@ -768,16 +763,10 @@ gld${EMULATION_NAME}_handle_option (int optc)
>        pep_dll_add_excludes (optarg, EXCLUDEFORIMPLIB);
>        break;
>      case OPTION_KILL_ATS:
> -      pep_dll_kill_ats = 1;
> -      break;
>      case OPTION_STDCALL_ALIASES:
> -      pep_dll_stdcall_aliases = 1;
> -      break;
>      case OPTION_ENABLE_STDCALL_FIXUP:
> -      pep_enable_stdcall_fixup = 1;
> -      break;
>      case OPTION_DISABLE_STDCALL_FIXUP:
> -      pep_enable_stdcall_fixup = 0;
> +      /* Ignored: stdcall and cdecl are unsupported on Win64.  */
>        break;

But Win64 isn't the only environment where PE32+ binaries can be used.
I'd prefer if Windows wasn't mentioned here, or if at least this was
generalized by e.g. saying "on platforms using Win64's ABI" (which
would then include e.g. UEFI). I can adjust while committing, but of
course only if you agree (and if you have preferred alternative
wording, please also indicate that preference of yours).

Jan
  
Oleg Tolmatcev Sept. 1, 2026, 10:01 a.m. UTC | #2
вт, 1 сент. 2026 г. в 10:49, Jan Beulich <jbeulich@suse.com>:
>
> On 29.08.2026 19:05, Oleg Tolmatcev wrote:
> > Win64 has a single calling convention, so symbols never carry the @nn
> > stdcall decoration.  The stdcall fixup pass in pep.em can therefore
> > never find a match, and neither can --kill-at or --add-stdcall-alias,
> > both of which only act on names containing '@'.
> >
> > Remove the stdcall fixup pass.  Besides being dead code, it traversed
> > the whole link hash table once per undefined cdecl symbol, so it was
> > quadratic.
> >
> > The four affected options are still parsed, and now simply ignored, so
> > that command lines shared with i386 PE targets keep working.  They are
> > no longer listed in --help, and ld.texi records that they have no
> > effect on PE+ targets.
> >
> > ld/ChangeLog:
> >
> >       * emultempl/pep.em (pep_enable_stdcall_fixup, pep_undef_found_sym)
> >       (pep_undef_cdecl_match, set_decoration, pep_fixup_stdcalls):
> >       Remove.
> >       (gld${EMULATION_NAME}_list_options): Don't list --add-stdcall-alias,
> >       --disable-stdcall-fixup, --enable-stdcall-fixup or --kill-at.
> >       (gld${EMULATION_NAME}_handle_option): Accept and ignore
> >       OPTION_KILL_ATS, OPTION_STDCALL_ALIASES,
> >       OPTION_ENABLE_STDCALL_FIXUP and OPTION_DISABLE_STDCALL_FIXUP.
> >       (gld${EMULATION_NAME}_after_open): Don't call pep_fixup_stdcalls.
> >       * ld.texi (--add-stdcall-alias, --enable-stdcall-fixup)
> >       (--kill-at): Note that these have no effect on PE+ targets.
> >
> > Signed-off-by: Oleg Tolmatcev <oleg.tolmatcev@gmail.com>
>
> Looks largely good to me, just one remark:
>
> > @@ -768,16 +763,10 @@ gld${EMULATION_NAME}_handle_option (int optc)
> >        pep_dll_add_excludes (optarg, EXCLUDEFORIMPLIB);
> >        break;
> >      case OPTION_KILL_ATS:
> > -      pep_dll_kill_ats = 1;
> > -      break;
> >      case OPTION_STDCALL_ALIASES:
> > -      pep_dll_stdcall_aliases = 1;
> > -      break;
> >      case OPTION_ENABLE_STDCALL_FIXUP:
> > -      pep_enable_stdcall_fixup = 1;
> > -      break;
> >      case OPTION_DISABLE_STDCALL_FIXUP:
> > -      pep_enable_stdcall_fixup = 0;
> > +      /* Ignored: stdcall and cdecl are unsupported on Win64.  */
> >        break;
>
> But Win64 isn't the only environment where PE32+ binaries can be used.
> I'd prefer if Windows wasn't mentioned here, or if at least this was
> generalized by e.g. saying "on platforms using Win64's ABI" (which
> would then include e.g. UEFI). I can adjust while committing, but of
> course only if you agree (and if you have preferred alternative
> wording, please also indicate that preference of yours).
>
> Jan

Thanks for the review. I didn't even know that, so I am of course OK
with any wording.

Oleg
  

Patch

diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em
index 3ba29401821..0845bd3a1d9 100644
--- a/ld/emultempl/pep.em
+++ b/ld/emultempl/pep.em
@@ -194,7 +194,6 @@  static char *pdb_name;
 #endif
 
 #ifdef DLL_SUPPORT
-static int    pep_enable_stdcall_fixup = 1; /* 0=disable 1=enable (default).  */
 static char * pep_out_def_filename = NULL;
 static int    pep_enable_auto_image_base = 0;
 static char * pep_dll_search_prefix = NULL;
@@ -425,9 +424,6 @@  gld${EMULATION_NAME}_list_options (FILE *file)
   fprintf (file, _("  --[no-]insert-timestamp            Use a real timestamp rather than zero (default)\n"));
   fprintf (file, _("                                     This makes binaries non-deterministic\n"));
 #ifdef DLL_SUPPORT
-  fprintf (file, _("  --add-stdcall-alias                Export symbols with and without @nn\n"));
-  fprintf (file, _("  --disable-stdcall-fixup            Don't link _sym to _sym@nn\n"));
-  fprintf (file, _("  --enable-stdcall-fixup             Link _sym to _sym@nn without warnings\n"));
   fprintf (file, _("  --exclude-symbols sym,sym,...      Exclude symbols from automatic export\n"));
   fprintf (file, _("  --exclude-all-symbols              Exclude all symbols from automatic export\n"));
   fprintf (file, _("  --exclude-libs lib,lib,...         Exclude libraries from automatic export\n"));
@@ -435,7 +431,6 @@  gld${EMULATION_NAME}_list_options (FILE *file)
   fprintf (file, _("                                     Exclude objects, archive members from auto\n"));
   fprintf (file, _("                                     export, place into import library instead\n"));
   fprintf (file, _("  --export-all-symbols               Automatically export all globals to DLL\n"));
-  fprintf (file, _("  --kill-at                          Remove @nn from exported symbols\n"));
   fprintf (file, _("  --output-def <file>                Generate a .DEF file for the built DLL\n"));
   fprintf (file, _("  --warn-duplicate-exports           Warn about duplicate exports\n"));
   fprintf (file, _("  --compat-implib                    Create backward compatible import libs;\n\
@@ -768,16 +763,10 @@  gld${EMULATION_NAME}_handle_option (int optc)
       pep_dll_add_excludes (optarg, EXCLUDEFORIMPLIB);
       break;
     case OPTION_KILL_ATS:
-      pep_dll_kill_ats = 1;
-      break;
     case OPTION_STDCALL_ALIASES:
-      pep_dll_stdcall_aliases = 1;
-      break;
     case OPTION_ENABLE_STDCALL_FIXUP:
-      pep_enable_stdcall_fixup = 1;
-      break;
     case OPTION_DISABLE_STDCALL_FIXUP:
-      pep_enable_stdcall_fixup = 0;
+      /* Ignored: stdcall and cdecl are unsupported on Win64.  */
       break;
     case OPTION_WARN_DUPLICATE_EXPORTS:
       pep_dll_warn_dup_exports = 1;
@@ -1031,132 +1020,6 @@  gld${EMULATION_NAME}_after_parse (void)
 }
 
 #ifdef DLL_SUPPORT
-static struct bfd_link_hash_entry *pep_undef_found_sym;
-
-static bool
-pep_undef_cdecl_match (struct bfd_link_hash_entry *h, void *inf)
-{
-  int sl;
-  char *string = inf;
-  const char *hs = h->root.string;
-
-  sl = strlen (string);
-  if (h->type == bfd_link_hash_defined
-      && ((*hs == '@' && *string == '_'
-		   && strncmp (hs + 1, string + 1, sl - 1) == 0)
-		  || strncmp (hs, string, sl) == 0)
-      && h->root.string[sl] == '@')
-    {
-      pep_undef_found_sym = h;
-      return false;
-    }
-  return true;
-}
-
-static void
-set_decoration (const char *undecorated_name,
-		struct bfd_link_hash_entry * decoration)
-{
-  static bool  gave_warning_message = false;
-  struct decoration_hash_entry *entry;
-
-  if (is_underscoring () && undecorated_name[0] == '_')
-    undecorated_name++;
-
-  entry = (struct decoration_hash_entry *)
-	  bfd_hash_lookup (&(coff_hash_table (&link_info)->decoration_hash),
-			   undecorated_name, true /* create */, false /* copy */);
-
-  if (entry->decorated_link != NULL && !gave_warning_message)
-    {
-      einfo (_("%P: warning: overwriting decorated name %s with %s\n"),
-	     entry->decorated_link->root.string, undecorated_name);
-      gave_warning_message = true;
-    }
-
-  entry->decorated_link = decoration;
-}
-
-static void
-pep_fixup_stdcalls (void)
-{
-  static int gave_warning_message = 0;
-  struct bfd_link_hash_entry *undef, *sym;
-
-  if (pep_dll_extra_pe_debug)
-    printf ("%s\n", __func__);
-
-  for (undef = link_info.hash->undefs; undef; undef=undef->u.undef.next)
-    if (undef->type == bfd_link_hash_undefined)
-      {
-	const char* at = strchr (undef->root.string, '@');
-	int lead_at = (*undef->root.string == '@');
-	if (lead_at)
-	  at = strchr (undef->root.string + 1, '@');
-	if (at || lead_at)
-	  {
-	    /* The symbol is a stdcall symbol, so let's look for a
-	       cdecl symbol with the same name and resolve to that.  */
-	    char *cname = xstrdup (undef->root.string);
-	    char *at2;
-
-	    if (lead_at)
-	      *cname = '_';
-	    at2 = strchr (cname, '@');
-	    if (at2)
-	      *at2 = 0;
-	    sym = bfd_link_hash_lookup (link_info.hash, cname, 0, 0, 1);
-
-	    if (sym && sym->type == bfd_link_hash_defined)
-	      {
-		undef->type = bfd_link_hash_defined;
-		undef->u.def.value = sym->u.def.value;
-		undef->u.def.section = sym->u.def.section;
-
-		if (pep_enable_stdcall_fixup == -1)
-		  {
-		    einfo (_("warning: resolving %s by linking to %s\n"),
-			   undef->root.string, cname);
-		    if (! gave_warning_message)
-		      {
-			gave_warning_message = 1;
-			einfo (_("Use --enable-stdcall-fixup to disable these warnings\n"));
-			einfo (_("Use --disable-stdcall-fixup to disable these fixups\n"));
-		      }
-		  }
-	      }
-	  }
-	else
-	  {
-	    /* The symbol is a cdecl symbol, so we look for stdcall
-	       symbols - which means scanning the whole symbol table.  */
-	    pep_undef_found_sym = 0;
-	    bfd_link_hash_traverse (link_info.hash, pep_undef_cdecl_match,
-				    (char *) undef->root.string);
-	    sym = pep_undef_found_sym;
-	    if (sym)
-	      {
-		undef->type = bfd_link_hash_defined;
-		undef->u.def.value = sym->u.def.value;
-		undef->u.def.section = sym->u.def.section;
-		set_decoration (undef->root.string, sym);
-
-		if (pep_enable_stdcall_fixup == -1)
-		  {
-		    einfo (_("warning: resolving %s by linking to %s\n"),
-			   undef->root.string, sym->root.string);
-		    if (! gave_warning_message)
-		      {
-			gave_warning_message = 1;
-			einfo (_("Use --enable-stdcall-fixup to disable these warnings\n"));
-			einfo (_("Use --disable-stdcall-fixup to disable these fixups\n"));
-		      }
-		  }
-	      }
-	  }
-      }
-}
-
 static bfd_vma
 read_addend (arelent *rel, asection *s)
 {
@@ -1598,12 +1461,6 @@  gld${EMULATION_NAME}_after_open (void)
   if (link_info.pei386_auto_import) /* -1=warn or 1=enable */
     pep_find_data_imports (U ("_head_"), make_import_fixup);
 
-  /* The implementation of the feature is rather dumb and would cause the
-     compilation time to go through the roof if there are many undefined
-     symbols in the link, so it needs to be run after auto-import.  */
-  if (pep_enable_stdcall_fixup) /* -1=warn or 1=enable */
-    pep_fixup_stdcalls ();
-
 #if !defined(TARGET_IS_i386pep) && !defined(COFF_WITH_peAArch64)
   if (bfd_link_pic (&link_info))
 #else
diff --git a/ld/ld.texi b/ld/ld.texi
index bfc6ae00e3b..807c4d760c6 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -3501,6 +3501,8 @@  values by either a space or an equals sign.
 @item --add-stdcall-alias
 If given, symbols with a stdcall suffix (@@@var{nn}) will be exported
 as-is and also with the suffix stripped.
+Because PE+ targets have a single calling convention, symbols there
+are never decorated and this option is accepted but has no effect.
 [This option is specific to PE targeted ports of the linker]
 
 @kindex --base-file
@@ -3556,6 +3558,8 @@  to be usable.  If you specify @option{--enable-stdcall-fixup}, this
 feature is fully enabled and warnings are not printed.  If you specify
 @option{--disable-stdcall-fixup}, this feature is disabled and such
 mismatches are considered to be errors.
+Because PE+ targets have a single calling convention, symbols there
+are never decorated and these options are accepted but have no effect.
 [This option is specific to PE targeted ports of the linker]
 
 @kindex --leading-underscore
@@ -3625,6 +3629,8 @@  committed.
 @item --kill-at
 If given, the stdcall suffixes (@@@var{nn}) will be stripped from
 symbols before they are exported.
+Because PE+ targets have a single calling convention, symbols there
+are never decorated and this option is accepted but has no effect.
 [This option is specific to PE targeted ports of the linker]
 
 @kindex --large-address-aware