Skip to content

Commit 16c30c3

Browse files
committed
[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
1 parent 580416d commit 16c30c3

File tree

9 files changed

+90
-40
lines changed

9 files changed

+90
-40
lines changed

lld/ELF/Config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ struct Configuration {
198198
bool relocatable;
199199
bool relrPackDynRelocs;
200200
bool saveTemps;
201-
llvm::Optional<uint32_t> shuffleSectionSeed;
201+
std::vector<std::pair<llvm::GlobPattern, uint32_t>> shuffleSections;
202202
bool singleRoRx;
203203
bool shared;
204204
bool symbolic;

lld/ELF/Driver.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,8 +1068,6 @@ static void readConfigs(opt::InputArgList &args) {
10681068
config->rpath = getRpath(args);
10691069
config->relocatable = args.hasArg(OPT_relocatable);
10701070
config->saveTemps = args.hasArg(OPT_save_temps);
1071-
if (args.hasArg(OPT_shuffle_sections))
1072-
config->shuffleSectionSeed = args::getInteger(args, OPT_shuffle_sections, 0);
10731071
config->searchPaths = args::getStrings(args, OPT_library_path);
10741072
config->sectionStartMap = getSectionStartMap(args);
10751073
config->shared = args.hasArg(OPT_shared);
@@ -1149,6 +1147,24 @@ static void readConfigs(opt::InputArgList &args) {
11491147
config->optEL = true;
11501148
}
11511149

1150+
for (opt::Arg *arg : args.filtered(OPT_shuffle_sections)) {
1151+
constexpr StringRef errPrefix = "--shuffle-sections=: ";
1152+
std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
1153+
if (kv.first.empty() || kv.second.empty()) {
1154+
error(errPrefix + "expected <section_glob>=<seed>, but got '" +
1155+
arg->getValue() + "'");
1156+
continue;
1157+
}
1158+
// Signed so that <section_glob>=-1 is allowed.
1159+
int64_t v;
1160+
if (!to_integer(kv.second, v))
1161+
error(errPrefix + "expected an integer, but got '" + kv.second + "'");
1162+
else if (Expected<GlobPattern> pat = GlobPattern::create(kv.first))
1163+
config->shuffleSections.emplace_back(std::move(*pat), uint32_t(v));
1164+
else
1165+
error(errPrefix + toString(pat.takeError()));
1166+
}
1167+
11521168
for (opt::Arg *arg : args.filtered(OPT_z)) {
11531169
std::pair<StringRef, StringRef> option =
11541170
StringRef(arg->getValue()).split('=');

lld/ELF/Options.td

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -586,9 +586,10 @@ def lto_basic_block_sections: JJ<"lto-basic-block-sections=">,
586586
defm lto_unique_basic_block_section_names: BB<"lto-unique-basic-block-section-names",
587587
"Give unique names to every basic block section for LTO",
588588
"Do not give unique names to every basic block section for LTO (default)">;
589-
def shuffle_sections: JJ<"shuffle-sections=">, MetaVarName<"<seed>">,
590-
HelpText<"Shuffle input sections using the given seed. "
591-
"If -1, reverse the section order. If 0, use a random seed">;
589+
defm shuffle_sections: EEq<"shuffle-sections",
590+
"Shuffle matched sections using the given seed before mapping them to the output sections. "
591+
"If -1, reverse the section order. If 0, use a random seed">,
592+
MetaVarName<"<section-glob>=<seed>">;
592593
def thinlto_cache_dir: JJ<"thinlto-cache-dir=">,
593594
HelpText<"Path to ThinLTO cached object file directory">;
594595
defm thinlto_cache_policy: EEq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;

lld/ELF/Writer.cpp

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,29 +1291,39 @@ findOrphanPos(std::vector<BaseCommand *>::iterator b,
12911291

12921292
// Adds random priorities to sections not already in the map.
12931293
static void maybeShuffle(DenseMap<const InputSectionBase *, int> &order) {
1294-
if (!config->shuffleSectionSeed)
1294+
if (config->shuffleSections.empty())
12951295
return;
12961296

1297-
std::vector<int> priorities(inputSections.size() - order.size());
1297+
std::vector<InputSectionBase *> matched, sections = inputSections;
1298+
matched.reserve(sections.size());
1299+
for (const auto &patAndSeed : config->shuffleSections) {
1300+
matched.clear();
1301+
for (InputSectionBase *sec : sections)
1302+
if (patAndSeed.first.match(sec->name))
1303+
matched.push_back(sec);
1304+
const uint32_t seed = patAndSeed.second;
1305+
if (seed == UINT32_MAX) {
1306+
// If --shuffle-sections <section-glob>=-1, reverse the section order. The
1307+
// section order is stable even if the number of sections changes. This is
1308+
// useful to catch issues like static initialization order fiasco
1309+
// reliably.
1310+
std::reverse(matched.begin(), matched.end());
1311+
} else {
1312+
std::mt19937 g(seed ? seed : std::random_device()());
1313+
llvm::shuffle(matched.begin(), matched.end(), g);
1314+
}
1315+
size_t i = 0;
1316+
for (InputSectionBase *&sec : sections)
1317+
if (patAndSeed.first.match(sec->name))
1318+
sec = matched[i++];
1319+
}
1320+
12981321
// Existing priorities are < 0, so use priorities >= 0 for the missing
12991322
// sections.
1300-
int curPrio = 0;
1301-
for (int &prio : priorities)
1302-
prio = curPrio++;
1303-
uint32_t seed = *config->shuffleSectionSeed;
1304-
if (seed == UINT32_MAX) {
1305-
// If --shuffle-sections=-1, reverse the section order. The section order is
1306-
// stable even if the number of sections changes. This is useful to catch
1307-
// issues like static initialization order fiasco reliably.
1308-
std::reverse(priorities.begin(), priorities.end());
1309-
} else {
1310-
std::mt19937 g(seed ? seed : std::random_device()());
1311-
llvm::shuffle(priorities.begin(), priorities.end(), g);
1312-
}
1313-
int prioIndex = 0;
1314-
for (InputSectionBase *sec : inputSections) {
1315-
if (order.try_emplace(sec, priorities[prioIndex]).second)
1316-
++prioIndex;
1323+
int prio = 0;
1324+
for (InputSectionBase *sec : sections) {
1325+
if (order.try_emplace(sec, prio).second)
1326+
++prio;
13171327
}
13181328
}
13191329

lld/docs/ReleaseNotes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ ELF Improvements
2929
Breaking changes
3030
----------------
3131

32-
* ...
32+
* ``--shuffle-sections=<seed>`` has been changed to ``--shuffle-sections=<section-glob>=<seed>``.
33+
Specify ``*`` as ``<section-glob>`` to get the previous behavior.
3334

3435
COFF Improvements
3536
-----------------

lld/docs/ld.lld.1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ Set address of section.
487487
.It Fl -shared , Fl -Bsharable
488488
Build a shared object.
489489
.It Fl -shuffle-sections Ns = Ns Ar seed
490-
Shuffle input sections using the given seed.
490+
Shuffle matched sections using the given seed before mapping them to the output sections.
491491
If -1, reverse the section order. If 0, use a random seed.
492492
.It Fl -soname Ns = Ns Ar value , Fl h Ar value
493493
Set

lld/test/ELF/gnu-ifunc-plt.s

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@
8080
// Test that --shuffle-sections does not affect the order of relocations and that
8181
// we still place IRELATIVE relocations last. Check both random seed (0) and an
8282
// arbitrary seed that was known to break the order of relocations previously (3).
83-
// RUN: ld.lld --shuffle-sections=3 %t.so %t.o -o %tout2
83+
// RUN: ld.lld --shuffle-sections='*=3' %t.so %t.o -o %tout2
8484
// RUN: llvm-readobj --relocations %tout2 | FileCheck %s --check-prefix=SHUFFLE
85-
// RUN: ld.lld --shuffle-sections=0 %t.so %t.o -o %tout3
85+
// RUN: ld.lld --shuffle-sections='*=0' %t.so %t.o -o %tout3
8686
// RUN: llvm-readobj --relocations %tout3 | FileCheck %s --check-prefix=SHUFFLE
8787

8888
// SHUFFLE: Section {{.*}} .rela.dyn {

lld/test/ELF/shuffle-sections-init-fini.s

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t | \
66
# RUN: FileCheck --check-prefixes=CHECK,ORDERED %s
77

8-
# RUN: ld.lld %t.o --shuffle-sections=1 -o %t1
8+
# RUN: ld.lld %t.o --shuffle-sections '*=1' -o %t1
99
# RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t1 | \
1010
# RUN: FileCheck --check-prefixes=CHECK,SHUFFLED %s
1111

@@ -21,12 +21,12 @@
2121
# CHECK: Hex dump of section '.init_array'
2222
# CHECK-NEXT: 0x{{[0-9a-f]+}} ff
2323
# ORDERED-SAME: 000102 03040506 0708090a 0b
24-
# SHUFFLED-SAME: 04000b 06010a08 09070203 05
24+
# SHUFFLED-SAME: 080301 04050907 0b020a06 00
2525

2626
# CHECK: Hex dump of section '.fini_array'
2727
# CHECK-NEXT: 0x{{[0-9a-f]+}} ff
2828
# ORDERED-SAME: 000102 03040506 0708090a 0b
29-
# SHUFFLED-SAME: 090401 070b0003 080a0605 02
29+
# SHUFFLED-SAME: 0a0405 08070b02 03090006 01
3030

3131
## With a SECTIONS command, SHT_INIT_ARRAY prirotities are ignored.
3232
## All .init_array* are shuffled together.
@@ -36,13 +36,13 @@
3636
# RUN: ld.lld -T %t.script %t.o -o %t2
3737
# RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t2 | \
3838
# RUN: FileCheck --check-prefixes=CHECK2,ORDERED2 %s
39-
# RUN: ld.lld -T %t.script %t.o --shuffle-sections=1 -o %t3
39+
# RUN: ld.lld -T %t.script %t.o --shuffle-sections '*=1' -o %t3
4040
# RUN: llvm-readelf -x .init -x .fini -x .init_array -x .fini_array %t3 | \
4141
# RUN: FileCheck --check-prefixes=CHECK2,SHUFFLED2 %s
4242

4343
# CHECK2: Hex dump of section '.init_array'
4444
# ORDERED2-NEXT: 0x{{[0-9a-f]+}} 00010203 04050607 08090a0b ff
45-
# SHUFFLED2-NEXT: 0x{{[0-9a-f]+}} 04000b06 010a0809 07ff0203 05
45+
# SHUFFLED2-NEXT: 0x{{[0-9a-f]+}} 08030104 0509070b 02ff0a06 00
4646

4747
.irp i,0,1,2,3,4,5,6,7,8,9,10,11
4848
.section .init,"ax",@progbits,unique,\i

lld/test/ELF/shuffle-sections.s

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,53 @@
77
# CHECK-NEXT: 01020304
88

99
## --shuffle-sections= shuffles input sections.
10-
# RUN: ld.lld --shuffle-sections=1 %t.o -o %t1.out
10+
# RUN: ld.lld --shuffle-sections='*=1' %t.o -o %t1.out
1111
# RUN: llvm-readelf -x .text %t1.out | FileCheck %s --check-prefix=SHUFFLE1
1212
# SHUFFLE1: Hex dump of section '.text':
13-
# SHUFFLE1-NEXT: 0204cccc 0103
13+
# SHUFFLE1-NEXT: 0203cccc 0104
1414

1515
## Test that --shuffle-sections= can be used with --symbol-ordering-file
1616
# RUN: echo "foo" > %t_order.txt
1717
# RUN: echo "_start " >> %t_order.txt
1818

19-
# RUN: ld.lld --symbol-ordering-file %t_order.txt --shuffle-sections=2 %t.o -o %t2.out
19+
# RUN: ld.lld --symbol-ordering-file %t_order.txt --shuffle-sections='*=2' %t.o -o %t2.out
2020
# RUN: llvm-readelf -x .text %t2.out | FileCheck %s --check-prefix=SHUFFLE2
2121
# SHUFFLE2: Hex dump of section '.text':
22-
# SHUFFLE2-NEXT: 02cccccc 010304
22+
# SHUFFLE2-NEXT: 02cccccc 010403
2323

24-
# RUN: ld.lld --symbol-ordering-file %t_order.txt --shuffle-sections=3 %t.o -o %t3.out
24+
# RUN: ld.lld --symbol-ordering-file %t_order.txt --shuffle-sections='*=3' %t.o -o %t3.out
2525
# RUN: llvm-readelf -x .text %t3.out | FileCheck %s --check-prefix=SHUFFLE3
2626
# SHUFFLE3: Hex dump of section '.text':
2727
# SHUFFLE3-NEXT: 02cccccc 010403
2828

2929
## As a special case, -1 reverses sections as a stable transform.
30-
# RUN: ld.lld --shuffle-sections=-1 %t.o -o %t-1.out
30+
# RUN: ld.lld --shuffle-sections '*=-1' %t.o -o %t-1.out
3131
# RUN: llvm-readelf -x .text %t-1.out | FileCheck %s --check-prefix=SHUFFLE-1
3232
# SHUFFLE-1: Hex dump of section '.text':
3333
# SHUFFLE-1-NEXT: 040302cc 01
3434

35+
## .text does not change its order while .text.{foo,bar,zed} are reversed.
36+
# RUN: ld.lld --shuffle-sections '.text.*=-1' %t.o -o %t4.out
37+
# RUN: llvm-readelf -x .text %t4.out | FileCheck %s --check-prefix=SHUFFLE4
38+
# SHUFFLE4: Hex dump of section '.text':
39+
# SHUFFLE4-NEXT: 01040302
40+
41+
## Reversing twice restores the original order.
42+
# RUN: ld.lld --shuffle-sections '.text.*=-1' --shuffle-sections '.text.*=-1' %t.o -o %t.out
43+
# RUN: llvm-readelf -x .text %t.out | FileCheck %s
44+
45+
## Test all possible invalid cases.
46+
# RUN: not ld.lld --shuffle-sections= 2>&1 | FileCheck %s --check-prefix=USAGE -DV=
47+
# RUN: not ld.lld --shuffle-sections=a= 2>&1 | FileCheck %s --check-prefix=USAGE -DV=a=
48+
# RUN: not ld.lld --shuffle-sections==0 2>&1 | FileCheck %s --check-prefix=USAGE -DV==0
49+
# RUN: not ld.lld --shuffle-sections=a 2>&1 | FileCheck %s --check-prefix=USAGE -DV=a
50+
51+
# USAGE: error: --shuffle-sections=: expected <section_glob>=<seed>, but got '[[V]]'
52+
53+
# RUN: not ld.lld --shuffle-sections='['=0 2>&1 | FileCheck %s --check-prefix=INVALID
54+
55+
# INVALID: error: --shuffle-sections=: invalid glob pattern: [
56+
3557
## .text has an alignment of 4.
3658
.global _start
3759
_start:

0 commit comments

Comments
 (0)