[lld] 16c30c3 - [ELF] Change --shuffle-sections=<seed> to --shuffle-sections=<section-glob>=<seed>

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 18 10:18:26 PDT 2021


Author: Fangrui Song
Date: 2021-03-18T10:18:19-07:00
New Revision: 16c30c3c23ef02c0227256bb6f2005a574517de9

URL: https://github.com/llvm/llvm-project/commit/16c30c3c23ef02c0227256bb6f2005a574517de9
DIFF: https://github.com/llvm/llvm-project/commit/16c30c3c23ef02c0227256bb6f2005a574517de9.diff

LOG: [ELF] Change --shuffle-sections=<seed> to --shuffle-sections=<section-glob>=<seed>

`--shuffle-sections=<seed>` applies to all sections.  The new
`--shuffle-sections=<section-glob>=<seed>` makes shuffling selective.  To the
best of my knowledge, the option is only used as debugging, so just drop the
original form.

`--shuffle-sections '.init_array*=-1'` `--shuffle-sections '.fini_array*=-1'`.
reverses static constructors/destructors of the same priority.
Useful to detect some static initialization order fiasco.

`--shuffle-sections '.data*=-1'`
reverses `.data*` sections. Useful to detect unfunded pointer comparison results
of two unrelated objects.

If certain sections have an intrinsic order, the old form cannot be used.

Differential Revision: https://reviews.llvm.org/D98679

Added: 
    

