[lld] [lld] Fill section gaps with trap instructions (PR #176845)

Zachary Yedidia via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 28 11:49:36 PST 2026


https://github.com/zyedidia updated https://github.com/llvm/llvm-project/pull/176845

>From 4af875483c61c8e56c49aaab2969d61e0396785f Mon Sep 17 00:00:00 2001
From: Zachary Yedidia <zyedidia at gmail.com>
Date: Mon, 19 Jan 2026 15:54:17 -0800
Subject: [PATCH] [lld] Fill section gaps with trap instructions

Fills unused parts of code segments with trap instructions instead of
zeroes. This is especially useful on x86-64 since 0x00 0x00 is a
two-byte instruction that performs a memory access. Benefits:

- Provides an explicit trap when executing invalid code.
- Required for security reasons when targeting LFI.
- Makes disassembling easier, especially without symbol information.
  Previously an odd number of zero bytes between sections could cause a
  disassembler to incorrectly disassemble the remainder of the program.
---
 lld/ELF/Writer.cpp       | 23 ++++++++++++-----
 lld/test/ELF/fill-trap.s | 56 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+), 6 deletions(-)

diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 57202f42cce5b..f7a566226cc91 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -2960,14 +2960,25 @@ static void fillTrap(std::array<uint8_t, 4> trapInstr, uint8_t *i,
     memcpy(i, trapInstr.data(), 4);
 }
 
-// Fill the last page of executable segments with trap instructions
-// instead of leaving them as zero. Even though it is not required by any
-// standard, it is in general a good thing to do for security reasons.
-//
-// We'll leave other pages in segments as-is because the rest will be
-// overwritten by output sections.
+// Fill executable segments with trap instructions. This includes both the
+// gaps between sections (due to alignment) and the tail padding to the page
+// boundary. Even though it is not required by any standard, it is in general
+// a good thing to do for security reasons.
 template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
   for (Partition &part : ctx.partitions) {
+    // Fill gaps between consecutive sections in the same executable segment.
+    OutputSection *prev = nullptr;
+    for (OutputSection *sec : ctx.outputSections) {
+      PhdrEntry *p = sec->ptLoad;
+      if (!p || !(p->p_flags & PF_X))
+        continue;
+      if (prev && prev->ptLoad == p)
+        fillTrap(ctx.target->trapInstr,
+                 ctx.bufferStart + alignDown(prev->offset + prev->size, 4),
+                 ctx.bufferStart + sec->offset);
+      prev = sec;
+    }
+
     // Fill the last page.
     for (std::unique_ptr<PhdrEntry> &p : part.phdrs)
       if (p->p_type == PT_LOAD && (p->p_flags & PF_X))
diff --git a/lld/test/ELF/fill-trap.s b/lld/test/ELF/fill-trap.s
index f111bb87a92a1..ad81fdcb6ceb5 100644
--- a/lld/test/ELF/fill-trap.s
+++ b/lld/test/ELF/fill-trap.s
@@ -54,6 +54,34 @@
 # OMAGIC:     Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
 # OMAGIC-NEXT:LOAD           0x0000b0 0x00000000002000b0 0x00000000002000b0 0x000004 0x000404 RWE 0x4
 
+## Test that gaps between sections within an executable segment are filled with traps.
+# RUN: llvm-mc -filetype=obj -triple=x86_64 gap.s -o gap.o
+# RUN: ld.lld gap.o -z separate-code -z max-page-size=0x1000 -o gap.out
+## .text is at offset 0x1000, .text2 is aligned to 16 bytes at 0x1010.
+## The gap between them should be filled with 0xcc.
+# RUN: od -Ax -t x1 -v -N32 -j0x1000 gap.out | FileCheck %s --check-prefix=GAP
+# GAP:      001000 90 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
+# GAP-NEXT: 001010 90 cc
+
+## Test multiple gaps with various alignments.
+## Sections: .text (1 byte) -> .text2 (align 8) -> .text3 (align 32) -> .text4 (align 128)
+# RUN: llvm-mc -filetype=obj -triple=x86_64 multi-gap.s -o multi-gap.o
+# RUN: ld.lld multi-gap.o -z separate-code -z max-page-size=0x1000 -o multi-gap.out
+# RUN: od -Ax -t x1 -v -N144 -j0x1000 multi-gap.out | FileCheck %s --check-prefix=MGAP
+## .text at 0x1000, .text2 at 0x1008 (aligned to 8)
+# MGAP:      001000 90 cc cc cc cc cc cc cc 90 cc cc cc cc cc cc cc
+## gap from 0x1009 to 0x1020, .text3 at 0x1020 (aligned to 32)
+# MGAP-NEXT: 001010 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
+# MGAP-NEXT: 001020 90 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
+## gap from 0x1021 to 0x1080
+# MGAP-NEXT: 001030 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
+# MGAP-NEXT: 001040 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
+# MGAP-NEXT: 001050 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
+# MGAP-NEXT: 001060 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
+# MGAP-NEXT: 001070 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
+## .text4 at 0x1080 (aligned to 128)
+# MGAP-NEXT: 001080 90 cc
+
 #--- a.s
 .globl _start
 _start:
@@ -63,6 +91,34 @@ _start:
 .bss
 .space 1024
 
+#--- gap.s
+.globl _start
+.section .text,"ax"
+_start:
+  nop
+
+.section .text2,"ax"
+.p2align 4
+  nop
+
+#--- multi-gap.s
+.globl _start
+.section .text,"ax"
+_start:
+  nop
+
+.section .text2,"ax"
+.p2align 3
+  nop
+
+.section .text3,"ax"
+.p2align 5
+  nop
+
+.section .text4,"ax"
+.p2align 7
+  nop
+
 #--- rwx.lds
 PHDRS { all PT_LOAD; }
 SECTIONS {



More information about the llvm-commits mailing list