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.