Skip to content

[llvm-objcopy] Route section copies through writeSectionContents - #217974

Open
fzakaria wants to merge 1 commit into
mainfrom
users/fmzakari/llvm-objcopy-section-writes
Open

[llvm-objcopy] Route section copies through writeSectionContents#217974
fzakaria wants to merge 1 commit into
mainfrom
users/fmzakari/llvm-objcopy-section-writes

Conversation

@fzakaria

@fzakaria fzakaria commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Motivation

The ELF section writers currently copy section contents directly into a WritableMemoryBuffer. Before introducing an alternative output implementation, these writes need to go through a common function.

Change

This change adds a virtual writeSectionContents helper and routes the section byte-copy paths through it. The default implementation continues to write into the existing memory buffer, so this does not change the output strategy by itself.

While routing SHT_SYMTAB_SHNDX through the helper, the indexes are materialized using the target Elf_Word type. This ensures that the entries use the output ELF endianness when converting between formats. The big-endian test case exercises this behavior.

This change was developed with assistance from OpenAI Codex and includes a trailer following the AI policy.

Centralize section byte copies behind a virtual helper so ELF output can later target either a memory buffer or a seekable stream. Materialize SHT_SYMTAB_SHNDX entries using the target ELF word type to preserve output endianness.

Co-authored-by: Jeremy Braun <jtbraun@meta.com>

Assisted-by: OpenAI Codex
@fzakaria
fzakaria marked this pull request as ready for review August 21, 2026 16:50
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-binary-utilities

Author: Farid Zakaria (fzakaria)

Changes

Motivation

The ELF section writers currently copy section contents directly into a WritableMemoryBuffer. Before introducing an alternative output implementation, these writes need to go through a common function.

Change

This change adds a virtual writeSectionContents helper and routes the section byte-copy paths through it. The default implementation continues to write into the existing memory buffer, so this does not change the output strategy by itself.

While routing SHT_SYMTAB_SHNDX through the helper, the indexes are materialized using the target Elf_Word type. This ensures that the entries use the output ELF endianness when converting between formats. The big-endian test case exercises this behavior.

This change was developed with assistance from OpenAI Codex and includes a trailer following the AI policy.


Full diff: https://github.com/llvm/llvm-project/pull/217974.diff

3 Files Affected:

  • (modified) llvm/lib/ObjCopy/ELF/ELFObject.cpp (+26-15)
  • (modified) llvm/lib/ObjCopy/ELF/ELFObject.h (+1)
  • (modified) llvm/test/tools/llvm-objcopy/ELF/many-sections.test (+4)
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
index ac818343f3f8f..dace67fdca44d 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp
@@ -183,9 +183,14 @@ Error BinarySectionWriter::visit(const GroupSection &Sec) {
                            "cannot write '" + Sec.Name + "' out to binary");
 }
 
+void SectionWriter::writeSectionContents(ArrayRef<uint8_t> Data,
+                                         uint64_t Offset) {
+  llvm::copy(Data, Out.getBufferStart() + Offset);
+}
+
 Error SectionWriter::visit(const Section &Sec) {
   if (Sec.Type != SHT_NOBITS)
-    llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset);
+    writeSectionContents(Sec.Contents, Sec.Offset);
 
   return Error::success();
 }
@@ -453,7 +458,7 @@ void Section::restoreSymTabLink(SymbolTableSection &SymTab) {
 }
 
 Error SectionWriter::visit(const OwnedDataSection &Sec) {
-  llvm::copy(Sec.Data, Out.getBufferStart() + Sec.Offset);
+  writeSectionContents(Sec.Data, Sec.Offset);
   return Error::success();
 }
 
@@ -487,9 +492,7 @@ Error ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) {
                              "failed to decompress section '" + Sec.Name +
                                  "': " + toString(std::move(E)));
 
-  uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
-  llvm::copy(Decompressed, Buf);
-
+  writeSectionContents(Decompressed, Sec.Offset);
   return Error::success();
 }
 
