[binutils-gdb] PE-COFF: Fix link failure of C++ code with debug info after partial linking

Eric Botcazou ebotcazou@sourceware.org
Mon Mar 30 17:19:41 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=84acfa08c7a40f01b4ae254cc4658e5865edf3c6

commit 84acfa08c7a40f01b4ae254cc4658e5865edf3c6
Author: Eric Botcazou <ebotcazou@gcc.gnu.org>
Date:   Mon Mar 30 19:10:26 2026 +0200

    PE-COFF: Fix link failure of C++ code with debug info after partial linking
    
    If you apply the following recipe to the attached C/C++ files with a PE-COFF
    toolchain, you get the specified output:
    
    1. g++ -c clib.cpp cpplib.cpp test.c -g
    2. g++ -o pl.o clib.o cpplib.o -nostdlib -Wl,-r
    3. objcopy pl.o
    objcopy.exe: pl.o: warning: COMDAT symbol
    '.debug_frame$_ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev' does not match
    section name '.debug_frame'
    4. g++ -o test test.o pl.o
    ld.exe: pl.o: warning: COMDAT symbol
    '.debug_frame$_ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev' does not match
    section name '.debug_frame'
    pl.o:clib.cpp:(.pdata$_ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPiy+0x0):
    relocation truncated to fit: IMAGE_REL_AMD64_ADDR32NB against
    `.text$_ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPiy'
    pl.o:clib.cpp:(.pdata$_ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPiy+0x4):
    relocation truncated to fit: IMAGE_REL_AMD64_ADDR32NB against
    `.text$_ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPiy'
    pl.o:clib.cpp:(.pdata$_ZNSt12_Vector_baseIiSaIiEE19_M_get_Tp_allocatorEv+0x0):
    relocation truncated to fit: IMAGE_REL_AMD64_ADDR32NB against
    `.text$_ZNSt12_Vector_baseIiSaIiEE19_M_get_Tp_allocatorEv'
    pl.o:clib.cpp:(.pdata$_ZNSt12_Vector_baseIiSaIiEE19_M_get_Tp_allocatorEv+0x4):
    relocation truncated to fit: IMAGE_REL_AMD64_ADDR32NB against
    `.text$_ZNSt12_Vector_baseIiSaIiEE19_M_get_Tp_allocatorEv'
    pl.o:clib.cpp:(.pdata$_ZNSt15__new_allocatorIiE10deallocateEPiy+0x0):
    relocation truncated to fit: IMAGE_REL_AMD64_ADDR32NB against
    `.text$_ZNSt15__new_allocatorIiE10deallocateEPiy'
    pl.o:clib.cpp:(.pdata$_ZNSt15__new_allocatorIiE10deallocateEPiy+0x4):
    relocation truncated to fit: IMAGE_REL_AMD64_ADDR32NB against
    `.text$_ZNSt15__new_allocatorIiE10deallocateEPiy'
    collect2.exe: error: ld returned 1 exit status
    
    The problem pertains to section symbols generated for COMDAT sections: they
    are marked as local symbols as per Microsoft's PE-COFF specification, but
    partial linking discards the duplicate COMDAT sections without being able
    to either merge them, or remove them when they are used in a relocation.
    
    So they end up as undefined local symbols after the partial link, which in
    turn may cause the final link to fail (in practice you need e.g. a call to
    objcopy in between, because it moves them to the end of the symbol list).
    
    This change instructs the linker to "relocate" them instead, that is to say
    to attach them to the one COMDAT section that is output among the multiple
    COMDAT sections that are duplicate.  It also prevents partial linking from
    prematurely globing the .[z]debug_frame* sections together, as already done
    for the .eh_frame* sections.
    
    bfd/
            * cofflink.c (_bfd_coff_link_input_bfd): For a relocatable output,
            relocate section symbols for input sections that are not going to
            be emitted because they are duplicate of another one in the link.
    ld/
            * scripttempl/pe.sc (.debug_frame): Do not glob all .debug_frame*
            sections together when not relocating.
            (.zdebug_frame): Likewise for .zdebug_frame* sections.
            * scripttempl/pep.sc (.debug_frame): Likewise.
            (.zdebug_frame): Likewise.

