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.
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
unsigned size;
};
+struct InsertCommand {
+ OutputSection *os;
+ bool isAfter;
+ StringRef where;
+};
+
struct PhdrsCommand {
StringRef name;
unsigned type = llvm::ELF::PT_NULL;
// 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;
}
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("}")) {
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() {
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.
//
## 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 different
+## 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;
## 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 different
+## 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;
--- /dev/null
+# 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 different instance and matches no
+## input sections.
+SECTIONS { .foo.data : { *(.foo.data) } } INSERT AFTER .foo.text;
--- /dev/null
+# 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;