Modified: 
    lld/ELF/Config.h
    lld/ELF/Driver.cpp
    lld/ELF/Options.td
    lld/ELF/Writer.cpp
    lld/docs/ReleaseNotes.rst
    lld/docs/ld.lld.1
    lld/test/ELF/gnu-ifunc-plt.s
    lld/test/ELF/shuffle-sections-init-fini.s
    lld/test/ELF/shuffle-sections.s

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index fcfe5f64c32f..ab55c60bb6f9 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -198,7 +198,7 @@ struct Configuration {
   bool relocatable;
   bool relrPackDynRelocs;
   bool saveTemps;
-  llvm::Optional<uint32_t> shuffleSectionSeed;
+  std::vector<std::pair<llvm::GlobPattern, uint32_t>> shuffleSections;
   bool singleRoRx;
   bool shared;
   bool symbolic;

diff  --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index df9925d74f8a..3401c016dbe9 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1068,8 +1068,6 @@ static void readConfigs(opt::InputArgList &args) {
   config->rpath = getRpath(args);
   config->relocatable = args.hasArg(OPT_relocatable);
   config->saveTemps = args.hasArg(OPT_save_temps);
-  if (args.hasArg(OPT_shuffle_sections))
-    config->shuffleSectionSeed = args::getInteger(args, OPT_shuffle_sections, 0);
   config->searchPaths = args::getStrings(args, OPT_library_path);
   config->sectionStartMap = getSectionStartMap(args);
   config->shared = args.hasArg(OPT_shared);
@@ -1149,6 +1147,24 @@ static void readConfigs(opt::InputArgList &args) {
       config->optEL = true;
   }
 
+  for (opt::Arg *arg : args.filtered(OPT_shuffle_sections)) {
+    constexpr StringRef errPrefix = "--shuffle-sections=: ";
+    std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
+    if (kv.first.empty() || kv.second.empty()) {
+      error(errPrefix + "expected <section_glob>=<seed>, but got '" +
+            arg->getValue() + "'");
+      continue;
+    }
+    // Signed so that <section_glob>=-1 is allowed.
+    int64_t v;
+    if (!to_integer(kv.second, v))
+      error(errPrefix + "expected an integer, but got '" + kv.second + "'");
+    else if (Expected<GlobPattern> pat = GlobPattern::create(kv.first))
+      config->shuffleSections.emplace_back(std::move(*pat), uint32_t(v));
+    else
+      error(errPrefix + toString(pat.takeError()));
+  }
+
   for (opt::Arg *arg : args.filtered(OPT_z)) {
     std::pair<StringRef, StringRef> option =
         StringRef(arg->getValue()).split('=');

diff  --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index ee4a0610d362..55bde53cddcb 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -586,9 +586,10 @@ def lto_basic_block_sections: JJ<"lto-basic-block-sections=">,
 defm lto_unique_basic_block_section_names: BB<"lto-unique-basic-block-section-names",
     "Give unique names to every basic block section for LTO",
     "Do not give unique names to every basic block section for LTO (default)">;
-def shuffle_sections: JJ<"shuffle-sections=">, MetaVarName<"<seed>">,
-  HelpText<"Shuffle input sections using the given seed. "
-  "If -1, reverse the section order. If 0, use a random seed">;
+defm shuffle_sections: EEq<"shuffle-sections",
+  "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">,
+  MetaVarName<"<section-glob>=<seed>">;
 def thinlto_cache_dir: JJ<"thinlto-cache-dir=">,
   HelpText<"Path to ThinLTO cached object file directory">;
 defm thinlto_cache_policy: EEq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index f0d4e6e4e685..5f5f7ccb4d35 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1291,29 +1291,39 @@ findOrphanPos(std::vector<BaseCommand *>::iterator b,
 
 // Adds random priorities to sections not already in the map.
 static void maybeShuffle(DenseMap<const InputSectionBase *, int> &order) {
-  if (!config->shuffleSectionSeed)
+  if (config->shuffleSections.empty())
     return;
 
-  std::vector<int> priorities(inputSections.size() - order.size());
+  std::vector<InputSectionBase *> matched, sections = inputSections;
+  matched.reserve(sections.size());
+  for (const auto &patAndSeed : config->shuffleSections) {
+    matched.clear();
+    for (InputSectionBase *sec : sections)
+      if (patAndSeed.first.match(sec->name))
+        matched.push_back(sec);
+    const uint32_t seed = patAndSeed.second;
+    if (seed == UINT32_MAX) {
+      // If --shuffle-sections <section-glob>=-1, reverse the section order. The
+      // section order is stable even if the number of sections changes. This is
+      // useful to catch issues like static initialization order fiasco
+      // reliably.
+      std::reverse(matched.begin(), matched.end());
+    } else {
+      std::mt19937 g(seed ? seed : std::random_device()());
+      llvm::shuffle(matched.begin(), matched.end(), g);
+    }
+    size_t i = 0;
+    for (InputSectionBase *&sec : sections)
+      if (patAndSeed.first.match(sec->name))
+        sec = matched[i++];
+  }
+
   // Existing priorities are < 0, so use priorities >= 0 for the missing
   // sections.
-  int curPrio = 0;
-  for (int &prio : priorities)
-    prio = curPrio++;
-  uint32_t seed = *config->shuffleSectionSeed;
-  if (seed == UINT32_MAX) {
-    // If --shuffle-sections=-1, reverse the section order. The section order is
-    // stable even if the number of sections changes. This is useful to catch
-    // issues like static initialization order fiasco reliably.
-    std::reverse(priorities.begin(), priorities.end());
-  } else {
-    std::mt19937 g(seed ? seed : std::random_device()());
-    llvm::shuffle(priorities.begin(), priorities.end(), g);
-  }
-  int prioIndex = 0;
-  for (InputSectionBase *sec : inputSections) {
-    if (order.try_emplace(sec, priorities[prioIndex]).second)
-      ++prioIndex;
+  int prio = 0;
+  for (InputSectionBase *sec : sections) {
+    if (order.try_emplace(sec, prio).second)
+      ++prio;
   }
 }
 

diff  --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 3684e99cb80c..a3b577e48fb1 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -29,7 +29,8 @@ ELF Improvements
 Breaking changes
 ----------------
 
-* ...
+* ``--shuffle-sections=<seed>`` has been changed to ``--shuffle-sections=<section-glob>=<seed>``.
+  Specify ``*`` as ``<section-glob>`` to get the previous behavior.
 
 COFF Improvements
 -----------------

diff  --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index 3c1704c0c5e8..37c42a0eb51f 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -487,7 +487,7 @@ Set address of section.
 .It Fl -shared , Fl -Bsharable
 Build a shared object.
 .It Fl -shuffle-sections Ns = Ns Ar seed
-Shuffle input sections using the given 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.
 .It Fl -soname Ns = Ns Ar value , Fl h Ar value
 Set

diff  --git a/lld/test/ELF/gnu-ifunc-plt.s b/lld/test/ELF/gnu-ifunc-plt.s
index 540bfbc5325c..58fae803a0e5 100644
--- a/lld/test/ELF/gnu-ifunc-plt.s
+++ b/lld/test/ELF/gnu-ifunc-plt.s
@@ -80,9 +80,9 @@
 // Test that --shuffle-sections does not affect the order of relocations and that
 // we still place IRELATIVE relocations last. Check both random seed (0) and an
 // arbitrary seed that was known to break the order of relocations previously (3).
-// RUN: ld.lld --shuffle-sections=3 %t.so %t.o -o %tout2
+// RUN: ld.lld --shuffle-sections='*=3' %t.so %t.o -o %tout2
 // RUN: llvm-readobj --relocations %tout2 | FileCheck %s --check-prefix=SHUFFLE
-// RUN: ld.lld --shuffle-sections=0 %t.so %t.o -o %tout3
+// RUN: ld.lld --shuffle-sections='*=0' %t.so %t.o -o %tout3
 // RUN: llvm-readobj --relocations %tout3 | FileCheck %s --check-prefix=SHUFFLE
 
 // SHUFFLE:      Section {{.*}} .rela.dyn {

diff  --git a/lld/test/ELF/shuffle-sections-init-fini.s b/lld/test/ELF/shuffle-sections-init-fini.s
index d98ca8d359de..4ddbf6cb7483 100644
--- a/lld/test/ELF/shuffle-sections-init-fini.s
+++ b/lld/test/ELF/shuffle-sections-init-fini.s
@@ -5,7 +5,7 @@
 # RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t | \
 # RUN:   FileCheck --check-prefixes=CHECK,ORDERED %s
 
-# RUN: ld.lld %t.o --shuffle-sections=1 -o %t1
+# RUN: ld.lld %t.o --shuffle-sections '*=1' -o %t1
 # RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t1 | \
 # RUN:   FileCheck --check-prefixes=CHECK,SHUFFLED %s
 
@@ -21,12 +21,12 @@
 # CHECK:      Hex dump of section '.init_array'
 # CHECK-NEXT: 0x{{[0-9a-f]+}} ff
 # ORDERED-SAME: 000102 03040506 0708090a 0b
-# SHUFFLED-SAME: 04000b 06010a08 09070203 05
+# SHUFFLED-SAME: 080301 04050907 0b020a06 00
 
 # CHECK:      Hex dump of section '.fini_array'
 # CHECK-NEXT: 0x{{[0-9a-f]+}} ff
 # ORDERED-SAME:  000102 03040506 0708090a 0b
-# SHUFFLED-SAME: 090401 070b0003 080a0605 02
+# SHUFFLED-SAME: 0a0405 08070b02 03090006 01
 
 ## With a SECTIONS command, SHT_INIT_ARRAY prirotities are ignored.
 ## All .init_array* are shuffled together.
@@ -36,13 +36,13 @@
 # RUN: ld.lld -T %t.script %t.o -o %t2
 # RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t2 | \
 # RUN:   FileCheck --check-prefixes=CHECK2,ORDERED2 %s
-# RUN: ld.lld -T %t.script %t.o --shuffle-sections=1 -o %t3
+# RUN: ld.lld -T %t.script %t.o --shuffle-sections '*=1' -o %t3
 # RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t3 | \
 # RUN:   FileCheck --check-prefixes=CHECK2,SHUFFLED2 %s
 
 # CHECK2:       Hex dump of section '.init_array'
 # ORDERED2-NEXT:  0x{{[0-9a-f]+}} 00010203 04050607 08090a0b ff
-# SHUFFLED2-NEXT: 0x{{[0-9a-f]+}} 04000b06 010a0809 07ff0203 05
+# SHUFFLED2-NEXT: 0x{{[0-9a-f]+}} 08030104 0509070b 02ff0a06 00
 
 .irp i,0,1,2,3,4,5,6,7,8,9,10,11
   .section .init,"ax", at progbits,unique,\i

diff  --git a/lld/test/ELF/shuffle-sections.s b/lld/test/ELF/shuffle-sections.s
index 59b0642d639c..8211c482732b 100644
--- a/lld/test/ELF/shuffle-sections.s
+++ b/lld/test/ELF/shuffle-sections.s
@@ -7,31 +7,53 @@
 # CHECK-NEXT: 01020304
 
 ## --shuffle-sections= shuffles input sections.
-# RUN: ld.lld --shuffle-sections=1 %t.o -o %t1.out
+# RUN: ld.lld --shuffle-sections='*=1' %t.o -o %t1.out
 # RUN: llvm-readelf -x .text %t1.out | FileCheck %s --check-prefix=SHUFFLE1
 # SHUFFLE1: Hex dump of section '.text':
-# SHUFFLE1-NEXT: 0204cccc 0103
+# SHUFFLE1-NEXT: 0203cccc 0104
 
 ## Test that --shuffle-sections= can be used with --symbol-ordering-file
 # RUN: echo "foo" > %t_order.txt
 # RUN: echo "_start " >> %t_order.txt
 
-# RUN: ld.lld --symbol-ordering-file %t_order.txt --shuffle-sections=2 %t.o -o %t2.out
+# RUN: ld.lld --symbol-ordering-file %t_order.txt --shuffle-sections='*=2' %t.o -o %t2.out
 # RUN: llvm-readelf -x .text %t2.out | FileCheck %s --check-prefix=SHUFFLE2
 # SHUFFLE2: Hex dump of section '.text':
-# SHUFFLE2-NEXT: 02cccccc 010304
+# SHUFFLE2-NEXT: 02cccccc 010403
 
-# RUN: ld.lld --symbol-ordering-file %t_order.txt --shuffle-sections=3 %t.o -o %t3.out
+# RUN: ld.lld --symbol-ordering-file %t_order.txt --shuffle-sections='*=3' %t.o -o %t3.out
 # RUN: llvm-readelf -x .text %t3.out | FileCheck %s --check-prefix=SHUFFLE3
 # SHUFFLE3: Hex dump of section '.text':
 # SHUFFLE3-NEXT: 02cccccc 010403
 
 ## As a special case, -1 reverses sections as a stable transform.
-# RUN: ld.lld --shuffle-sections=-1 %t.o -o %t-1.out
+# RUN: ld.lld --shuffle-sections '*=-1' %t.o -o %t-1.out
 # RUN: llvm-readelf -x .text %t-1.out | FileCheck %s --check-prefix=SHUFFLE-1
 # SHUFFLE-1: Hex dump of section '.text':
 # SHUFFLE-1-NEXT: 040302cc 01
 
+## .text does not change its order while .text.{foo,bar,zed} are reversed.
+# RUN: ld.lld --shuffle-sections '.text.*=-1' %t.o -o %t4.out
+# RUN: llvm-readelf -x .text %t4.out | FileCheck %s --check-prefix=SHUFFLE4
+# SHUFFLE4: Hex dump of section '.text':
+# SHUFFLE4-NEXT: 01040302
+
+## Reversing twice restores the original order.
+# RUN: ld.lld --shuffle-sections '.text.*=-1' --shuffle-sections '.text.*=-1' %t.o -o %t.out
+# RUN: llvm-readelf -x .text %t.out | FileCheck %s
+
+## Test all possible invalid cases.
+# RUN: not ld.lld --shuffle-sections= 2>&1 | FileCheck %s --check-prefix=USAGE -DV=
+# RUN: not ld.lld --shuffle-sections=a= 2>&1 | FileCheck %s --check-prefix=USAGE -DV=a=
+# RUN: not ld.lld --shuffle-sections==0 2>&1 | FileCheck %s --check-prefix=USAGE -DV==0
+# RUN: not ld.lld --shuffle-sections=a 2>&1 | FileCheck %s --check-prefix=USAGE -DV=a
+
+# USAGE: error: --shuffle-sections=: expected <section_glob>=<seed>, but got '[[V]]'
+
+# RUN: not ld.lld --shuffle-sections='['=0 2>&1 | FileCheck %s --check-prefix=INVALID
+
+# INVALID: error: --shuffle-sections=: invalid glob pattern: [
+
 ## .text has an alignment of 4.
 .global _start
 _start:


        


More information about the llvm-commits mailing list