https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/229026
Backport cf40a83cb398897d21bb54c5a7a2eecb8d0633b3 Requested by: @mstorsjo >From 58f48c3434e7485b7d7d77e291d6e45020f6e6de Mon Sep 17 00:00:00 2001 From: BtbN <[email protected]> Date: Mon, 5 Oct 2026 00:02:43 +0200 Subject: [PATCH] [llvm-objcopy][COFF] Keep COMDAT section definition symbols when stripping (#228783) --strip-unneeded and --discard-all removed the section definition symbol of a COMDAT section if no relocation referenced it directly, which is the common case since relocations target the COMDAT leader. The aux record of that symbol carries the COMDAT selection; without it lld discards the section as a leaderless COMDAT and silently drops relocations against it. This broke e.g. .refptr.* sections in MinGW objects, producing executables that load garbage instead of the referenced pointer. Assisted-by: Claude Opus 5.5 (cherry picked from commit cf40a83cb398897d21bb54c5a7a2eecb8d0633b3) --- llvm/lib/ObjCopy/COFF/COFFObjcopy.cpp | 16 ++++ .../COFF/strip-unneeded-comdat.test | 94 +++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 llvm/test/tools/llvm-objcopy/COFF/strip-unneeded-comdat.test diff --git a/llvm/lib/ObjCopy/COFF/COFFObjcopy.cpp b/llvm/lib/ObjCopy/COFF/COFFObjcopy.cpp index 48c488826c47d..96de61737d78f 100644 --- a/llvm/lib/ObjCopy/COFF/COFFObjcopy.cpp +++ b/llvm/lib/ObjCopy/COFF/COFFObjcopy.cpp @@ -32,6 +32,19 @@ static bool isDebugSection(const Section &Sec) { return Sec.Name.starts_with(".debug"); } +// The COMDAT selection is stored in the section definition symbol's aux +// record, so it must be kept even if no relocation references it. +static bool isComdatSectionDefinition(const Object &Obj, const Symbol &Sym) { + if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && Sym.Sym.Value == 0 && + Sym.Sym.NumberOfAuxSymbols != 0 && Sym.TargetSectionId > 0) { + // Looks like a section definition symbol, check if it is a COMDAT. + const Section *Sec = Obj.findSection(Sym.TargetSectionId); + return Sec && Sec->Name == Sym.Name && + (Sec->Header.Characteristics & IMAGE_SCN_LNK_COMDAT); + } + return false; +} + static uint64_t getNextRVA(const Object &Obj) { if (Obj.getSections().empty()) return 0; @@ -233,6 +246,9 @@ static Error handleArgs(const CommonConfig &Config, return true; } + if (isComdatSectionDefinition(Obj, Sym)) + return false; + if (!Sym.Referenced) { // With --strip-unneeded, GNU objcopy removes all unreferenced local // symbols, and any unreferenced undefined external. diff --git a/llvm/test/tools/llvm-objcopy/COFF/strip-unneeded-comdat.test b/llvm/test/tools/llvm-objcopy/COFF/strip-unneeded-comdat.test new file mode 100644 index 0000000000000..8af57d031ee87 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/COFF/strip-unneeded-comdat.test @@ -0,0 +1,94 @@ +## Check that --strip-unneeded and --discard-all keep the section definition +## symbol of a COMDAT section, even if no relocation references it. It carries +## the COMDAT selection, without it the linker discards the section. + +# RUN: yaml2obj %s -o %t.in.o +# RUN: llvm-objcopy --strip-unneeded %t.in.o %t.strip-unneeded.o +# RUN: llvm-readobj --symbols %t.strip-unneeded.o | FileCheck %s +# RUN: llvm-objcopy --discard-all %t.in.o %t.discard-all.o +# RUN: llvm-readobj --symbols %t.discard-all.o | FileCheck %s + +# CHECK: Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: .rdata$.refptr.foo +# CHECK-NEXT: Value: 0 +# CHECK-NEXT: Section: .rdata$.refptr.foo (2) +# CHECK-NEXT: BaseType: Null (0x0) +# CHECK-NEXT: ComplexType: Null (0x0) +# CHECK-NEXT: StorageClass: Static (0x3) +# CHECK-NEXT: AuxSymbolCount: 1 +# CHECK-NEXT: AuxSectionDef { +# CHECK-NEXT: Length: 8 +# CHECK-NEXT: RelocationCount: 1 +# CHECK-NEXT: LineNumberCount: 0 +# CHECK-NEXT: Checksum: 0x0 +# CHECK-NEXT: Number: 2 +# CHECK-NEXT: Selection: Any (0x2) +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: .refptr.foo +# CHECK: Symbol { +# CHECK-NEXT: Name: foo +# CHECK-NOT: Name: .text + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 488B0500000000C3 + Relocations: + - VirtualAddress: 3 + SymbolName: .refptr.foo + Type: IMAGE_REL_AMD64_REL32 + - Name: '.rdata$.refptr.foo' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ] + Alignment: 8 + SectionData: '0000000000000000' + Relocations: + - VirtualAddress: 0 + SymbolName: foo + Type: IMAGE_REL_AMD64_ADDR64 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: '.rdata$.refptr.foo' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: .refptr.foo + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: foo + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
