[lld] 7c426fb - [ELF] Support INSERT [AFTER|BEFORE] for orphan sections
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Wed Feb 12 08:23:03 PST 2020
Author: Fangrui Song
Date: 2020-02-12T08:21:52-08:00
New Revision: 7c426fb1a6d4b7db2517529ec5d863b380479249
URL: https://github.com/llvm/llvm-project/commit/7c426fb1a6d4b7db2517529ec5d863b380479249
DIFF: https://github.com/llvm/llvm-project/commit/7c426fb1a6d4b7db2517529ec5d863b380479249.diff
LOG: [ELF] Support INSERT [AFTER|BEFORE] for orphan sections
D43468+D44380 added INSERT [AFTER|BEFORE] for non-orphan sections. This patch
makes INSERT work for orphan sections as well.
`SECTIONS {...} INSERT [AFTER|BEFORE] .foo` does not set `hasSectionCommands`, so the result
will be similar to a regular link without a linker script. The differences when `hasSectionCommands` is set include:
* image base is different
* -z noseparate-code/-z noseparate-loadable-segments are unavailable
* some special symbols such as `_end _etext _edata` are not defined
The behavior is similar to GNU ld:
INSERT is not considered an external linker script.
This feature makes the section layout more flexible. It can be used to:
* Place .nv_fatbin before other readonly SHT_PROGBITS sections to mitigate relocation overflows.
* Disturb the layout to expose address sensitive application bugs.
Reviewed By: grimar
Differential Revision: https://reviews.llvm.org/D74375
Added:
lld/test/ELF/linkerscript/insert-duplicate.test
lld/test/ELF/linkerscript/insert-not-exist.test
Modified:
lld/ELF/Driver.cpp
lld/ELF/LinkerScript.cpp
lld/ELF/LinkerScript.h
lld/ELF/ScriptParser.cpp
lld/ELF/Writer.cpp
lld/test/ELF/linkerscript/insert-after.test
lld/test/ELF/linkerscript/insert-before.test
Removed:
################################################################################
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 60f3e88afb1c..b31da3776411 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1865,10 +1865,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
if (errorCount())
return;
- // Now when we read all script files, we want to finalize order of linker
- // script commands, which can be not yet final because of INSERT commands.
- script->processInsertCommands();
-
// We want to declare linker script's symbols early,
// so that we can version them.
// They also might be exported if referenced by DSOs.
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index edb5cac7a3e2..9643da2e49e8 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -246,32 +246,30 @@ getChangedSymbolAssignment(const SymbolAssignmentMap &oldValues) {
return changed;
}
-// This method is used to handle INSERT AFTER statement. Here we rebuild
-// the list of script commands to mix sections inserted into.
+// Process INSERT [AFTER|BEFORE] commands. For each command, we move the
+// specified output section to the designated place.
void LinkerScript::processInsertCommands() {
- std::vector<BaseCommand *> v;
- auto insert = [&](std::vector<BaseCommand *> &from) {
- v.insert(v.end(), from.begin(), from.end());
- from.clear();
- };
-
- for (BaseCommand *base : sectionCommands) {
- if (auto *os = dyn_cast<OutputSection>(base)) {
- insert(insertBeforeCommands[os->name]);
- v.push_back(base);
- insert(insertAfterCommands[os->name]);
+ for (const InsertCommand &cmd : insertCommands) {
+ // If cmd.os is empty, it may have been discarded by
+ // adjustSectionsBeforeSorting(). We do not handle such output sections.
+ auto from = llvm::find(sectionCommands, cmd.os);
+ if (from == sectionCommands.end())
continue;
+ sectionCommands.erase(from);
+
+ auto insertPos = llvm::find_if(sectionCommands, [&cmd](BaseCommand *base) {
+ auto *to = dyn_cast<OutputSection>(base);
+ return to != nullptr && to->name == cmd.where;
+ });
+ if (insertPos == sectionCommands.end()) {
+ error("unable to insert " + cmd.os->name +
+ (cmd.isAfter ? " after " : " before ") + cmd.where);
+ } else {
+ if (cmd.isAfter)
+ ++insertPos;
+ sectionCommands.insert(insertPos, cmd.os);
}
- v.push_back(base);
}
-
- for (auto &cmds : {insertBeforeCommands, insertAfterCommands})
- for (const std::pair<StringRef, std::vector<BaseCommand *>> &p : cmds)
- if (!p.second.empty())
- error("unable to INSERT AFTER/BEFORE " + p.first +
- ": section not defined");
-
- sectionCommands = std::move(v);
}
// Symbols defined in script should not be inlined by LTO. At the same time
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index f020552c362c..848e3e406917 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -208,6 +208,12 @@ struct ByteCommand : BaseCommand {
unsigned size;
};
+struct InsertCommand {
+ OutputSection *os;
+ bool isAfter;
+ StringRef where;
+};
+
struct PhdrsCommand {
StringRef name;
unsigned type = llvm::ELF::PT_NULL;
@@ -311,10 +317,9 @@ class LinkerScript final {
// A list of symbols referenced by the script.
std::vector<llvm::StringRef> referencedSymbols;
- // Used to implement INSERT [AFTER|BEFORE]. Contains commands that need
- // to be inserted into SECTIONS commands list.
- llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertAfterCommands;
- llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertBeforeCommands;
+ // Used to implement INSERT [AFTER|BEFORE]. Contains output sections that need
+ // to be reordered.
+ std::vector<InsertCommand> insertCommands;
};
extern LinkerScript *script;
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 635d28139337..81864207a7ed 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -523,13 +523,6 @@ std::vector<BaseCommand *> ScriptParser::readOverlay() {
}
void ScriptParser::readSections() {
- script->hasSectionsCommand = true;
-
- // -no-rosegment is used to avoid placing read only non-executable sections in
- // their own segment. We do the same if SECTIONS command is present in linker
- // script. See comment for computeFlags().
- config->singleRoRx = true;
-
expect("{");
std::vector<BaseCommand *> v;
while (!errorCount() && !consume("}")) {
@@ -548,22 +541,29 @@ void ScriptParser::readSections() {
else
v.push_back(readOutputSectionDescription(tok));
}
+ script->sectionCommands.insert(script->sectionCommands.end(), v.begin(),
+ v.end());
- if (!atEOF() && consume("INSERT")) {
- std::vector<BaseCommand *> *dest = nullptr;
- if (consume("AFTER"))
- dest = &script->insertAfterCommands[next()];
- else if (consume("BEFORE"))
- dest = &script->insertBeforeCommands[next()];
- else
- setError("expected AFTER/BEFORE, but got '" + next() + "'");
- if (dest)
- dest->insert(dest->end(), v.begin(), v.end());
+ if (atEOF() || !consume("INSERT")) {
+ // --no-rosegment is used to avoid placing read only non-executable sections
+ // in their own segment. We do the same if SECTIONS command is present in
+ // linker script. See comment for computeFlags().
+ // TODO This rule will be dropped in the future.
+ config->singleRoRx = true;
+
+ script->hasSectionsCommand = true;
return;
}
- script->sectionCommands.insert(script->sectionCommands.end(), v.begin(),
- v.end());
+ bool isAfter = false;
+ if (consume("AFTER"))
+ isAfter = true;
+ else if (!consume("BEFORE"))
+ setError("expected AFTER/BEFORE, but got '" + next() + "'");
+ StringRef where = next();
+ for (BaseCommand *cmd : v)
+ if (auto *os = dyn_cast<OutputSection>(cmd))
+ script->insertCommands.push_back({os, isAfter, where});
}
void ScriptParser::readTarget() {
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 7c2a3febc097..e3e39ac95e7f 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1420,9 +1420,15 @@ template <class ELFT> void Writer<ELFT>::sortSections() {
llvm::find_if(script->sectionCommands, isSection),
llvm::find_if(llvm::reverse(script->sectionCommands), isSection).base(),
compareSections);
+
+ // Process INSERT commands. From this point onwards the order of
+ // script->sectionCommands is fixed.
+ script->processInsertCommands();
return;
}
+ script->processInsertCommands();
+
// Orphan sections are sections present in the input files which are
// not explicitly placed into the output file by the linker script.
//
diff --git a/lld/test/ELF/linkerscript/insert-after.test b/lld/test/ELF/linkerscript/insert-after.test
index 005c1500b1be..65751c75206a 100644
--- a/lld/test/ELF/linkerscript/insert-after.test
+++ b/lld/test/ELF/linkerscript/insert-after.test
@@ -5,25 +5,39 @@
## we check that can use INSERT AFTER to insert sections .foo.data
## and .foo.text at the right places.
-SECTIONS {
- .foo.data : { *(.foo.data) }
-} INSERT AFTER .data;
+# RUN: ld.lld %t1.o -o %t1 --script %p/Inputs/insert-after.script --script %s
+# RUN: llvm-readelf -S -l %t1 | FileCheck %s
+# CHECK: Name Type Address Off
+# CHECK-NEXT: NULL 0000000000000000 000000
+# CHECK-NEXT: .text PROGBITS 0000000000000000 001000
+# CHECK-NEXT: .foo.text PROGBITS 0000000000000008 001008
+# CHECK-NEXT: .data PROGBITS 0000000000000010 001010
+# CHECK-NEXT: .foo.data PROGBITS 0000000000000018 001018
+# CHECK: Type
+# CHECK-NEXT: LOAD {{.*}} R E
+# CHECK-NEXT: LOAD {{.*}} RW
+# CHECK-NEXT: GNU_STACK {{.*}} RW
-SECTIONS {
- .foo.text : { *(.foo.text) }
-} INSERT AFTER .text;
+## There is no main linker script. INSERT AFTER just reorders output sections,
+## without making more layout changes. Address/offset assignments are
diff erent
+## with a main linker script.
-# RUN: ld.lld %t1.o -o %t1 --script %p/Inputs/insert-after.script --script %s
-# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
-# CHECK: Sections:
-# CHECK-NEXT: Idx Name Size VMA Type
-# CHECK-NEXT: 0 00000000 0000000000000000
-# CHECK-NEXT: 1 .text 00000008 0000000000000000 TEXT
-# CHECK-NEXT: 2 .foo.text 00000008 0000000000000008 TEXT
-# CHECK-NEXT: 3 .data 00000008 0000000000000010 DATA
-# CHECK-NEXT: 4 .foo.data 00000008 0000000000000018 DATA
+# RUN: ld.lld --script %s %t1.o -o %t2
+# RUN: llvm-readelf -S -l %t2 | FileCheck --check-prefix=CHECK2 %s
+# CHECK2: Name Type Address Off
+# CHECK2-NEXT: NULL 0000000000000000 000000
+# CHECK2-NEXT: .text PROGBITS 0000000000201158 000158
+# CHECK2-NEXT: .foo.text PROGBITS 0000000000201160 000160
+# CHECK2-NEXT: .data PROGBITS 0000000000202168 000168
+# CHECK2-NEXT: .foo.data PROGBITS 0000000000202170 000170
+# CHECK2: Type
+# CHECK2-NEXT: PHDR {{.*}} R
+# CHECK2-NEXT: LOAD {{.*}} R
+# CHECK2-NEXT: LOAD {{.*}} R E
+# CHECK2-NEXT: LOAD {{.*}} RW
+# CHECK2-NEXT: GNU_STACK {{.*}} RW
+
+SECTIONS { .foo.data : { *(.foo.data) } } INSERT AFTER .data;
-# RUN: not ld.lld %t1.o -o %t1 --script %s 2>&1 \
-# RUN: | FileCheck %s --check-prefix=ERR
-# ERR-DAG: error: unable to INSERT AFTER/BEFORE .text: section not defined
-# ERR-DAG: error: unable to INSERT AFTER/BEFORE .data: section not defined
+## The input section .foo.text is an orphan. It will be placed in .foo.text
+SECTIONS { .foo.text : {} } INSERT AFTER .text;
diff --git a/lld/test/ELF/linkerscript/insert-before.test b/lld/test/ELF/linkerscript/insert-before.test
index 24fdd69a80ac..8fe912fdf912 100644
--- a/lld/test/ELF/linkerscript/insert-before.test
+++ b/lld/test/ELF/linkerscript/insert-before.test
@@ -5,25 +5,38 @@
## we check that can use INSERT BEFORE to insert sections .foo.data
## and .foo.text at the right places.
-SECTIONS {
- .foo.data : { *(.foo.data) }
-} INSERT BEFORE .data;
+# RUN: ld.lld %t1.o -o %t1 --script %p/Inputs/insert-after.script --script %s
+# RUN: llvm-readelf -S -l %t1 | FileCheck %s
+# CHECK: Name Type Address Off
+# CHECK-NEXT: NULL 0000000000000000 000000
+# CHECK-NEXT: .foo.text PROGBITS 0000000000000000 001000
+# CHECK-NEXT: .text PROGBITS 0000000000000008 001008
+# CHECK-NEXT: .foo.data PROGBITS 0000000000000010 001010
+# CHECK-NEXT: .data PROGBITS 0000000000000018 001018
+# CHECK: Type
+# CHECK-NEXT: LOAD {{.*}} R E
+# CHECK-NEXT: LOAD {{.*}} RW
+# CHECK-NEXT: GNU_STACK {{.*}} RW
-SECTIONS {
- .foo.text : { *(.foo.text) }
-} INSERT BEFORE .text;
+## There is no main linker script. INSERT BEFORE just reorders output sections,
+## without making more layout changes. Address/offset assignments are
diff erent
+## with a main linker script.
-# RUN: ld.lld %t1.o -o %t1 --script %p/Inputs/insert-after.script --script %s
-# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
-# CHECK: Sections:
-# CHECK-NEXT: Idx Name Size VMA Type
-# CHECK-NEXT: 0 00000000 0000000000000000
-# CHECK-NEXT: 1 .foo.text 00000008 0000000000000000 TEXT
-# CHECK-NEXT: 2 .text 00000008 0000000000000008 TEXT
-# CHECK-NEXT: 3 .foo.data 00000008 0000000000000010 DATA
-# CHECK-NEXT: 4 .data 00000008 0000000000000018 DATA
+# RUN: ld.lld --script %s %t1.o -o %t2
+# RUN: llvm-readelf -S -l %t2 | FileCheck --check-prefix=CHECK2 %s
+# CHECK2: Name Type Address Off
+# CHECK2-NEXT: NULL 0000000000000000 000000
+# CHECK2-NEXT: .foo.text PROGBITS 0000000000201158 000158
+# CHECK2-NEXT: .text PROGBITS 0000000000201160 000160
+# CHECK2-NEXT: .foo.data PROGBITS 0000000000202168 000168
+# CHECK2-NEXT: .data PROGBITS 0000000000202170 000170
+# CHECK2: Type
+# CHECK2-NEXT: PHDR {{.*}} R
+# CHECK2-NEXT: LOAD {{.*}} R
+# CHECK2-NEXT: LOAD {{.*}} R E
+# CHECK2-NEXT: LOAD {{.*}} RW
+
+SECTIONS { .foo.data : { *(.foo.data) } } INSERT BEFORE .data;
-# RUN: not ld.lld %t1.o -o %t1 --script %s 2>&1 \
-# RUN: | FileCheck %s --check-prefix=ERR
-# ERR-DAG: error: unable to INSERT AFTER/BEFORE .text: section not defined
-# ERR-DAG: error: unable to INSERT AFTER/BEFORE .data: section not defined
+## The input section .foo.text is an orphan. It will be placed in .foo.text
+SECTIONS { .foo.text : {} } INSERT BEFORE .text;
diff --git a/lld/test/ELF/linkerscript/insert-duplicate.test b/lld/test/ELF/linkerscript/insert-duplicate.test
new file mode 100644
index 000000000000..8fa7e2ce4b98
--- /dev/null
+++ b/lld/test/ELF/linkerscript/insert-duplicate.test
@@ -0,0 +1,32 @@
+# REQUIRES: x86
+## Test that we can handle cases where an output section is specified by multiple
+## INSERT commands. Each output section description creates a new instance.
+## A redundant description matches no input sections and thus is a no-op.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/insert-after.s -o %t.o
+# RUN: ld.lld -T %s %t.o -o %t
+# RUN: llvm-readelf -S -l %t | FileCheck %s
+
+# CHECK: Name Type Address Off
+# CHECK-NEXT: NULL 0000000000000000 000000
+# CHECK-NEXT: .text PROGBITS 00000000002011c8 0001c8
+# CHECK-NEXT: .foo.data PROGBITS 00000000002021d0 0001d0
+# CHECK-NEXT: .foo.text PROGBITS 00000000002031d8 0001d8
+# CHECK: Type
+# CHECK-NEXT: PHDR {{.*}} R
+# CHECK-NEXT: LOAD {{.*}} R
+# CHECK-NEXT: LOAD {{.*}} R E
+# CHECK-NEXT: LOAD {{.*}} RW
+# CHECK-NEXT: LOAD {{.*}} R E
+# CHECK-NEXT: LOAD {{.*}} RW
+# CHECK-NEXT: GNU_STACK {{.*}} RW
+
+## First, move .foo.data after .foo.text
+SECTIONS { .foo.data : { *(.foo.data) } } INSERT AFTER .foo.text;
+
+## Next, move .foo.text after .foo.data
+SECTIONS { .foo.text : { *(.foo.text) } } INSERT AFTER .foo.data;
+
+## No-op. The .foo.data output section is a
diff erent instance and matches no
+## input sections.
+SECTIONS { .foo.data : { *(.foo.data) } } INSERT AFTER .foo.text;
diff --git a/lld/test/ELF/linkerscript/insert-not-exist.test b/lld/test/ELF/linkerscript/insert-not-exist.test
new file mode 100644
index 000000000000..1f95070517e1
--- /dev/null
+++ b/lld/test/ELF/linkerscript/insert-not-exist.test
@@ -0,0 +1,9 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/insert-after.s -o %t.o
+# RUN: not ld.lld -T %s %t.o 2>&1 | FileCheck %s
+
+# CHECK: error: unable to insert .foo.data after .not_exist
+# CHECK: error: unable to insert .foo.text before .not_exist
+
+SECTIONS { .foo.data : {} } INSERT AFTER .not_exist;
+SECTIONS { .foo.text : {} } INSERT BEFORE .not_exist;
More information about the llvm-commits
mailing list