[lld] ELF: Introduce --shuffle-padding flag. (PR #117653)

via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 25 16:44:19 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lld

Author: None (pcc)

<details>
<summary>Changes</summary>

The --shuffle-padding flag randomly inserts padding between input
sections using the given seed. It is intended to be used in A/B
experiments to determine the average effect of a change on program
performance, while controlling for effects such as false sharing in
the cache which may introduce measurement bias. For more details,
see the RFC:


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


9 Files Affected:

- (modified) lld/ELF/Config.h (+1) 
- (modified) lld/ELF/Driver.cpp (+2) 
- (modified) lld/ELF/Options.td (+3) 
- (modified) lld/ELF/OutputSections.h (+2-2) 
- (modified) lld/ELF/SyntheticSections.cpp (+15) 
- (modified) lld/ELF/SyntheticSections.h (+9) 
- (modified) lld/ELF/Writer.cpp (+34) 
- (modified) lld/docs/ld.lld.1 (+9) 
- (added) lld/test/ELF/shuffle-padding.s (+29) 


``````````diff
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index a2836733c2715e..ed6bce405d1664 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -329,6 +329,7 @@ struct Config {
   bool relrPackDynRelocs = false;
   llvm::DenseSet<llvm::StringRef> saveTempsArgs;
   llvm::SmallVector<std::pair<llvm::GlobPattern, uint32_t>, 0> shuffleSections;
+  std::optional<uint64_t> shufflePadding;
   bool singleRoRx;
   bool shared;
   bool symbolic;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index bc4b967ccbbbb4..d8bcbe4a2d19d8 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1410,6 +1410,8 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   ctx.arg.searchPaths = args::getStrings(args, OPT_library_path);
   ctx.arg.sectionStartMap = getSectionStartMap(ctx, args);
   ctx.arg.shared = args.hasArg(OPT_shared);
+  if (args.hasArg(OPT_shuffle_padding))
+    ctx.arg.shufflePadding = args::getInteger(args, OPT_shuffle_padding, 0);
   ctx.arg.singleRoRx = !args.hasFlag(OPT_rosegment, OPT_no_rosegment, true);
   ctx.arg.soName = args.getLastArgValue(OPT_soname);
   ctx.arg.sortSection = getSortSection(ctx, args);
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index ebe77204264210..44ac0ee43a8502 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -434,6 +434,9 @@ defm section_start: Eq<"section-start", "Set address of section">,
 
 def shared: F<"shared">, HelpText<"Build a shared object">;
 
+def shuffle_padding: JJ<"shuffle-padding=">,
+  HelpText<"Randomly insert padding between input sections using given seed">;
+
 defm soname: Eq<"soname", "Set DT_SONAME">;
 
 defm sort_section:
diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h
index 67191392d1dbe7..3ab36a21ce488d 100644
--- a/lld/ELF/OutputSections.h
+++ b/lld/ELF/OutputSections.h
@@ -124,14 +124,14 @@ class OutputSection final : public SectionBase {
   void sortInitFini();
   void sortCtorsDtors();
 
+  std::array<uint8_t, 4> getFiller(Ctx &);
+
   // Used for implementation of --compress-debug-sections and
   // --compress-sections.
   CompressedData compressed;
 
 private:
   SmallVector<InputSection *, 0> storage;
-
-  std::array<uint8_t, 4> getFiller(Ctx &);
 };
 
 struct OutputDesc final : SectionCommand {
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 21fe2a25fa1bd2..70eca0e58036e0 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -2753,6 +2753,21 @@ RelroPaddingSection::RelroPaddingSection(Ctx &ctx)
     : SyntheticSection(ctx, ".relro_padding", SHT_NOBITS, SHF_ALLOC | SHF_WRITE,
                        1) {}
 
+ShufflePaddingSection::ShufflePaddingSection(Ctx &ctx, uint64_t size,
+                                             OutputSection *parent)
+    : SyntheticSection(ctx, ".shuffle_padding", SHF_ALLOC, SHT_PROGBITS, 1),
+      size(size) {
+  this->parent = parent;
+}
+
+void ShufflePaddingSection::writeTo(uint8_t *buf) {
+  std::array<uint8_t, 4> filler = getParent()->getFiller(ctx);
+  uint8_t *end = buf + size;
+  for (; buf + 4 <= end; buf += 4)
+    memcpy(buf, &filler[0], 4);
+  memcpy(buf, &filler[0], end - buf);
+}
+
 // The string hash function for .gdb_index.
 static uint32_t computeGdbHash(StringRef s) {
   uint32_t h = 0;
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 4b643e86335510..177a0337607da8 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -796,6 +796,15 @@ class RelroPaddingSection final : public SyntheticSection {
   void writeTo(uint8_t *buf) override {}
 };
 
+class ShufflePaddingSection final : public SyntheticSection {
+  uint64_t size;
+
+public:
+  ShufflePaddingSection(Ctx &ctx, uint64_t size, OutputSection *parent);
+  size_t getSize() const override { return size; }
+  void writeTo(uint8_t *buf) override;
+};
+
 // Used by the merged DWARF32 .debug_names (a per-module index). If we
 // move to DWARF64, most of this data will need to be re-sized.
 class DebugNamesBaseSection : public SyntheticSection {
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index a7fbdc07907044..3c1ab3234801d9 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1444,6 +1444,37 @@ static void finalizeSynthetic(Ctx &ctx, SyntheticSection *sec) {
   }
 }
 
+static bool canInsertPadding(OutputSection *sec) {
+    StringRef s = sec->name;
+    return s == ".bss" || s == ".data" || s == ".data.rel.ro" ||
+           s == ".rodata" || s.starts_with(".text");
+}
+
+static void shufflePadding(Ctx &ctx) {
+  std::mt19937 g(*ctx.arg.shufflePadding);
+  PhdrEntry *curPtLoad = nullptr;
+  for (OutputSection *os : ctx.outputSections) {
+    if (!canInsertPadding(os))
+      continue;
+    for (SectionCommand *bc : os->commands) {
+      if (auto *isd = dyn_cast<InputSectionDescription>(bc)) {
+        SmallVector<InputSection *, 0> tmp;
+        if (os->ptLoad != curPtLoad) {
+          tmp.push_back(
+              make<ShufflePaddingSection>(ctx, g() % ctx.arg.maxPageSize, os));
+          curPtLoad = os->ptLoad;
+        }
+        for (InputSection *isec : isd->sections) {
+          if (g() < (1<<28))
+            tmp.push_back(make<ShufflePaddingSection>(ctx, isec->addralign, os));
+          tmp.push_back(isec);
+        }
+        isd->sections = std::move(tmp);
+      }
+    }
+  }
+}
+
 // We need to generate and finalize the content that depends on the address of
 // InputSections. As the generation of the content may also alter InputSection
 // addresses we must converge to a fixed point. We do that here. See the comment
@@ -1470,6 +1501,9 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
   if (ctx.arg.emachine == EM_HEXAGON)
     hexagonTLSSymbolUpdate(ctx);
 
+  if (ctx.arg.shufflePadding)
+    shufflePadding(ctx);
+
   uint32_t pass = 0, assignPasses = 0;
   for (;;) {
     bool changed = ctx.target->needsThunks
diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index b22cb362837715..5d3d4164622166 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -577,6 +577,15 @@ were concatenated in the order they appeared on the command line.
 Set address of section.
 .It Fl -shared , Fl -Bsharable
 Build a shared object.
+.It Fl -shuffle-padding Ns = Ns Ar seed
+Randomly insert padding between input sections using the given seed.
+Padding is inserted into output sections with names matching the following patterns:
+.Cm .bss ,
+.Cm .data ,
+.Cm .data.rel.ro ,
+.Cm .rodata
+and
+.Cm .text* .
 .It Fl -shuffle-sections Ns = Ns Ar seed
 Shuffle matched sections using the given seed before mapping them to the output sections.
 If -1, reverse the section order. If 0, use a random seed.
diff --git a/lld/test/ELF/shuffle-padding.s b/lld/test/ELF/shuffle-padding.s
new file mode 100644
index 00000000000000..f816100ffba1be
--- /dev/null
+++ b/lld/test/ELF/shuffle-padding.s
@@ -0,0 +1,29 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+
+# RUN: ld.lld %t.o -o %t.out
+# RUN: llvm-readelf -x .rodata %t.out | FileCheck %s
+# CHECK: Hex dump of section '.rodata':
+# CHECK-NEXT: 0x00201120 010203
+
+## --shuffle-padding= inserts segment offset padding and pre-section padding.
+# RUN: ld.lld --shuffle-padding=1 %t.o -o %t.out
+# RUN: llvm-readelf -x .rodata %t.out | FileCheck --check-prefix=PAD1 %s
+# PAD1: Hex dump of section '.rodata':
+# PAD1-NEXT: 0x00201548 0102cc03
+
+## Size of segment offset padding and location of pre-section padding is
+## dependent on the seed.
+# RUN: ld.lld --shuffle-padding=2 %t.o -o %t.out
+# RUN: llvm-readelf -x .rodata %t.out | FileCheck --check-prefix=PAD2 %s
+# PAD2: Hex dump of section '.rodata':
+# PAD2-NEXT: 0x00201dc8 cc010203
+
+.section .rodata.a,"ax"
+.byte 1
+
+.section .rodata.b,"ax"
+.byte 2
+
+.section .rodata.c,"ax"
+.byte 3

``````````

</details>


https://github.com/llvm/llvm-project/pull/117653


More information about the llvm-commits mailing list