@@ -532,11 +535,10 @@ Error BinarySectionWriter::visit(const CompressedSection &Sec) {
 
 template <class ELFT>
 Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) {
-  uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
   Elf_Chdr_Impl<ELFT> Chdr = {};
   switch (Sec.CompressionType) {
   case DebugCompressionType::None:
-    llvm::copy(Sec.OriginalData, Buf);
+    writeSectionContents(Sec.OriginalData, Sec.Offset);
     return Error::success();
   case DebugCompressionType::Zlib:
     Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB;
@@ -547,10 +549,10 @@ Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) {
   }
   Chdr.ch_size = Sec.DecompressedSize;
   Chdr.ch_addralign = Sec.DecompressedAlign;
-  memcpy(Buf, &Chdr, sizeof(Chdr));
-  Buf += sizeof(Chdr);
-
-  llvm::copy(Sec.CompressedData, Buf);
+  writeSectionContents(
+      ArrayRef(reinterpret_cast<const uint8_t *>(&Chdr), sizeof(Chdr)),
+      Sec.Offset);
+  writeSectionContents(Sec.CompressedData, Sec.Offset + sizeof(Chdr));
   return Error::success();
 }
 
@@ -613,8 +615,14 @@ Error StringTableSection::accept(MutableSectionVisitor &Visitor) {
 
 template <class ELFT>
 Error ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) {
-  uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset;
-  llvm::copy(Sec.Indexes, reinterpret_cast<Elf_Word *>(Buf));
+  SmallVector<Elf_Word, 0> Indexes;
+  Indexes.reserve(Sec.Indexes.size());
+  for (uint32_t Index : Sec.Indexes)
+    Indexes.emplace_back(Index);
+  writeSectionContents(
+      ArrayRef(reinterpret_cast<const uint8_t *>(Indexes.data()),
+               Indexes.size() * sizeof(Elf_Word)),
+      Sec.Offset);
   return Error::success();
 }
 
@@ -1029,7 +1037,7 @@ void RelocationSection::replaceSectionReferences(
 }
 
 Error SectionWriter::visit(const DynamicRelocationSection &Sec) {
-  llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset);
+  writeSectionContents(Sec.Contents, Sec.Offset);
   return Error::success();
 }
 
@@ -1182,7 +1190,10 @@ Error ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) {
   Elf_Word *CRC =
       reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word));
   *CRC = Sec.CRC32;
-  llvm::copy(Sec.FileName, Buf);
+  writeSectionContents(
+      ArrayRef(reinterpret_cast<const uint8_t *>(Sec.FileName.data()),
+               Sec.FileName.size()),
+      Sec.Offset);
   return Error::success();
 }
 
diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h
index 2783ef27ac9de..9df62d8642917 100644
--- a/llvm/lib/ObjCopy/ELF/ELFObject.h
+++ b/llvm/lib/ObjCopy/ELF/ELFObject.h
@@ -107,6 +107,7 @@ class MutableSectionVisitor {
 class SectionWriter : public SectionVisitor {
 protected:
   WritableMemoryBuffer &Out;
+  virtual void writeSectionContents(ArrayRef<uint8_t> Data, uint64_t Offset);
 
 public:
   ~SectionWriter() override = default;
diff --git a/llvm/test/tools/llvm-objcopy/ELF/many-sections.test b/llvm/test/tools/llvm-objcopy/ELF/many-sections.test
index 6622db237026f..64ee1e4b4b48b 100644
--- a/llvm/test/tools/llvm-objcopy/ELF/many-sections.test
+++ b/llvm/test/tools/llvm-objcopy/ELF/many-sections.test
@@ -5,6 +5,10 @@ RUN: llvm-objcopy %t %t2
 RUN: llvm-readobj --file-headers --sections --symbols %t2 | FileCheck %s
 RUN: llvm-readelf --symbols %t2 | FileCheck --check-prefix=SYMS %s
 
+## Check that SHT_SYMTAB_SHNDX entries use the output endianness.
+RUN: llvm-objcopy --output-target=elf64-s390 %t %t.be
+RUN: llvm-readobj --file-headers --sections --symbols %t.be | FileCheck %s
+
 ## The ELF header should have e_shnum == 0 and e_shstrndx == SHN_XINDEX.
 # CHECK:        SectionHeaderCount: 0
 # CHECK-NEXT:   StringTableSectionIndex: 65535

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant