]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
PE/COFF: raise normal PE section limit safely
authorOleg Tolmatcev <oleg.tolmatcev@gmail.com>
Fri, 17 Jul 2026 13:39:11 +0000 (15:39 +0200)
committerJan Beulich <jbeulich@suse.com>
Fri, 17 Jul 2026 13:39:11 +0000 (15:39 +0200)
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.

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.

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.

bfd/ChangeLog:

* coffcode.h (COFF_DEFAULT_MAX_NSCNS): Define.
(bfd_coff_std_swap_table): Use it for the default maximum section
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.

include/ChangeLog:

* coff/pe.h (IMAGE_SYM_UNDEFINED): Define.
(IMAGE_SYM_ABSOLUTE): Define.
(IMAGE_SYM_DEBUG): Define.
(IMAGE_SYM_SECTION_MAX): Define.

gas/ChangeLog:

* 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.

Signed-off-by: Oleg Tolmatcev <oleg.tolmatcev@gmail.com>
bfd/coffcode.h
bfd/peXXigen.c
gas/testsuite/gas/pe/large-obj-normal.d [new file with mode: 0644]
gas/testsuite/gas/pe/large-obj-normal.s [new file with mode: 0644]
gas/testsuite/gas/pe/pe.exp
include/coff/pe.h

index 12cc7c3ca791134655ccdccc4c0934034e96fe1a..267c6ce2782bb1ecf287ccc26351ddf57c303df8 100644 (file)
@@ -376,6 +376,12 @@ extern const bfd_target TARGET_SYM_BIG;
 # define COFF_WITH_EXTENDED_RELOC_COUNTER
 #endif
 
+#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 =
 #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 =
 #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 =
 #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,
index f604a84ec0adff17ef0ee3ba0cd748450dc784ea..9794ecdaabe050f48c5f07688ea09a8b71af38b0 100644 (file)
 #define SetHighBit(val)      ((val) | 0x80000000)
 #define WithoutHighBit(val)  ((val) & 0x7fffffff)
 \f
+static int
+pe_decode_sym_section_number (bfd *abfd, const char *raw_scnum)
+{
+  unsigned int scnum = 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 = IMAGE_SYM_UNDEFINED;
+      break;
+    case N_ABS:
+      encoded_scnum = IMAGE_SYM_ABSOLUTE;
+      break;
+    case N_DEBUG:
+      encoded_scnum = IMAGE_SYM_DEBUG;
+      break;
+    default:
+      encoded_scnum = 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 * in1)
     memcpy (in->_n._n_name, ext->e.e_name, SYMNMLEN);
 
   in->n_value = H_GET_32 (abfd, ext->e_value);
-  in->n_scnum = (short) H_GET_16 (abfd, ext->e_scnum);
+  in->n_scnum = pe_decode_sym_section_number (abfd, ext->e_scnum);
 
   if (sizeof (ext->e_type) == 2)
     in->n_type = H_GET_16 (abfd, ext->e_type);
@@ -262,7 +304,7 @@ _bfd_XXi_swap_sym_out (bfd * abfd, void * inp, void * extp)
     }
 
   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);
 
   if (sizeof (ext->e_type) == 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 (file)
index 0000000..e9009e5
--- /dev/null
@@ -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 (file)
index 0000000..2547330
--- /dev/null
@@ -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
index 82f217cf9485e157f689ff517542565f738ab289..600fd2425a462f09b66856ae3757d37a40708004 100644 (file)
@@ -71,6 +71,7 @@ if {[istarget "aarch64-*-pe*"] || [istarget "aarch64-*-mingw*"]} {
 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"
 }
index ece4bf25db1ea2dbc2706af199698edf5d6d69f9..133548ff4baeb15ebf7ed1962b9fa6ecf7c0a586 100644 (file)
@@ -388,6 +388,11 @@ struct external_SYMBOL_EX
   char e_numaux[1];
 } ATTRIBUTE_PACKED ;
 
+#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