[binutils-gdb] PE/COFF: raise normal PE section limit safely

Jan Beulich via Binutils-cvs <[email protected]> Fri, 17 Jul 2026 13:41:17 +0000 (GMT)
Newsgroups gmane.comp.gnu.binutils.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=3Dbinutils-gdb.git;h=3Dd9d09c878e56=
48501e33a3f07950c633ca70b298

commit d9d09c878e5648501e33a3f07950c633ca70b298
Author: Oleg Tolmatcev <[email protected]>
Date:   Fri Jul 17 15:39:11 2026 +0200

    PE/COFF: raise normal PE section limit safely
   =20
    PE/COFF stores symbol section numbers in a 16-bit field.  Binutils used
    signed 16-bit handling there, which limited normal PE objects to 32767
    sections even though MSVC and Clang already accept a larger unsigned
    range.
   =20
    Raise the normal PE section limit to 65279, while keeping the PE/COFF
    special section-number values for undefined, absolute and debug symbols
    working correctly.  Do this by decoding and encoding normal PE symbol
    section numbers as unsigned values in the ordinary range, but preserving
    the reserved PE constants explicitly.
   =20
    Also add a gas test that exercises a normal PE object above the old
    32767-section limit and checks that objdump reports the high section
    number correctly.
   =20
    bfd/ChangeLog:
   =20
            * coffcode.h (COFF_DEFAULT_MAX_NSCNS): Define.
            (bfd_coff_std_swap_table): Use it for the default maximum secti=
on
            count.
            (ticoff0_swap_table): Likewise.
            (ticoff1_swap_table): Likewise.
            * peXXigen.c (pe_decode_sym_section_number): New function.
            (pe_encode_sym_section_number): New function.
            (_bfd_XXi_swap_sym_in): Use pe_decode_sym_section_number.
            (_bfd_XXi_swap_sym_out): Use pe_encode_sym_section_number.
   =20
    include/ChangeLog:
   =20
            * coff/pe.h (IMAGE_SYM_UNDEFINED): Define.
            (IMAGE_SYM_ABSOLUTE): Define.
            (IMAGE_SYM_DEBUG): Define.
            (IMAGE_SYM_SECTION_MAX): Define.
   =20
    gas/ChangeLog:
   =20
            * testsuite/gas/pe/pe.exp: Run large-obj-normal.
            * testsuite/gas/pe/large-obj-normal.s: New test.
            * testsuite/gas/pe/large-obj-normal.d: New test.
   =20
    Signed-off-by: Oleg Tolmatcev <[email protected]>

Diff:
---
 bfd/coffcode.h                          | 12 ++++++---
 bfd/peXXigen.c                          | 46 +++++++++++++++++++++++++++++=
++--
 gas/testsuite/gas/pe/large-obj-normal.d | 17 ++++++++++++
 gas/testsuite/gas/pe/large-obj-normal.s | 16 ++++++++++++
 gas/testsuite/gas/pe/pe.exp             |  1 +
 include/coff/pe.h                       |  5 ++++
 6 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/bfd/coffcode.h b/bfd/coffcode.h
index 12cc7c3ca79..267c6ce2782 100644
--- a/bfd/coffcode.h
+++ b/bfd/coffcode.h
@@ -376,6 +376,12 @@ extern const bfd_target TARGET_SYM_BIG;
 # define COFF_WITH_EXTENDED_RELOC_COUNTER
 #endif
=20
+#if defined(COFF_WITH_PE) && !defined(COFF_IMAGE_WITH_PE)
+# define COFF_DEFAULT_MAX_NSCNS (IMAGE_SYM_SECTION_MAX + 1)
+#else
+# define COFF_DEFAULT_MAX_NSCNS 32768
+#endif
+
 #if defined (COFF_LONG_SECTION_NAMES)
 /* Needed to expand the inputs to BLANKOR1TOODD.  */
 #define COFFLONGSECTIONCATHELPER(x,y)    x ## y