Diff:
---
 bfd/cofflink.c        | 22 ++++++++++++++++++----
 ld/scripttempl/pe.sc  |  4 ++--
 ld/scripttempl/pep.sc |  4 ++--
 3 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/bfd/cofflink.c b/bfd/cofflink.c
index 3504b2d4a0c..e5c8a987d69 100644
--- a/bfd/cofflink.c
+++ b/bfd/cofflink.c
@@ -1857,12 +1857,26 @@ _bfd_coff_link_input_bfd (struct coff_final_link_info *flaginfo, bfd *input_bfd)
 	      /* Compute new symbol location.  */
 	    if (isym.n_scnum > 0)
 	      {
-		isym.n_scnum = (*secpp)->output_section->target_index;
-		isym.n_value += (*secpp)->output_offset;
+		const asection *s = *secpp;
+
+		/* Relocate section symbols for sections that are not going to
+		   be emitted because they are duplicate of another one.  */
+		if (bfd_link_relocatable (flaginfo->info)
+		    && isym.n_sclass == C_STAT
+		    && isym.n_type == T_NULL
+		    && isym.n_numaux > 0
+		    && (s->output_section == bfd_abs_section_ptr
+			|| bfd_section_removed_from_list
+			   (output_bfd, s->output_section))
+		    && s->kept_section)
+		  s = s->kept_section;
+
+		isym.n_scnum = s->output_section->target_index;
+		isym.n_value += s->output_offset;
 		if (! obj_pe (input_bfd))
-		  isym.n_value -= (*secpp)->vma;
+		  isym.n_value -= s->vma;
 		if (! obj_pe (flaginfo->output_bfd))
-		  isym.n_value += (*secpp)->output_section->vma;
+		  isym.n_value += s->output_section->vma;
 	      }
 	    break;
 
diff --git a/ld/scripttempl/pe.sc b/ld/scripttempl/pe.sc
index f8d7c8ef6e7..a669f279eda 100644
--- a/ld/scripttempl/pe.sc
+++ b/ld/scripttempl/pe.sc
@@ -364,11 +364,11 @@ SECTIONS
 
   .debug_frame ${RELOCATING+BLOCK(__section_alignment__)} ${RELOCATING+(NOLOAD)} :
   {
-    *(.debug_frame*)
+    *(.debug_frame${RELOCATING+*})
   }
   .zdebug_frame ${RELOCATING+BLOCK(__section_alignment__)} ${RELOCATING+(NOLOAD)} :
   {
-    *(.zdebug_frame*)
+    *(.zdebug_frame${RELOCATING+*})
   }
 
   .debug_str ${RELOCATING+BLOCK(__section_alignment__)} ${RELOCATING+(NOLOAD)} :
diff --git a/ld/scripttempl/pep.sc b/ld/scripttempl/pep.sc
index a5b4679544c..45e785492c5 100644
--- a/ld/scripttempl/pep.sc
+++ b/ld/scripttempl/pep.sc
@@ -373,11 +373,11 @@ SECTIONS
 
   .debug_frame ${RELOCATING+BLOCK(__section_alignment__)} ${RELOCATING+(NOLOAD)} :
   {
-    *(.debug_frame*)
+    *(.debug_frame${RELOCATING+*})
   }
   .zdebug_frame ${RELOCATING+BLOCK(__section_alignment__)} ${RELOCATING+(NOLOAD)} :
   {
-    *(.zdebug_frame*)
+    *(.zdebug_frame${RELOCATING+*})
   }
 
   .debug_str ${RELOCATING+BLOCK(__section_alignment__)} ${RELOCATING+(NOLOAD)} :


More information about the Binutils-cvs mailing list