https://sourceware.org/bugzilla/show_bug.cgi?id=34704

            Bug ID: 34704
           Summary: [PE/COFF][x86-64] ld -r produces duplicate .pdata
                    BeginAddress entries
           Product: binutils
           Version: 2.47
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: ld
          Assignee: unassigned at sourceware dot org
          Reporter: hodong at nimfsoft dot com
  Target Milestone: ---

Created attachment 17053
  --> https://sourceware.org/bugzilla/attachment.cgi?id=17053&action=edit
Standalone C reproducer for x86-64 PE/COFF .pdata duplicate BeginAddress
entries after ld -r

Environment
===========

Host:
  Linux 7.2.6+deb14-amd64 x86_64 GNU/Linux

Target:
  x86_64-w64-mingw32ucrt

Compiler:
  x86_64-w64-mingw32ucrt-gcc (GCC) 16

Linker:
  GNU ld (GNU Binutils) 2.47

Inspection tool:
  GNU objdump (GNU Binutils) 2.47


Reproducer
==========

The attached ld-r-pdata-reproducer.c is compiled as two translation units:

  x86_64-w64-mingw32ucrt-gcc -std=c11 -Wall -Wextra -Werror -O0 \
      -DPART_A -c ld-r-pdata-reproducer.c -o a.o

  x86_64-w64-mingw32ucrt-gcc -std=c11 -Wall -Wextra -Werror -O0 \
      -DPART_B -c ld-r-pdata-reproducer.c -o b.o

Create a relocatable partial-link object:

  x86_64-w64-mingw32ucrt-ld -r a.o b.o -o combined.o

Direct final link:

  x86_64-w64-mingw32ucrt-ld \
      --subsystem console -e entry \
      a.o b.o -o direct.exe

Final link through the partial-link object:

  x86_64-w64-mingw32ucrt-ld \
      --subsystem console -e entry \
      combined.o -o partial.exe

Inspect the runtime-function tables:

  x86_64-w64-mingw32ucrt-objdump -p direct.exe

  x86_64-w64-mingw32ucrt-objdump -p partial.exe


Observed result
===============

direct.exe contains these BeginAddress values:

  0000000140001000
  000000014000101d
  000000014000109b
  00000001400010e0
  000000014000116e
  00000001400011cd
  00000001400011ec

partial.exe contains:

  0000000140001000
  0000000140001000
  000000014000101d
  000000014000116e
  000000014000117b
  00000001400011cd
  00000001400011ec

For partial.exe, objdump prints:

  has same begin address as predecessor

The first and second .pdata entries in partial.exe therefore both have
BeginAddress 0x140001000.


Intermediate .pdata contents
============================

a.o:

  00000000 0000001d 00000000
  0000001d 0000009b 0000000c
  0000009b 000000dc 00000018

b.o:

  00000000 0000008e 00000000
  0000008e 000000ed 0000000c
  000000ed 0000010c 00000018
  0000010c 0000016d 00000024

After:

  x86_64-w64-mingw32ucrt-ld -r a.o b.o -o combined.o

combined.o contains:

  00000000 0000001d 00000000
  00000000 0000008e 00000000
  0000001d 0000009b 0000000c
  0000008e 000000ed 0000000c
  0000009b 000000dc 00000018
  000000ed 0000010c 00000018
  0000010c 0000016d 00000024

The relocation table in combined.o contains three
IMAGE_REL_AMD64_ADDR32NB relocations for each 12-byte .pdata record.


FreeBSD reproduction
====================

The same result was independently reproduced on:

  FreeBSD 15.1-RELEASE-p4 amd64
  FreeBSD Clang 19.1.7
  GNU ld (GNU Binutils) 2.47.20260726
  GNU objdump (GNU Binutils) 2.47.20260726

GNU Binutils was built on the FreeBSD host from binutils-2.47.tar.xz
with target x86_64-w64-mingw32.

The input objects were compiled with:

  cc --target=x86_64-w64-windows-gnu -fno-addrsig \
      -std=c11 -Wall -Wextra -Werror -O0 \
      -DPART_A -c ld-r-pdata-reproducer.c -o a.o

  cc --target=x86_64-w64-windows-gnu -fno-addrsig \
      -std=c11 -Wall -Wextra -Werror -O0 \
      -DPART_B -c ld-r-pdata-reproducer.c -o b.o

For direct.exe, the BeginAddress values were:

  0000000140001000
  0000000140001020
  0000000140001090
  00000001400010e0
  0000000140001120
  0000000140001170
  0000000140001190

For partial.exe:

  0000000140001000
  0000000140001000
  0000000140001020
  0000000140001120
  0000000140001170
  0000000140001170
  0000000140001190

objdump printed:

  has same begin address as predecessor

after both duplicate entries.

The compilation, partial-link, and both final-link commands completed
successfully.


Related reports
===============

PR ld/15041 was referenced by the 2017 PE ld -r script change:

  https://sourceware.org/pipermail/binutils-cvs/2017-February/044595.html

A 2026-03-25 PE-COFF partial-link patch reports
IMAGE_REL_AMD64_ADDR32NB relocation failures in .pdata after partial
linking of a C++/COMDAT/debug-info case:

  https://sourceware.org/pipermail/binutils/2026-March/148715.html

This reproducer uses C without debug information. Both final-link commands
complete, and the partial-link path produces duplicate .pdata
BeginAddress entries.


Additional application observation
==================================

The same direct-link/partial-link difference was originally observed in a
Windows x64 Ada application.

With GNU ld -r used as a partial linker:

  * the final executable contained 2,803 overlapping .pdata function ranges;
  * one partial-link object contained 421 interpreted .pdata ranges outside
    the code range represented by that object;
  * one overlap covered __gnat_raise_exception;
  * the process terminated during Ada exception propagation.

After rebuilding the static archive from the original object files without
the ld -r partial-link step:

  * the archive contained 82 original object members;
  * unwind-table checks reported no bounds or overlap findings;
  * the same native exception path reached its exception handler.

The standalone reproducer does not use Ada or GNAT runtime objects.


LLM assistance
==============

This report and the standalone reproducer were prepared with assistance
from OpenAI ChatGPT (GPT-5.6 Sol xhigh).

The reproduction commands and outputs reported above were executed against
the stated toolchains.

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to