diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -198,7 +198,7 @@ bool relocatable; bool relrPackDynRelocs; bool saveTemps; - llvm::Optional shuffleSectionSeed; + std::vector> shuffleSections; bool singleRoRx; bool shared; bool symbolic; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1068,8 +1068,6 @@ 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 @@ config->optEL = true; } + for (opt::Arg *arg : args.filtered(OPT_shuffle_sections)) { + constexpr StringRef errPrefix = "--shuffle-sections=: "; + std::pair kv = StringRef(arg->getValue()).split('='); + if (kv.first.empty() || kv.second.empty()) { + error(errPrefix + "expected =, but got '" + + arg->getValue() + "'"); + continue; + } + // Signed so that =-1 is allowed. + int64_t v; + if (!to_integer(kv.second, v)) + error(errPrefix + "expected an integer, but got '" + kv.second + "'"); + else if (Expected 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 option = StringRef(arg->getValue()).split('='); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -586,9 +586,10 @@ 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<"">, - 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<"=">; 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 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1291,29 +1291,39 @@ // Adds random priorities to sections not already in the map. static void maybeShuffle(DenseMap &order) { - if (!config->shuffleSectionSeed) + if (config->shuffleSections.empty()) return; - std::vector priorities(inputSections.size() - order.size()); + std::vector 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 =-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 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -29,7 +29,8 @@ Breaking changes ---------------- -* ... +* ``--shuffle-sections=`` has been changed to ``--shuffle-sections==``. + Specify ``*`` as ```` to get the previous behavior. COFF Improvements ----------------- diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -487,7 +487,7 @@ .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 --- 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 --- 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",@progbits,unique,\i diff --git a/lld/test/ELF/shuffle-sections.s b/lld/test/ELF/shuffle-sections.s --- 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 +# RUN: not ld.lld --shuffle-sections=a= 2>&1 | FileCheck %s --check-prefix=USAGE +# RUN: not ld.lld --shuffle-sections==0 2>&1 | FileCheck %s --check-prefix=USAGE +# RUN: not ld.lld --shuffle-sections=a 2>&1 | FileCheck %s --check-prefix=USAGE + +# USAGE: error: --shuffle-sections=: expected =, but got '{{.*}}' + +# 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: