[llvm] [BOLT] Zero alignment padding when reusing old text section (PR #202375)

YongKang Zhu via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 08:56:52 PDT 2026


https://github.com/yozhu created https://github.com/llvm/llvm-project/pull/202375

With --use-old-text, the output starts as a byte-for-byte copy of the input. Alignment padding between sections could retain stale data from the original binary. Zero the padding so the result matches writing sections to new file offsets.

>From 2c7179145dd744cf693318b40abcb5c7cef362ae Mon Sep 17 00:00:00 2001
From: YongKang Zhu <yongzhu at fb.com>
Date: Thu, 28 May 2026 16:21:20 -0700
Subject: [PATCH] [BOLT] Zero alignment padding when reusing old text section

Summary:
With --use-old-text, the output starts as a byte-for-byte copy of the
input. Alignment padding between sections could retain stale data from
the original binary. Zero the padding so the result matches writing
sections to new file offsets.
---
 bolt/include/bolt/Rewrite/RewriteInstance.h   |  5 +++
 bolt/lib/Rewrite/RewriteInstance.cpp          | 44 +++++++++++++++++++
 bolt/test/AArch64/use-old-text-zero-padding.c | 43 ++++++++++++++++++
 3 files changed, 92 insertions(+)
 create mode 100644 bolt/test/AArch64/use-old-text-zero-padding.c

diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index 166336cbb8fcf..3873d944e01ba 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -231,6 +231,11 @@ class RewriteInstance {
   /// Used by non-relocation mode and for patched functions.
   void rewriteFunctionsInPlace(raw_fd_ostream &OS);
 
+  /// When --use-old-text reuses input file regions, zero the alignment
+  /// padding after BOLT-written text sections so the output does not retain
+  /// stale bytes from the input binary.
+  void zeroPaddingForReusedSections(raw_fd_ostream &OS);
+
   /// Return address of a function in the new binary corresponding to
   /// \p OldAddress address in the original binary.
   uint64_t getNewFunctionAddress(uint64_t OldAddress);
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 9364e32939982..dabbf04797314 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -6286,6 +6286,47 @@ void RewriteInstance::rewriteFunctionsInPlace(raw_fd_ostream &OS) {
   }
 }
 
+void RewriteInstance::zeroPaddingForReusedSections(raw_fd_ostream &OS) {
+  // The output starts as a byte-for-byte copy of the input, so alignment
+  // padding after BOLT-written sections could retain stale data from the
+  // original binary. Zero the padding as if we were writing sections onto
+  // new file offset (i.e., not reusing old existing sections).
+
+  // Collect file offsets of all sections (allocatable and non-allocatable)
+  // that occupy file bytes.
+  SmallVector<uint64_t, 16> SectionStarts;
+  for (BinarySection &Section : BC->sections()) {
+    if (Section.isVirtual())
+      continue;
+    uint64_t Offset = Section.getOutputFileOffset();
+    if (!Offset)
+      Offset = Section.getInputFileOffset();
+    if (Offset)
+      SectionStarts.push_back(Offset);
+  }
+  llvm::sort(SectionStarts);
+
+  uint64_t SavedPos = OS.tell();
+  for (BinarySection &Section : BC->allocatableSections()) {
+    if (!Section.isFinalized() || !Section.getOutputData())
+      continue;
+    if (Section.isLinkOnly() || !Section.getOutputSize())
+      continue;
+    if (!(Section.getELFFlags() & ELF::SHF_EXECINSTR))
+      continue;
+    uint64_t SecEnd = Section.getOutputFileOffset() + Section.getOutputSize();
+    auto It = llvm::upper_bound(SectionStarts, SecEnd - 1);
+    if (It != SectionStarts.end()) {
+      uint64_t NextStart = *It;
+      if (NextStart > SecEnd) {
+        OS.seek(SecEnd);
+        OS.write_zeros(NextStart - SecEnd);
+      }
+    }
+  }
+  OS.seek(SavedPos);
+}
+
 void RewriteInstance::rewriteFile() {
   std::error_code EC;
   Out = std::make_unique<ToolOutputFile>(opts::OutputFilename, EC,
@@ -6375,6 +6416,9 @@ void RewriteInstance::rewriteFile() {
   // Copy non-allocatable sections once allocatable part is finished.
   rewriteNoteSections();
 
+  if (opts::UseOldText)
+    zeroPaddingForReusedSections(OS);
+
   if (BC->HasRelocations) {
     patchELFAllocatableRelaSections();
     patchELFAllocatableRelrSection();
diff --git a/bolt/test/AArch64/use-old-text-zero-padding.c b/bolt/test/AArch64/use-old-text-zero-padding.c
new file mode 100644
index 0000000000000..9e1d4231c0c84
--- /dev/null
+++ b/bolt/test/AArch64/use-old-text-zero-padding.c
@@ -0,0 +1,43 @@
+// Verify that BOLT will zero pad section end when --use-old-text
+// is specified, such that there won't be stale instructions left
+// there from input library.
+
+// RUN: rm -rf %t && split-file %s %t
+
+// Use a linker script to force section ordering .text -> .rodata so
+// the test can locate the padding immediately after .text.
+
+// RUN: %clang %cflags -falign-functions=64 \
+// RUN:   -Wl,--script=%t/script.ld %t/test.c -o %t/test -Wl,-q
+// RUN: llvm-bolt %t/test -o %t/test.bolt --use-old-text --align-text=4
+
+// RUN: llvm-objdump -s -j .rodata %t/test.bolt \
+// RUN:   | FileCheck %s --check-prefix=RODATA
+
+// RODATA:     55555555 aaaaaaaa 33333333 cccccccc
+
+// Input was built with -falign-functions=64; BOLT writes a more compact
+// .text (--align-text=4) into the old region, leaving unused file space
+// between the new .text end and the next section. Verify those gap bytes
+// are zeroed. Padding starts at end-of-.text file offset = sh_offset +
+// sh_size (read from llvm-readelf section headers).
+
+// RUN: llvm-readelf -S %t/test.bolt | awk '$3==".text"{print $6, $7}' \
+// RUN:   > %t/txt-loc
+// RUN: bash -c "read O S < %t/txt-loc; \
+// RUN:   od -A x -t x1 -N 0x20 -j \$((0x\$O + 0x\$S)) %t/test.bolt" \
+// RUN:   | FileCheck %s --check-prefix=PADDING
+
+// PADDING: {{[0-9a-f]+}} 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+//--- script.ld
+SECTIONS { .rodata : { *(.rodata) *(.rodata.*) } } INSERT AFTER .text;
+
+//--- test.c
+__attribute__((used))
+const unsigned data[] = {0x55555555, 0xaaaaaaaa, 0x33333333, 0xcccccccc};
+
+__attribute__((used, noinline)) int foo(int x, int y) { return x + y; }
+__attribute__((used, noinline)) int bar(int x, int y) { return x ^ y; }
+
+int _start(void) { return foo(1, 2) + bar(3, 4); }



More information about the llvm-commits mailing list