[llvm] Reland "[llvm-objcopy] Handle SHF_ALLOC relocation sections in relocatable files" (PR #228654)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Fri Oct 2 20:57:12 PDT 2026
https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/228654
This relands #227204, reverted by #228572 due to a UBSan failure in
compress-sections.s:
```
ELFObject.h:887:35: runtime error: load of value 190, which is not a valid value for type 'const bool'
```
--compress-sections on a relocation section creates a CompressedSection
that incorrectly keeps OriginalType SHT_RELA, so
RelocationSection::classof cast it and read
RelocationSectionBase::Dynamic. Set its OriginalType to SHT_PROGBITS,
which also fixes a TODO in Object::removeSections
Original description:
llvm-objcopy treats every SHF_ALLOC relocation section as dynamic and
requires its sh_link to refer to .dynsym. Linux livepatch modules
(ET_REL) contain SHF_ALLOC .klp.rela.* sections linked to .symtab
(SHF_ALLOC keeps them in module memory until the patched module loads),
so llvm-objcopy rejects them:
```
error: Link field value 4 in section .klp.rela.vmlinux.text is not a symbol table
```
A relocatable file has no dynamic symbol table, so treat all of its
relocation sections as static, as BFD does. Their symbol indexes are
then updated when the symbol table changes. Since SHF_ALLOC no longer
distinguishes RelocationSection from DynamicRelocationSection, record
the kind in RelocationSectionBase.
LLM-aided
>From fe3f92d38b5bff55622c1550b0315c450aeee09e Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Fri, 2 Oct 2026 08:40:00 -0700
Subject: [PATCH] Reland "[llvm-objcopy] Handle SHF_ALLOC relocation sections
in relocatable files"
This relands #227204, reverted by #228572 due to a UBSan failure in
compress-sections.s:
```
ELFObject.h:887:35: runtime error: load of value 190, which is not a valid value for type 'const bool'
```
--compress-sections on a relocation section creates a CompressedSection
that incorrectly keeps OriginalType SHT_RELA, so
RelocationSection::classof cast it and read
RelocationSectionBase::Dynamic. Set its OriginalType to SHT_PROGBITS,
which also fixes a TODO in Object::removeSections
Original description:
llvm-objcopy treats every SHF_ALLOC relocation section as dynamic and
requires its sh_link to refer to .dynsym. Linux livepatch modules
(ET_REL) contain SHF_ALLOC .klp.rela.* sections linked to .symtab
(SHF_ALLOC keeps them in module memory until the patched module loads),
so llvm-objcopy rejects them:
```
error: Link field value 4 in section .klp.rela.vmlinux.text is not a symbol table
```
A relocatable file has no dynamic symbol table, so treat all of its
relocation sections as static, as BFD does. Their symbol indexes are
then updated when the symbol table changes. Since SHF_ALLOC no longer
distinguishes RelocationSection from DynamicRelocationSection, record
the kind in RelocationSectionBase.
LLM-aided
---
llvm/lib/ObjCopy/ELF/ELFObject.cpp | 11 ++---
llvm/lib/ObjCopy/ELF/ELFObject.h | 20 +++++----
.../llvm-objcopy/ELF/reloc-alloc-symtab.test | 42 +++++++++++++++++++
.../llvm-objcopy/ELF/reloc-no-symtab.test | 7 +++-
4 files changed, 67 insertions(+), 13 deletions(-)
create mode 100644 llvm/test/tools/llvm-objcopy/ELF/reloc-alloc-symtab.test
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index ac818343f3f8f0..3fbc7f00e61e8a 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -564,6 +564,7 @@ CompressedSection::CompressedSection(const SectionBase &Sec,
Flags |= ELF::SHF_COMPRESSED;
OriginalFlags |= ELF::SHF_COMPRESSED;
+ OriginalType = ELF::SHT_PROGBITS;
size_t ChdrSize = Is64Bits ? sizeof(object::Elf_Chdr_Impl<object::ELF64LE>)
: sizeof(object::Elf_Chdr_Impl<object::ELF32LE>);
Size = ChdrSize + CompressedData.size();
@@ -1709,7 +1710,11 @@ Expected<SectionBase &> ELFBuilder<ELFT>::makeSection(const Elf_Shdr &Shdr) {
case SHT_REL:
case SHT_RELA:
case SHT_CREL:
- if (Shdr.sh_flags & SHF_ALLOC) {
+ // SHF_ALLOC relocations of an executable or shared object are copied
+ // verbatim. Relocatable files relocations are usually non-ALLOC, but Linux
+ // livepatch modules set SHF_ALLOC on .klp.rela.* to keep these static
+ // relocations in memory.
+ if ((Shdr.sh_flags & SHF_ALLOC) && ElfFile.getHeader().e_type != ET_REL) {
if (Expected<ArrayRef<uint8_t>> Data = ElfFile.getSectionContents(Shdr))
return Obj.addSection<DynamicRelocationSection>(*Data);
else
@@ -2211,10 +2216,6 @@ Error Object::removeSections(
std::begin(Sections), std::end(Sections), [=](const SecPtr &Sec) {
if (ToRemove(*Sec))
return false;
- // TODO: A compressed relocation section may be recognized as
- // RelocationSectionBase. We don't want such a section to be removed.
- if (isa<CompressedSection>(Sec))
- return true;
if (auto RelSec = dyn_cast<RelocationSectionBase>(Sec.get())) {
if (auto ToRelSec = RelSec->getSection())
return !ToRemove(*ToRelSec);
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 2783ef27ac9de4..a3131008c7bf03 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -38,6 +38,7 @@ class StringTableSection;
class SymbolTableSection;
class RelocationSection;
class DynamicRelocationSection;
+class DynamicSymbolTableSection;
class GnuDebugLinkSection;
class GroupSection;
class SectionIndexSection;
@@ -875,10 +876,15 @@ struct Relocation {
// and another which handles the symbol table type. The symbol table type is
// taken as a type parameter to the class (see RelocSectionWithSymtabBase).
class RelocationSectionBase : public SectionBase {
+ const bool Dynamic;
+
protected:
SectionBase *SecToApplyRel = nullptr;
+ explicit RelocationSectionBase(bool Dynamic) : Dynamic(Dynamic) {}
+
public:
+ bool isDynamic() const { return Dynamic; }
const SectionBase *getSection() const { return SecToApplyRel; }
void setSection(SectionBase *Sec) { SecToApplyRel = Sec; }
@@ -897,7 +903,9 @@ class RelocSectionWithSymtabBase : public RelocationSectionBase {
void setSymTab(SymTabType *SymTab) { Symbols = SymTab; }
protected:
- RelocSectionWithSymtabBase() = default;
+ RelocSectionWithSymtabBase()
+ : RelocationSectionBase(
+ std::is_same_v<SymTabType, DynamicSymbolTableSection>) {}
SymTabType *Symbols = nullptr;
@@ -928,9 +936,8 @@ class RelocationSection
const Object &getObject() const { return Obj; }
static bool classof(const SectionBase *S) {
- if (S->OriginalFlags & ELF::SHF_ALLOC)
- return false;
- return RelocationSectionBase::classof(S);
+ return RelocationSectionBase::classof(S) &&
+ !static_cast<const RelocationSectionBase *>(S)->isDynamic();
}
};
@@ -1015,9 +1022,8 @@ class DynamicRelocationSection
function_ref<bool(const SectionBase *)> ToRemove) override;
static bool classof(const SectionBase *S) {
- if (!(S->OriginalFlags & ELF::SHF_ALLOC))
- return false;
- return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA;
+ return RelocationSectionBase::classof(S) &&
+ static_cast<const RelocationSectionBase *>(S)->isDynamic();
}
};
diff --git a/llvm/test/tools/llvm-objcopy/ELF/reloc-alloc-symtab.test b/llvm/test/tools/llvm-objcopy/ELF/reloc-alloc-symtab.test
new file mode 100644
index 00000000000000..7409125961ee83
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/ELF/reloc-alloc-symtab.test
@@ -0,0 +1,42 @@
+## In a relocatable file, an SHF_ALLOC relocation section (e.g. .klp.rela.* in
+## Linux livepatch modules) is a static relocation section: its symbol indexes
+## are updated and the symbols it references are kept.
+
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-strip --strip-unneeded %t -o %t1
+# RUN: llvm-readelf -Sr -s %t1 | FileCheck %s
+# RUN: yaml2obj -DTYPE=SHT_CREL %s -o %t.crel
+# RUN: llvm-strip --strip-unneeded %t.crel -o %t1.crel
+# RUN: llvm-readelf -Sr -s %t1.crel | FileCheck %s
+
+# CHECK: [ 2] .klp.rela.vmlinux.text {{RELA|CREL}} {{.*}} AI 3 1
+# CHECK-NEXT: [ 3] .symtab
+# CHECK: R_X86_64_PC32 {{.*}} referenced + 0
+# CHECK: Symbol table '.symtab' contains 2 entries:
+# CHECK: 1: {{.*}} UND referenced
+
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Content: "e800000000"
+ - Name: .klp.rela.vmlinux.text
+ Type: [[TYPE=SHT_RELA]]
+ Flags: [ SHF_ALLOC, SHF_INFO_LINK ]
+ Link: .symtab
+ Info: .text
+ Relocations:
+ - Offset: 1
+ Symbol: referenced
+ Type: R_X86_64_PC32
+Symbols:
+ - Name: unreferenced
+ Section: .text
+ - Name: referenced
+ Binding: STB_GLOBAL
diff --git a/llvm/test/tools/llvm-objcopy/ELF/reloc-no-symtab.test b/llvm/test/tools/llvm-objcopy/ELF/reloc-no-symtab.test
index 90f47c6d227fd3..71fb9e672b04e8 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/reloc-no-symtab.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/reloc-no-symtab.test
@@ -10,11 +10,16 @@
# RUN: llvm-readobj --sections %t.original | FileCheck %s
# RUN: llvm-readobj --sections %t.stripped | FileCheck %s
+## In a relocatable file, .rela.plt is a static relocation section.
+# RUN: yaml2obj -DTYPE=ET_REL %s -o %t.rel
+# RUN: llvm-strip %t.rel -o %t.rel.stripped
+# RUN: llvm-readobj --sections %t.rel.stripped | FileCheck %s
+
!ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
- Type: ET_REL
+ Type: [[TYPE=ET_EXEC]]
Machine: EM_X86_64
Sections:
- Name: .text
More information about the llvm-commits
mailing list