@@ -5615,7 +5621,7 @@ static const bfd_coff_backend_data bfd_coff_std_swap_=
table ATTRIBUTE_UNUSED =3D
 #else
   2,
 #endif
-  32768,
+  COFF_DEFAULT_MAX_NSCNS,
   coff_SWAP_filehdr_in, coff_SWAP_aouthdr_in, coff_SWAP_scnhdr_in,
   coff_SWAP_reloc_in, coff_bad_format_hook, coff_set_arch_mach_hook,
   coff_mkobject_hook, styp_to_sec_flags, coff_set_alignment_hook,
@@ -5656,7 +5662,7 @@ static const bfd_coff_backend_data ticoff0_swap_table=
 =3D
 #else
   2,
 #endif
-  32768,
+  COFF_DEFAULT_MAX_NSCNS,
   coff_SWAP_filehdr_in, coff_SWAP_aouthdr_in, coff_SWAP_scnhdr_in,
   coff_swap_reloc_v0_in, ticoff0_bad_format_hook, coff_set_arch_mach_hook,
   coff_mkobject_hook, styp_to_sec_flags, coff_set_alignment_hook,
@@ -5698,7 +5704,7 @@ static const bfd_coff_backend_data ticoff1_swap_table=
 =3D
 #else
   2,
 #endif
-  32768,
+  COFF_DEFAULT_MAX_NSCNS,
   coff_SWAP_filehdr_in, coff_SWAP_aouthdr_in, coff_SWAP_scnhdr_in,
   coff_SWAP_reloc_in, ticoff1_bad_format_hook, coff_set_arch_mach_hook,
   coff_mkobject_hook, styp_to_sec_flags, coff_set_alignment_hook,
diff --git a/bfd/peXXigen.c b/bfd/peXXigen.c
index f604a84ec0a..9794ecdaabe 100644
--- a/bfd/peXXigen.c
+++ b/bfd/peXXigen.c
@@ -109,6 +109,48 @@
 #define SetHighBit(val)      ((val) | 0x80000000)
 #define WithoutHighBit(val)  ((val) & 0x7fffffff)
 =0C
+static int
+pe_decode_sym_section_number (bfd *abfd, const char *raw_scnum)
+{
+  unsigned int scnum =3D H_GET_16 (abfd, raw_scnum);
+
+  switch (scnum)
+    {
+    case IMAGE_SYM_UNDEFINED:
+      return N_UNDEF;
+    case IMAGE_SYM_ABSOLUTE:
+      return N_ABS;
+    case IMAGE_SYM_DEBUG:
+      return N_DEBUG;
+    default:
+      return scnum;
+    }
+}
+
+static void
+pe_encode_sym_section_number (bfd *abfd, int scnum, char *raw_scnum)
+{
+  unsigned int encoded_scnum;
+
+  switch (scnum)
+    {
+    case N_UNDEF:
+      encoded_scnum =3D IMAGE_SYM_UNDEFINED;
+      break;
+    case N_ABS:
+      encoded_scnum =3D IMAGE_SYM_ABSOLUTE;
+      break;
+    case N_DEBUG:
+      encoded_scnum =3D IMAGE_SYM_DEBUG;
+      break;
+    default:
+      encoded_scnum =3D scnum;
+      break;
+    }
+
+  H_PUT_16 (abfd, encoded_scnum, raw_scnum);
+}
+
 void
 _bfd_XXi_swap_sym_in (bfd * abfd, void * ext1, void * in1)
 {
@@ -124,7 +166,7 @@ _bfd_XXi_swap_sym_in (bfd * abfd, void * ext1, void * i=
n1)
     memcpy (in->_n._n_name, ext->e.e_name, SYMNMLEN);
=20
   in->n_value =3D H_GET_32 (abfd, ext->e_value);
-  in->n_scnum =3D (short) H_GET_16 (abfd, ext->e_scnum);
+  in->n_scnum =3D pe_decode_sym_section_number (abfd, ext->e_scnum);
=20
   if (sizeof (ext->e_type) =3D=3D 2)
     in->n_type =3D H_GET_16 (abfd, ext->e_type);
@@ -262,7 +304,7 @@ _bfd_XXi_swap_sym_out (bfd * abfd, void * inp, void * e=
xtp)
     }
