[PATCH] PE/COFF: raise normal PE section limit safely

Jan Beulich jbeulich@suse.com
Wed Jul 1 06:55:48 GMT 2026


On 30.06.2026 22:59, Oleg Tolmatcev wrote:
> 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>

Looks largely okay to me, there's just one concern I have:

> --- a/bfd/peXXigen.c
> +++ b/bfd/peXXigen.c
> @@ -109,6 +109,49 @@
>  #define SetHighBit(val)      ((val) | 0x80000000)
>  #define WithoutHighBit(val)  ((val) & 0x7fffffff)
>  

> +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;
> +    }
> +}

When coming here for objcopy, upon reading the input we blindly accept any
other values in the reserved range. Then ...

> +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:
> +      BFD_ASSERT (scnum > 0 && (unsigned int) scnum <= IMAGE_SYM_SECTION_MAX);

... upon writing we'd stumble over this assertion. Assertions really should
be about internal state only, not about input we consumed.

Jan


More information about the Binutils mailing list