=20
   H_PUT_32 (abfd, in->n_value, ext->e_value);
-  H_PUT_16 (abfd, in->n_scnum, ext->e_scnum);
+  pe_encode_sym_section_number (abfd, in->n_scnum, ext->e_scnum);
=20
   if (sizeof (ext->e_type) =3D=3D 2)
     H_PUT_16 (abfd, in->n_type, ext->e_type);
diff --git a/gas/testsuite/gas/pe/large-obj-normal.d b/gas/testsuite/gas/pe=
/large-obj-normal.d
new file mode 100644
index 00000000000..e9009e57953
--- /dev/null
+++ b/gas/testsuite/gas/pe/large-obj-normal.d
@@ -0,0 +1,17 @@
+#objdump: -h -t
+#name: PE large normal object after raising the normal section limit
+
+# This only becomes meaningful once normal PE/COFF is allowed past the
+# old 32767-section cutoff.  Before that, BFD errors out or auto-promotes
+# to bigobj before the normal 16-bit symbol-section encoding is exercised.
+
+.*: *file format pe-(aarch64-little|i386|x86-64)
+
+Sections:
+#...
+40002 +\.data\$a39999  .*
+                  CONTENTS, ALLOC, LOAD, DATA
+
+SYMBOL TABLE:
+#...
+.*\(sec 40003\).*\(scl +2\).*\) 0x[0-9a-f]+ a39999$
diff --git a/gas/testsuite/gas/pe/large-obj-normal.s b/gas/testsuite/gas/pe=
/large-obj-normal.s
new file mode 100644
index 00000000000..254733023d9
--- /dev/null
+++ b/gas/testsuite/gas/pe/large-obj-normal.s
@@ -0,0 +1,16 @@
+	.file "large-obj-normal.s"
+
+	.irp n,0,1,2,3
+	.irp m,0,1,2,3,4,5,6,7,8,9
+	.irp c,0,1,2,3,4,5,6,7,8,9
+	.irp d,0,1,2,3,4,5,6,7,8,9
+	.irp u,0,1,2,3,4,5,6,7,8,9
+	.globl a\n\m\c\d\u
+	.section .data$a\n\m\c\d\u,"w"
+a\n\m\c\d\u :
+	.byte 1
+	.endr
+	.endr
+	.endr
+	.endr
+	.endr
diff --git a/gas/testsuite/gas/pe/pe.exp b/gas/testsuite/gas/pe/pe.exp
index 82f217cf948..600fd2425a4 100644
--- a/gas/testsuite/gas/pe/pe.exp
+++ b/gas/testsuite/gas/pe/pe.exp
@@ -71,6 +71,7 @@ if {[istarget "aarch64-*-pe*"] || [istarget "aarch64-*-mi=
ngw*"]} {
 if { [istarget "aarch64-*-*"]
      || ([istarget "i*86-*-*"] && ![istarget "*-*-interix*"])
      || [istarget "x86_64-*-*"] } then {
+	run_dump_test "large-obj-normal"
 	run_dump_test "big-obj"
 	run_dump_test "big-obj-auto"
 }
diff --git a/include/coff/pe.h b/include/coff/pe.h
index ece4bf25db1..133548ff4ba 100644
--- a/include/coff/pe.h
+++ b/include/coff/pe.h
@@ -388,6 +388,11 @@ struct external_SYMBOL_EX
   char e_numaux[1];
 } ATTRIBUTE_PACKED ;
=20
+#define IMAGE_SYM_UNDEFINED      0
+#define IMAGE_SYM_ABSOLUTE       0xffff
+#define IMAGE_SYM_DEBUG          0xfffe
+#define IMAGE_SYM_SECTION_MAX    0xfeff
+
 #define	SYMENT_BIGOBJ	struct external_SYMBOL_EX
 #define	SYMESZ_BIGOBJ	20