llvm-project/lld/ELF/ScriptParser.cpp

1549 lines
45 KiB
C++
Raw Normal View History

//===- ScriptParser.cpp ---------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
2017-04-05 13:50:08 +08:00
//
// This file contains a recursive-descendent parser for linker scripts.
// Parsed results are stored to Config and Script global objects.
//
//===----------------------------------------------------------------------===//
#include "ScriptParser.h"
#include "Config.h"
#include "Driver.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "OutputSections.h"
#include "ScriptLexer.h"
#include "Symbols.h"
#include "Target.h"
#include "lld/Common/Memory.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include <cassert>
#include <limits>
#include <vector>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
static bool isUnderSysroot(StringRef Path);
namespace {
class ScriptParser final : ScriptLexer {
public:
ScriptParser(MemoryBufferRef MB)
: ScriptLexer(MB),
IsUnderSysroot(isUnderSysroot(MB.getBufferIdentifier())) {}
void readLinkerScript();
void readVersionScript();
void readDynamicList();
void readDefsym(StringRef Name);
private:
void addFile(StringRef Path);
void readAsNeeded();
void readEntry();
void readExtern();
void readGroup();
void readInclude();
Add --warn-backrefs to maintain compatibility with other linkers I'm proposing a new command line flag, --warn-backrefs in this patch. The flag and the feature proposed below don't exist in GNU linkers nor the current lld. --warn-backrefs is an option to detect reverse or cyclic dependencies between static archives, and it can be used to keep your program compatible with GNU linkers after you switch to lld. I'll explain the feature and why you may find it useful below. lld's symbol resolution semantics is more relaxed than traditional Unix linkers. Therefore, ld.lld foo.a bar.o succeeds even if bar.o contains an undefined symbol that have to be resolved by some object file in foo.a. Traditional Unix linkers don't allow this kind of backward reference, as they visit each file only once from left to right in the command line while resolving all undefined symbol at the moment of visiting. In the above case, since there's no undefined symbol when a linker visits foo.a, no files are pulled out from foo.a, and because the linker forgets about foo.a after visiting, it can't resolve undefined symbols that could have been resolved otherwise. That lld accepts more relaxed form means (besides it makes more sense) that you can accidentally write a command line or a build file that works only with lld, even if you have a plan to distribute it to wider users who may be using GNU linkers. With --check-library-dependency, you can detect a library order that doesn't work with other Unix linkers. The option is also useful to detect cyclic dependencies between static archives. Again, lld accepts ld.lld foo.a bar.a even if foo.a and bar.a depend on each other. With --warn-backrefs it is handled as an error. Here is how the option works. We assign a group ID to each file. A file with a smaller group ID can pull out object files from an archive file with an equal or greater group ID. Otherwise, it is a reverse dependency and an error. A file outside --{start,end}-group gets a fresh ID when instantiated. All files within the same --{start,end}-group get the same group ID. E.g. ld.lld A B --start-group C D --end-group E A and B form group 0, C, D and their member object files form group 1, and E forms group 2. I think that you can see how this group assignment rule simulates the traditional linker's semantics. Differential Revision: https://reviews.llvm.org/D45195 llvm-svn: 329636
2018-04-10 07:05:48 +08:00
void readInput();
void readMemory();
void readOutput();
void readOutputArch();
void readOutputFormat();
void readPhdrs();
void readRegionAlias();
void readSearchDir();
void readSections();
void readTarget();
void readVersion();
void readVersionScriptCommand();
SymbolAssignment *readSymbolAssignment(StringRef Name);
ByteCommand *readByteCommand(StringRef Tok);
std::array<uint8_t, 4> readFill();
std::array<uint8_t, 4> parseFill(StringRef Tok);
bool readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2);
void readSectionAddressType(OutputSection *Cmd);
OutputSection *readOverlaySectionDescription();
OutputSection *readOutputSectionDescription(StringRef OutSec);
std::vector<BaseCommand *> readOverlay();
std::vector<StringRef> readOutputSectionPhdrs();
InputSectionDescription *readInputSectionDescription(StringRef Tok);
StringMatcher readFilePatterns();
std::vector<SectionPattern> readInputSectionsList();
InputSectionDescription *readInputSectionRules(StringRef FilePattern);
unsigned readPhdrType();
SortSectionPolicy readSortKind();
SymbolAssignment *readProvideHidden(bool Provide, bool Hidden);
SymbolAssignment *readAssignment(StringRef Tok);
std::tuple<ELFKind, uint16_t, bool> readBfdName();
void readSort();
Expr readAssert();
Expr readConstant();
Expr getPageSize();
uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
std::pair<uint32_t, uint32_t> readMemoryAttributes();
Expr combine(StringRef Op, Expr L, Expr R);
Expr readExpr();
Expr readExpr1(Expr Lhs, int MinPrec);
StringRef readParenLiteral();
Expr readPrimary();
Expr readTernary(Expr Cond);
Expr readParenExpr();
// For parsing version script.
std::vector<SymbolVersion> readVersionExtern();
void readAnonymousDeclaration();
void readVersionDeclaration(StringRef VerStr);
std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
readSymbols();
2017-09-07 02:09:06 +08:00
// True if a script being read is in a subdirectory specified by -sysroot.
bool IsUnderSysroot;
// A set to detect an INCLUDE() cycle.
StringSet<> Seen;
};
} // namespace
static StringRef unquote(StringRef S) {
if (S.startswith("\""))
return S.substr(1, S.size() - 2);
return S;
}
static bool isUnderSysroot(StringRef Path) {
if (Config->Sysroot == "")
return false;
for (; !Path.empty(); Path = sys::path::parent_path(Path))
if (sys::fs::equivalent(Config->Sysroot, Path))
return true;
return false;
}
// Some operations only support one non absolute value. Move the
// absolute one to the right hand side for convenience.
static void moveAbsRight(ExprValue &A, ExprValue &B) {
if (A.Sec == nullptr || (A.ForceAbsolute && !B.isAbsolute()))
std::swap(A, B);
if (!B.isAbsolute())
error(A.Loc + ": at least one side of the expression must be absolute");
}
static ExprValue add(ExprValue A, ExprValue B) {
moveAbsRight(A, B);
return {A.Sec, A.ForceAbsolute, A.getSectionOffset() + B.getValue(), A.Loc};
}
static ExprValue sub(ExprValue A, ExprValue B) {
// The distance between two symbols in sections is absolute.
if (!A.isAbsolute() && !B.isAbsolute())
return A.getValue() - B.getValue();
return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc};
}
static ExprValue bitAnd(ExprValue A, ExprValue B) {
moveAbsRight(A, B);
return {A.Sec, A.ForceAbsolute,
(A.getValue() & B.getValue()) - A.getSecAddr(), A.Loc};
}
static ExprValue bitOr(ExprValue A, ExprValue B) {
moveAbsRight(A, B);
return {A.Sec, A.ForceAbsolute,
(A.getValue() | B.getValue()) - A.getSecAddr(), A.Loc};
}
void ScriptParser::readDynamicList() {
Config->HasDynamicList = true;
expect("{");
std::vector<SymbolVersion> Locals;
std::vector<SymbolVersion> Globals;
std::tie(Locals, Globals) = readSymbols();
expect(";");
if (!atEOF()) {
setError("EOF expected, but got " + next());
return;
}
if (!Locals.empty()) {
setError("\"local:\" scope not supported in --dynamic-list");
return;
}
for (SymbolVersion V : Globals)
Config->DynamicList.push_back(V);
}
void ScriptParser::readVersionScript() {
readVersionScriptCommand();
if (!atEOF())
setError("EOF expected, but got " + next());
}
void ScriptParser::readVersionScriptCommand() {
if (consume("{")) {
readAnonymousDeclaration();
return;
}
while (!atEOF() && !errorCount() && peek() != "}") {
StringRef VerStr = next();
if (VerStr == "{") {
setError("anonymous version definition is used in "
"combination with other version definitions");
return;
}
expect("{");
readVersionDeclaration(VerStr);
}
}
void ScriptParser::readVersion() {
expect("{");
readVersionScriptCommand();
expect("}");
}
void ScriptParser::readLinkerScript() {
while (!atEOF()) {
StringRef Tok = next();
if (Tok == ";")
continue;
if (Tok == "ENTRY") {
readEntry();
} else if (Tok == "EXTERN") {
readExtern();
Add --warn-backrefs to maintain compatibility with other linkers I'm proposing a new command line flag, --warn-backrefs in this patch. The flag and the feature proposed below don't exist in GNU linkers nor the current lld. --warn-backrefs is an option to detect reverse or cyclic dependencies between static archives, and it can be used to keep your program compatible with GNU linkers after you switch to lld. I'll explain the feature and why you may find it useful below. lld's symbol resolution semantics is more relaxed than traditional Unix linkers. Therefore, ld.lld foo.a bar.o succeeds even if bar.o contains an undefined symbol that have to be resolved by some object file in foo.a. Traditional Unix linkers don't allow this kind of backward reference, as they visit each file only once from left to right in the command line while resolving all undefined symbol at the moment of visiting. In the above case, since there's no undefined symbol when a linker visits foo.a, no files are pulled out from foo.a, and because the linker forgets about foo.a after visiting, it can't resolve undefined symbols that could have been resolved otherwise. That lld accepts more relaxed form means (besides it makes more sense) that you can accidentally write a command line or a build file that works only with lld, even if you have a plan to distribute it to wider users who may be using GNU linkers. With --check-library-dependency, you can detect a library order that doesn't work with other Unix linkers. The option is also useful to detect cyclic dependencies between static archives. Again, lld accepts ld.lld foo.a bar.a even if foo.a and bar.a depend on each other. With --warn-backrefs it is handled as an error. Here is how the option works. We assign a group ID to each file. A file with a smaller group ID can pull out object files from an archive file with an equal or greater group ID. Otherwise, it is a reverse dependency and an error. A file outside --{start,end}-group gets a fresh ID when instantiated. All files within the same --{start,end}-group get the same group ID. E.g. ld.lld A B --start-group C D --end-group E A and B form group 0, C, D and their member object files form group 1, and E forms group 2. I think that you can see how this group assignment rule simulates the traditional linker's semantics. Differential Revision: https://reviews.llvm.org/D45195 llvm-svn: 329636
2018-04-10 07:05:48 +08:00
} else if (Tok == "GROUP") {
readGroup();
} else if (Tok == "INCLUDE") {
readInclude();
Add --warn-backrefs to maintain compatibility with other linkers I'm proposing a new command line flag, --warn-backrefs in this patch. The flag and the feature proposed below don't exist in GNU linkers nor the current lld. --warn-backrefs is an option to detect reverse or cyclic dependencies between static archives, and it can be used to keep your program compatible with GNU linkers after you switch to lld. I'll explain the feature and why you may find it useful below. lld's symbol resolution semantics is more relaxed than traditional Unix linkers. Therefore, ld.lld foo.a bar.o succeeds even if bar.o contains an undefined symbol that have to be resolved by some object file in foo.a. Traditional Unix linkers don't allow this kind of backward reference, as they visit each file only once from left to right in the command line while resolving all undefined symbol at the moment of visiting. In the above case, since there's no undefined symbol when a linker visits foo.a, no files are pulled out from foo.a, and because the linker forgets about foo.a after visiting, it can't resolve undefined symbols that could have been resolved otherwise. That lld accepts more relaxed form means (besides it makes more sense) that you can accidentally write a command line or a build file that works only with lld, even if you have a plan to distribute it to wider users who may be using GNU linkers. With --check-library-dependency, you can detect a library order that doesn't work with other Unix linkers. The option is also useful to detect cyclic dependencies between static archives. Again, lld accepts ld.lld foo.a bar.a even if foo.a and bar.a depend on each other. With --warn-backrefs it is handled as an error. Here is how the option works. We assign a group ID to each file. A file with a smaller group ID can pull out object files from an archive file with an equal or greater group ID. Otherwise, it is a reverse dependency and an error. A file outside --{start,end}-group gets a fresh ID when instantiated. All files within the same --{start,end}-group get the same group ID. E.g. ld.lld A B --start-group C D --end-group E A and B form group 0, C, D and their member object files form group 1, and E forms group 2. I think that you can see how this group assignment rule simulates the traditional linker's semantics. Differential Revision: https://reviews.llvm.org/D45195 llvm-svn: 329636
2018-04-10 07:05:48 +08:00
} else if (Tok == "INPUT") {
readInput();
} else if (Tok == "MEMORY") {
readMemory();
} else if (Tok == "OUTPUT") {
readOutput();
} else if (Tok == "OUTPUT_ARCH") {
readOutputArch();
} else if (Tok == "OUTPUT_FORMAT") {
readOutputFormat();
} else if (Tok == "PHDRS") {
readPhdrs();
} else if (Tok == "REGION_ALIAS") {
readRegionAlias();
} else if (Tok == "SEARCH_DIR") {
readSearchDir();
} else if (Tok == "SECTIONS") {
readSections();
} else if (Tok == "TARGET") {
readTarget();
} else if (Tok == "VERSION") {
readVersion();
} else if (SymbolAssignment *Cmd = readAssignment(Tok)) {
Script->SectionCommands.push_back(Cmd);
} else {
setError("unknown directive: " + Tok);
}
}
}
void ScriptParser::readDefsym(StringRef Name) {
if (errorCount())
return;
Expr E = readExpr();
if (!atEOF())
setError("EOF expected, but got " + next());
SymbolAssignment *Cmd = make<SymbolAssignment>(Name, E, getCurrentLocation());
Script->SectionCommands.push_back(Cmd);
}
void ScriptParser::addFile(StringRef S) {
if (IsUnderSysroot && S.startswith("/")) {
SmallString<128> PathData;
StringRef Path = (Config->Sysroot + S).toStringRef(PathData);
if (sys::fs::exists(Path)) {
Driver->addFile(Saver.save(Path), /*WithLOption=*/false);
return;
}
}
if (S.startswith("/")) {
Driver->addFile(S, /*WithLOption=*/false);
} else if (S.startswith("=")) {
if (Config->Sysroot.empty())
Driver->addFile(S.substr(1), /*WithLOption=*/false);
else
Driver->addFile(Saver.save(Config->Sysroot + "/" + S.substr(1)),
/*WithLOption=*/false);
} else if (S.startswith("-l")) {
Driver->addLibrary(S.substr(2));
} else if (sys::fs::exists(S)) {
Driver->addFile(S, /*WithLOption=*/false);
} else {
if (Optional<std::string> Path = findFromSearchPaths(S))
Driver->addFile(Saver.save(*Path), /*WithLOption=*/true);
else
setError("unable to find " + S);
}
}
void ScriptParser::readAsNeeded() {
expect("(");
bool Orig = Config->AsNeeded;
Config->AsNeeded = true;
while (!errorCount() && !consume(")"))
addFile(unquote(next()));
Config->AsNeeded = Orig;
}
void ScriptParser::readEntry() {
// -e <symbol> takes predecence over ENTRY(<symbol>).
expect("(");
StringRef Tok = next();
if (Config->Entry.empty())
Config->Entry = Tok;
expect(")");
}
void ScriptParser::readExtern() {
expect("(");
while (!errorCount() && !consume(")"))
Config->Undefined.push_back(next());
}
void ScriptParser::readGroup() {
Add --warn-backrefs to maintain compatibility with other linkers I'm proposing a new command line flag, --warn-backrefs in this patch. The flag and the feature proposed below don't exist in GNU linkers nor the current lld. --warn-backrefs is an option to detect reverse or cyclic dependencies between static archives, and it can be used to keep your program compatible with GNU linkers after you switch to lld. I'll explain the feature and why you may find it useful below. lld's symbol resolution semantics is more relaxed than traditional Unix linkers. Therefore, ld.lld foo.a bar.o succeeds even if bar.o contains an undefined symbol that have to be resolved by some object file in foo.a. Traditional Unix linkers don't allow this kind of backward reference, as they visit each file only once from left to right in the command line while resolving all undefined symbol at the moment of visiting. In the above case, since there's no undefined symbol when a linker visits foo.a, no files are pulled out from foo.a, and because the linker forgets about foo.a after visiting, it can't resolve undefined symbols that could have been resolved otherwise. That lld accepts more relaxed form means (besides it makes more sense) that you can accidentally write a command line or a build file that works only with lld, even if you have a plan to distribute it to wider users who may be using GNU linkers. With --check-library-dependency, you can detect a library order that doesn't work with other Unix linkers. The option is also useful to detect cyclic dependencies between static archives. Again, lld accepts ld.lld foo.a bar.a even if foo.a and bar.a depend on each other. With --warn-backrefs it is handled as an error. Here is how the option works. We assign a group ID to each file. A file with a smaller group ID can pull out object files from an archive file with an equal or greater group ID. Otherwise, it is a reverse dependency and an error. A file outside --{start,end}-group gets a fresh ID when instantiated. All files within the same --{start,end}-group get the same group ID. E.g. ld.lld A B --start-group C D --end-group E A and B form group 0, C, D and their member object files form group 1, and E forms group 2. I think that you can see how this group assignment rule simulates the traditional linker's semantics. Differential Revision: https://reviews.llvm.org/D45195 llvm-svn: 329636
2018-04-10 07:05:48 +08:00
bool Orig = InputFile::IsInGroup;
InputFile::IsInGroup = true;
readInput();
InputFile::IsInGroup = Orig;
if (!Orig)
++InputFile::NextGroupId;
}
void ScriptParser::readInclude() {
StringRef Tok = unquote(next());
if (!Seen.insert(Tok).second) {
setError("there is a cycle in linker script INCLUDEs");
return;
}
if (Optional<std::string> Path = searchScript(Tok)) {
if (Optional<MemoryBufferRef> MB = readFile(*Path))
tokenize(*MB);
return;
}
setError("cannot find linker script " + Tok);
}
Add --warn-backrefs to maintain compatibility with other linkers I'm proposing a new command line flag, --warn-backrefs in this patch. The flag and the feature proposed below don't exist in GNU linkers nor the current lld. --warn-backrefs is an option to detect reverse or cyclic dependencies between static archives, and it can be used to keep your program compatible with GNU linkers after you switch to lld. I'll explain the feature and why you may find it useful below. lld's symbol resolution semantics is more relaxed than traditional Unix linkers. Therefore, ld.lld foo.a bar.o succeeds even if bar.o contains an undefined symbol that have to be resolved by some object file in foo.a. Traditional Unix linkers don't allow this kind of backward reference, as they visit each file only once from left to right in the command line while resolving all undefined symbol at the moment of visiting. In the above case, since there's no undefined symbol when a linker visits foo.a, no files are pulled out from foo.a, and because the linker forgets about foo.a after visiting, it can't resolve undefined symbols that could have been resolved otherwise. That lld accepts more relaxed form means (besides it makes more sense) that you can accidentally write a command line or a build file that works only with lld, even if you have a plan to distribute it to wider users who may be using GNU linkers. With --check-library-dependency, you can detect a library order that doesn't work with other Unix linkers. The option is also useful to detect cyclic dependencies between static archives. Again, lld accepts ld.lld foo.a bar.a even if foo.a and bar.a depend on each other. With --warn-backrefs it is handled as an error. Here is how the option works. We assign a group ID to each file. A file with a smaller group ID can pull out object files from an archive file with an equal or greater group ID. Otherwise, it is a reverse dependency and an error. A file outside --{start,end}-group gets a fresh ID when instantiated. All files within the same --{start,end}-group get the same group ID. E.g. ld.lld A B --start-group C D --end-group E A and B form group 0, C, D and their member object files form group 1, and E forms group 2. I think that you can see how this group assignment rule simulates the traditional linker's semantics. Differential Revision: https://reviews.llvm.org/D45195 llvm-svn: 329636
2018-04-10 07:05:48 +08:00
void ScriptParser::readInput() {
expect("(");
while (!errorCount() && !consume(")")) {
if (consume("AS_NEEDED"))
readAsNeeded();
else
addFile(unquote(next()));
}
}
void ScriptParser::readOutput() {
// -o <file> takes predecence over OUTPUT(<file>).
expect("(");
StringRef Tok = next();
if (Config->OutputFile.empty())
Config->OutputFile = unquote(Tok);
expect(")");
}
void ScriptParser::readOutputArch() {
// OUTPUT_ARCH is ignored for now.
expect("(");
while (!errorCount() && !consume(")"))
skip();
}
std::tuple<ELFKind, uint16_t, bool> ScriptParser::readBfdName() {
StringRef S = unquote(next());
if (S == "elf32-i386")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF32LEKind, EM_386, false);
if (S == "elf32-iamcu")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF32LEKind, EM_IAMCU, false);
if (S == "elf32-littlearm")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF32LEKind, EM_ARM, false);
if (S == "elf32-x86-64")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF32LEKind, EM_X86_64, false);
if (S == "elf64-littleaarch64")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF64LEKind, EM_AARCH64, false);
if (S == "elf64-powerpc")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF64BEKind, EM_PPC64, false);
if (S == "elf64-powerpcle")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF64LEKind, EM_PPC64, false);
if (S == "elf64-x86-64")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF64LEKind, EM_X86_64, false);
if (S == "elf32-tradbigmips")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF32BEKind, EM_MIPS, false);
if (S == "elf32-ntradbigmips")
2018-11-28 21:20:39 +08:00
return std::make_tuple(ELF32BEKind, EM_MIPS, true);
if (S == "elf32-tradlittlemips")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF32LEKind, EM_MIPS, false);
if (S == "elf32-ntradlittlemips")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF32LEKind, EM_MIPS, true);
if (S == "elf64-tradbigmips")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF64BEKind, EM_MIPS, false);
if (S == "elf64-tradlittlemips")
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELF64LEKind, EM_MIPS, false);
setError("unknown output format name: " + S);
2018-11-28 21:15:06 +08:00
return std::make_tuple(ELFNoneKind, EM_NONE, false);
}
// Parse OUTPUT_FORMAT(bfdname) or OUTPUT_FORMAT(bfdname, big, little).
// Currently we ignore big and little parameters.
void ScriptParser::readOutputFormat() {
expect("(");
std::tuple<ELFKind, uint16_t, bool> BfdTuple = readBfdName();
if (Config->EKind == ELFNoneKind)
std::tie(Config->EKind, Config->EMachine, Config->MipsN32Abi) = BfdTuple;
if (consume(")"))
return;
expect(",");
skip();
expect(",");
skip();
expect(")");
}
void ScriptParser::readPhdrs() {
expect("{");
while (!errorCount() && !consume("}")) {
PhdrsCommand Cmd;
Cmd.Name = next();
Cmd.Type = readPhdrType();
while (!errorCount() && !consume(";")) {
if (consume("FILEHDR"))
Cmd.HasFilehdr = true;
else if (consume("PHDRS"))
Cmd.HasPhdrs = true;
else if (consume("AT"))
Cmd.LMAExpr = readParenExpr();
else if (consume("FLAGS"))
Cmd.Flags = readParenExpr()().getValue();
else
setError("unexpected header attribute: " + next());
}
Script->PhdrsCommands.push_back(Cmd);
}
}
void ScriptParser::readRegionAlias() {
expect("(");
StringRef Alias = unquote(next());
expect(",");
StringRef Name = next();
expect(")");
if (Script->MemoryRegions.count(Alias))
setError("redefinition of memory region '" + Alias + "'");
if (!Script->MemoryRegions.count(Name))
setError("memory region '" + Name + "' is not defined");
Script->MemoryRegions.insert({Alias, Script->MemoryRegions[Name]});
}
void ScriptParser::readSearchDir() {
expect("(");
StringRef Tok = next();
if (!Config->Nostdlib)
Config->SearchPaths.push_back(unquote(Tok));
expect(")");
}
// This reads an overlay description. Overlays are used to describe output
// sections that use the same virtual memory range and normally would trigger
// linker's sections sanity check failures.
// https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description
std::vector<BaseCommand *> ScriptParser::readOverlay() {
// VA and LMA expressions are optional, though for simplicity of
// implementation we assume they are not. That is what OVERLAY was designed
// for first of all: to allow sections with overlapping VAs at different LMAs.
Expr AddrExpr = readExpr();
expect(":");
expect("AT");
Expr LMAExpr = readParenExpr();
expect("{");
std::vector<BaseCommand *> V;
OutputSection *Prev = nullptr;
while (!errorCount() && !consume("}")) {
// VA is the same for all sections. The LMAs are consecutive in memory
// starting from the base load address specified.
OutputSection *OS = readOverlaySectionDescription();
OS->AddrExpr = AddrExpr;
if (Prev)
OS->LMAExpr = [=] { return Prev->getLMA() + Prev->Size; };
else
OS->LMAExpr = LMAExpr;
V.push_back(OS);
Prev = OS;
}
// According to the specification, at the end of the overlay, the location
// counter should be equal to the overlay base address plus size of the
// largest section seen in the overlay.
// Here we want to create the Dot assignment command to achieve that.
Expr MoveDot = [=] {
uint64_t Max = 0;
for (BaseCommand *Cmd : V)
Max = std::max(Max, cast<OutputSection>(Cmd)->Size);
return AddrExpr().getValue() + Max;
};
V.push_back(make<SymbolAssignment>(".", MoveDot, getCurrentLocation()));
return V;
}
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("}")) {
StringRef Tok = next();
if (Tok == "OVERLAY") {
for (BaseCommand *Cmd : readOverlay())
V.push_back(Cmd);
continue;
} else if (Tok == "INCLUDE") {
readInclude();
continue;
}
if (BaseCommand *Cmd = readAssignment(Tok))
V.push_back(Cmd);
else
V.push_back(readOutputSectionDescription(Tok));
}
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());
return;
}
Script->SectionCommands.insert(Script->SectionCommands.end(), V.begin(),
V.end());
}
void ScriptParser::readTarget() {
// TARGET(foo) is an alias for "--format foo". Unlike GNU linkers,
// we accept only a limited set of BFD names (i.e. "elf" or "binary")
// for --format. We recognize only /^elf/ and "binary" in the linker
// script as well.
expect("(");
StringRef Tok = next();
expect(")");
if (Tok.startswith("elf"))
Config->FormatBinary = false;
else if (Tok == "binary")
Config->FormatBinary = true;
else
setError("unknown target: " + Tok);
}
static int precedence(StringRef Op) {
return StringSwitch<int>(Op)
.Cases("*", "/", "%", 8)
.Cases("+", "-", 7)
.Cases("<<", ">>", 6)
.Cases("<", "<=", ">", ">=", "==", "!=", 5)
.Case("&", 4)
.Case("|", 3)
.Case("&&", 2)
.Case("||", 1)
.Default(-1);
}
StringMatcher ScriptParser::readFilePatterns() {
std::vector<StringRef> V;
while (!errorCount() && !consume(")"))
V.push_back(next());
return StringMatcher(V);
}
SortSectionPolicy ScriptParser::readSortKind() {
if (consume("SORT") || consume("SORT_BY_NAME"))
return SortSectionPolicy::Name;
if (consume("SORT_BY_ALIGNMENT"))
return SortSectionPolicy::Alignment;
if (consume("SORT_BY_INIT_PRIORITY"))
return SortSectionPolicy::Priority;
if (consume("SORT_NONE"))
return SortSectionPolicy::None;
return SortSectionPolicy::Default;
}
2017-04-06 03:20:54 +08:00
// Reads SECTIONS command contents in the following form:
//
// <contents> ::= <elem>*
// <elem> ::= <exclude>? <glob-pattern>
// <exclude> ::= "EXCLUDE_FILE" "(" <glob-pattern>+ ")"
//
// For example,
//
// *(.foo EXCLUDE_FILE (a.o) .bar EXCLUDE_FILE (b.o) .baz)
//
// is parsed as ".foo", ".bar" with "a.o", and ".baz" with "b.o".
// The semantics of that is section .foo in any file, section .bar in
// any file but a.o, and section .baz in any file but b.o.
std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
std::vector<SectionPattern> Ret;
while (!errorCount() && peek() != ")") {
StringMatcher ExcludeFilePat;
if (consume("EXCLUDE_FILE")) {
expect("(");
ExcludeFilePat = readFilePatterns();
}
std::vector<StringRef> V;
while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE")
V.push_back(next());
if (!V.empty())
Ret.push_back({std::move(ExcludeFilePat), StringMatcher(V)});
else
setError("section pattern is expected");
}
return Ret;
}
// Reads contents of "SECTIONS" directive. That directive contains a
// list of glob patterns for input sections. The grammar is as follows.
//
// <patterns> ::= <section-list>
// | <sort> "(" <section-list> ")"
// | <sort> "(" <sort> "(" <section-list> ")" ")"
//
// <sort> ::= "SORT" | "SORT_BY_NAME" | "SORT_BY_ALIGNMENT"
// | "SORT_BY_INIT_PRIORITY" | "SORT_NONE"
//
// <section-list> is parsed by readInputSectionsList().
InputSectionDescription *
ScriptParser::readInputSectionRules(StringRef FilePattern) {
auto *Cmd = make<InputSectionDescription>(FilePattern);
expect("(");
while (!errorCount() && !consume(")")) {
SortSectionPolicy Outer = readSortKind();
SortSectionPolicy Inner = SortSectionPolicy::Default;
std::vector<SectionPattern> V;
if (Outer != SortSectionPolicy::Default) {
expect("(");
Inner = readSortKind();
if (Inner != SortSectionPolicy::Default) {
expect("(");
V = readInputSectionsList();
expect(")");
} else {
V = readInputSectionsList();
}
expect(")");
} else {
V = readInputSectionsList();
}
for (SectionPattern &Pat : V) {
Pat.SortInner = Inner;
Pat.SortOuter = Outer;
}
std::move(V.begin(), V.end(), std::back_inserter(Cmd->SectionPatterns));
}
return Cmd;
}
InputSectionDescription *
ScriptParser::readInputSectionDescription(StringRef Tok) {
// Input section wildcard can be surrounded by KEEP.
// https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep
if (Tok == "KEEP") {
expect("(");
StringRef FilePattern = next();
InputSectionDescription *Cmd = readInputSectionRules(FilePattern);
expect(")");
Script->KeptSections.push_back(Cmd);
return Cmd;
}
return readInputSectionRules(Tok);
}
void ScriptParser::readSort() {
expect("(");
expect("CONSTRUCTORS");
expect(")");
}
Expr ScriptParser::readAssert() {
expect("(");
Expr E = readExpr();
expect(",");
StringRef Msg = unquote(next());
expect(")");
return [=] {
if (!E().getValue())
error(Msg);
return Script->getDot();
};
}
// Reads a FILL(expr) command. We handle the FILL command as an
// alias for =fillexp section attribute, which is different from
// what GNU linkers do.
// https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
std::array<uint8_t, 4> ScriptParser::readFill() {
expect("(");
std::array<uint8_t, 4> V = parseFill(next());
expect(")");
return V;
}
// Tries to read the special directive for an output section definition which
// can be one of following: "(NOLOAD)", "(COPY)", "(INFO)" or "(OVERLAY)".
// Tok1 and Tok2 are next 2 tokens peeked. See comment for readSectionAddressType below.
bool ScriptParser::readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2) {
if (Tok1 != "(")
return false;
if (Tok2 != "NOLOAD" && Tok2 != "COPY" && Tok2 != "INFO" && Tok2 != "OVERLAY")
return false;
expect("(");
if (consume("NOLOAD")) {
Cmd->Noload = true;
} else {
skip(); // This is "COPY", "INFO" or "OVERLAY".
Cmd->NonAlloc = true;
}
expect(")");
return true;
}
// Reads an expression and/or the special directive for an output
// section definition. Directive is one of following: "(NOLOAD)",
// "(COPY)", "(INFO)" or "(OVERLAY)".
//
// An output section name can be followed by an address expression
// and/or directive. This grammar is not LL(1) because "(" can be
// interpreted as either the beginning of some expression or beginning
// of directive.
//
// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html
void ScriptParser::readSectionAddressType(OutputSection *Cmd) {
if (readSectionDirective(Cmd, peek(), peek2()))
return;
Cmd->AddrExpr = readExpr();
if (peek() == "(" && !readSectionDirective(Cmd, "(", peek2()))
setError("unknown section directive: " + peek2());
}
static Expr checkAlignment(Expr E, std::string &Loc) {
return [=] {
uint64_t Alignment = std::max((uint64_t)1, E().getValue());
if (!isPowerOf2_64(Alignment)) {
error(Loc + ": alignment must be power of 2");
return (uint64_t)1; // Return a dummy value.
}
return Alignment;
};
}
OutputSection *ScriptParser::readOverlaySectionDescription() {
OutputSection *Cmd =
Script->createOutputSection(next(), getCurrentLocation());
Cmd->InOverlay = true;
expect("{");
while (!errorCount() && !consume("}"))
Cmd->SectionCommands.push_back(readInputSectionRules(next()));
Cmd->Phdrs = readOutputSectionPhdrs();
return Cmd;
}
OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
OutputSection *Cmd =
Script->createOutputSection(OutSec, getCurrentLocation());
size_t SymbolsReferenced = Script->ReferencedSymbols.size();
if (peek() != ":")
readSectionAddressType(Cmd);
expect(":");
std::string Location = getCurrentLocation();
if (consume("AT"))
Cmd->LMAExpr = readParenExpr();
if (consume("ALIGN"))
Cmd->AlignExpr = checkAlignment(readParenExpr(), Location);
if (consume("SUBALIGN"))
Cmd->SubalignExpr = checkAlignment(readParenExpr(), Location);
// Parse constraints.
if (consume("ONLY_IF_RO"))
Cmd->Constraint = ConstraintKind::ReadOnly;
if (consume("ONLY_IF_RW"))
Cmd->Constraint = ConstraintKind::ReadWrite;
expect("{");
while (!errorCount() && !consume("}")) {
StringRef Tok = next();
if (Tok == ";") {
// Empty commands are allowed. Do nothing here.
} else if (SymbolAssignment *Assign = readAssignment(Tok)) {
Cmd->SectionCommands.push_back(Assign);
} else if (ByteCommand *Data = readByteCommand(Tok)) {
Cmd->SectionCommands.push_back(Data);
} else if (Tok == "CONSTRUCTORS") {
// CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors
// by name. This is for very old file formats such as ECOFF/XCOFF.
// For ELF, we should ignore.
} else if (Tok == "FILL") {
Cmd->Filler = readFill();
} else if (Tok == "SORT") {
readSort();
} else if (Tok == "INCLUDE") {
readInclude();
} else if (peek() == "(") {
Cmd->SectionCommands.push_back(readInputSectionDescription(Tok));
} else {
setError("unknown command " + Tok);
}
}
if (consume(">"))
Cmd->MemoryRegionName = next();
if (consume("AT")) {
expect(">");
Cmd->LMARegionName = next();
}
if (Cmd->LMAExpr && !Cmd->LMARegionName.empty())
error("section can't have both LMA and a load region");
Cmd->Phdrs = readOutputSectionPhdrs();
if (consume("="))
Cmd->Filler = parseFill(next());
else if (peek().startswith("="))
Cmd->Filler = parseFill(next().drop_front());
// Consume optional comma following output section command.
consume(",");
if (Script->ReferencedSymbols.size() > SymbolsReferenced)
Cmd->ExpressionsUseSymbols = true;
return Cmd;
}
// Parses a given string as a octal/decimal/hexadecimal number and
// returns it as a big-endian number. Used for `=<fillexp>`.
// https://sourceware.org/binutils/docs/ld/Output-Section-Fill.html
//
// When reading a hexstring, ld.bfd handles it as a blob of arbitrary
// size, while ld.gold always handles it as a 32-bit big-endian number.
// We are compatible with ld.gold because it's easier to implement.
std::array<uint8_t, 4> ScriptParser::parseFill(StringRef Tok) {
uint32_t V = 0;
if (!to_integer(Tok, V))
setError("invalid filler expression: " + Tok);
std::array<uint8_t, 4> Buf;
write32be(Buf.data(), V);
return Buf;
}
SymbolAssignment *ScriptParser::readProvideHidden(bool Provide, bool Hidden) {
expect("(");
SymbolAssignment *Cmd = readSymbolAssignment(next());
Cmd->Provide = Provide;
Cmd->Hidden = Hidden;
expect(")");
return Cmd;
}
SymbolAssignment *ScriptParser::readAssignment(StringRef Tok) {
// Assert expression returns Dot, so this is equal to ".=."
if (Tok == "ASSERT")
return make<SymbolAssignment>(".", readAssert(), getCurrentLocation());
size_t OldPos = Pos;
SymbolAssignment *Cmd = nullptr;
if (peek() == "=" || peek() == "+=")
Cmd = readSymbolAssignment(Tok);
else if (Tok == "PROVIDE")
Cmd = readProvideHidden(true, false);
else if (Tok == "HIDDEN")
Cmd = readProvideHidden(false, true);
else if (Tok == "PROVIDE_HIDDEN")
Cmd = readProvideHidden(true, true);
if (Cmd) {
Cmd->CommandString =
Tok.str() + " " +
llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
expect(";");
}
return Cmd;
}
SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) {
StringRef Op = next();
assert(Op == "=" || Op == "+=");
Expr E = readExpr();
if (Op == "+=") {
std::string Loc = getCurrentLocation();
E = [=] { return add(Script->getSymbolValue(Name, Loc), E()); };
}
return make<SymbolAssignment>(Name, E, getCurrentLocation());
}
// This is an operator-precedence parser to parse a linker
// script expression.
Expr ScriptParser::readExpr() {
// Our lexer is context-aware. Set the in-expression bit so that
// they apply different tokenization rules.
bool Orig = InExpr;
InExpr = true;
Expr E = readExpr1(readPrimary(), 0);
InExpr = Orig;
return E;
}
Expr ScriptParser::combine(StringRef Op, Expr L, Expr R) {
if (Op == "+")
return [=] { return add(L(), R()); };
if (Op == "-")
return [=] { return sub(L(), R()); };
if (Op == "*")
return [=] { return L().getValue() * R().getValue(); };
if (Op == "/") {
std::string Loc = getCurrentLocation();
return [=]() -> uint64_t {
if (uint64_t RV = R().getValue())
return L().getValue() / RV;
error(Loc + ": division by zero");
return 0;
};
}
if (Op == "%") {
std::string Loc = getCurrentLocation();
return [=]() -> uint64_t {
if (uint64_t RV = R().getValue())
return L().getValue() % RV;
error(Loc + ": modulo by zero");
return 0;
};
}
if (Op == "<<")
return [=] { return L().getValue() << R().getValue(); };
if (Op == ">>")
return [=] { return L().getValue() >> R().getValue(); };
if (Op == "<")
return [=] { return L().getValue() < R().getValue(); };
if (Op == ">")
return [=] { return L().getValue() > R().getValue(); };
if (Op == ">=")
return [=] { return L().getValue() >= R().getValue(); };
if (Op == "<=")
return [=] { return L().getValue() <= R().getValue(); };
if (Op == "==")
return [=] { return L().getValue() == R().getValue(); };
if (Op == "!=")
return [=] { return L().getValue() != R().getValue(); };
if (Op == "||")
return [=] { return L().getValue() || R().getValue(); };
if (Op == "&&")
return [=] { return L().getValue() && R().getValue(); };
if (Op == "&")
return [=] { return bitAnd(L(), R()); };
if (Op == "|")
return [=] { return bitOr(L(), R()); };
llvm_unreachable("invalid operator");
}
// This is a part of the operator-precedence parser. This function
// assumes that the remaining token stream starts with an operator.
Expr ScriptParser::readExpr1(Expr Lhs, int MinPrec) {
while (!atEOF() && !errorCount()) {
// Read an operator and an expression.
if (consume("?"))
return readTernary(Lhs);
StringRef Op1 = peek();
if (precedence(Op1) < MinPrec)
break;
skip();
Expr Rhs = readPrimary();
// Evaluate the remaining part of the expression first if the
// next operator has greater precedence than the previous one.
// For example, if we have read "+" and "3", and if the next
// operator is "*", then we'll evaluate 3 * ... part first.
while (!atEOF()) {
StringRef Op2 = peek();
if (precedence(Op2) <= precedence(Op1))
break;
Rhs = readExpr1(Rhs, precedence(Op2));
}
Lhs = combine(Op1, Lhs, Rhs);
}
return Lhs;
}
Expr ScriptParser::getPageSize() {
std::string Location = getCurrentLocation();
return [=]() -> uint64_t {
if (Target)
return Target->PageSize;
error(Location + ": unable to calculate page size");
return 4096; // Return a dummy value.
};
}
Expr ScriptParser::readConstant() {
StringRef S = readParenLiteral();
if (S == "COMMONPAGESIZE")
return getPageSize();
if (S == "MAXPAGESIZE")
return [] { return Config->MaxPageSize; };
setError("unknown constant: " + S);
return [] { return 0; };
}
// Parses Tok as an integer. It recognizes hexadecimal (prefixed with
// "0x" or suffixed with "H") and decimal numbers. Decimal numbers may
// have "K" (Ki) or "M" (Mi) suffixes.
static Optional<uint64_t> parseInt(StringRef Tok) {
// Hexadecimal
uint64_t Val;
if (Tok.startswith_lower("0x")) {
if (!to_integer(Tok.substr(2), Val, 16))
return None;
return Val;
}
if (Tok.endswith_lower("H")) {
if (!to_integer(Tok.drop_back(), Val, 16))
return None;
return Val;
}
// Decimal
if (Tok.endswith_lower("K")) {
if (!to_integer(Tok.drop_back(), Val, 10))
return None;
return Val * 1024;
}
if (Tok.endswith_lower("M")) {
if (!to_integer(Tok.drop_back(), Val, 10))
return None;
return Val * 1024 * 1024;
}
if (!to_integer(Tok, Val, 10))
return None;
return Val;
}
ByteCommand *ScriptParser::readByteCommand(StringRef Tok) {
int Size = StringSwitch<int>(Tok)
.Case("BYTE", 1)
.Case("SHORT", 2)
.Case("LONG", 4)
.Case("QUAD", 8)
.Default(-1);
if (Size == -1)
return nullptr;
size_t OldPos = Pos;
Expr E = readParenExpr();
std::string CommandString =
Tok.str() + " " +
llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
return make<ByteCommand>(E, Size, CommandString);
}
StringRef ScriptParser::readParenLiteral() {
expect("(");
bool Orig = InExpr;
InExpr = false;
StringRef Tok = next();
InExpr = Orig;
expect(")");
return Tok;
}
static void checkIfExists(OutputSection *Cmd, StringRef Location) {
if (Cmd->Location.empty() && Script->ErrorOnMissingSection)
error(Location + ": undefined section " + Cmd->Name);
}
Expr ScriptParser::readPrimary() {
if (peek() == "(")
return readParenExpr();
if (consume("~")) {
Expr E = readPrimary();
return [=] { return ~E().getValue(); };
}
if (consume("!")) {
Expr E = readPrimary();
return [=] { return !E().getValue(); };
}
if (consume("-")) {
Expr E = readPrimary();
return [=] { return -E().getValue(); };
}
StringRef Tok = next();
std::string Location = getCurrentLocation();
// Built-in functions are parsed here.
// https://sourceware.org/binutils/docs/ld/Builtin-Functions.html.
if (Tok == "ABSOLUTE") {
Expr Inner = readParenExpr();
return [=] {
ExprValue I = Inner();
I.ForceAbsolute = true;
return I;
};
}
if (Tok == "ADDR") {
StringRef Name = readParenLiteral();
OutputSection *Sec = Script->getOrCreateOutputSection(Name);
return [=]() -> ExprValue {
checkIfExists(Sec, Location);
return {Sec, false, 0, Location};
};
}
if (Tok == "ALIGN") {
expect("(");
Expr E = readExpr();
if (consume(")")) {
E = checkAlignment(E, Location);
return [=] { return alignTo(Script->getDot(), E().getValue()); };
}
expect(",");
Expr E2 = checkAlignment(readExpr(), Location);
expect(")");
return [=] {
ExprValue V = E();
V.Alignment = E2().getValue();
return V;
};
}
if (Tok == "ALIGNOF") {
StringRef Name = readParenLiteral();
OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
return [=] {
checkIfExists(Cmd, Location);
return Cmd->Alignment;
};
}
if (Tok == "ASSERT")
return readAssert();
if (Tok == "CONSTANT")
return readConstant();
if (Tok == "DATA_SEGMENT_ALIGN") {
expect("(");
Expr E = readExpr();
expect(",");
readExpr();
expect(")");
return [=] {
return alignTo(Script->getDot(), std::max((uint64_t)1, E().getValue()));
};
}
if (Tok == "DATA_SEGMENT_END") {
expect("(");
expect(".");
expect(")");
return [] { return Script->getDot(); };
}
if (Tok == "DATA_SEGMENT_RELRO_END") {
// GNU linkers implements more complicated logic to handle
// DATA_SEGMENT_RELRO_END. We instead ignore the arguments and
// just align to the next page boundary for simplicity.
expect("(");
readExpr();
expect(",");
readExpr();
expect(")");
Expr E = getPageSize();
return [=] { return alignTo(Script->getDot(), E().getValue()); };
}
if (Tok == "DEFINED") {
StringRef Name = readParenLiteral();
return [=] { return Symtab->find(Name) ? 1 : 0; };
}
if (Tok == "LENGTH") {
StringRef Name = readParenLiteral();
if (Script->MemoryRegions.count(Name) == 0) {
setError("memory region not defined: " + Name);
return [] { return 0; };
}
return [=] { return Script->MemoryRegions[Name]->Length; };
}
if (Tok == "LOADADDR") {
StringRef Name = readParenLiteral();
OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
return [=] {
checkIfExists(Cmd, Location);
return Cmd->getLMA();
};
}
if (Tok == "MAX" || Tok == "MIN") {
expect("(");
Expr A = readExpr();
expect(",");
Expr B = readExpr();
expect(")");
if (Tok == "MIN")
return [=] { return std::min(A().getValue(), B().getValue()); };
return [=] { return std::max(A().getValue(), B().getValue()); };
}
if (Tok == "ORIGIN") {
StringRef Name = readParenLiteral();
if (Script->MemoryRegions.count(Name) == 0) {
setError("memory region not defined: " + Name);
return [] { return 0; };
}
return [=] { return Script->MemoryRegions[Name]->Origin; };
}
if (Tok == "SEGMENT_START") {
expect("(");
skip();
expect(",");
Expr E = readExpr();
expect(")");
return [=] { return E(); };
}
if (Tok == "SIZEOF") {
StringRef Name = readParenLiteral();
OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
// Linker script does not create an output section if its content is empty.
// We want to allow SIZEOF(.foo) where .foo is a section which happened to
// be empty.
return [=] { return Cmd->Size; };
}
if (Tok == "SIZEOF_HEADERS")
return [=] { return elf::getHeaderSize(); };
// Tok is the dot.
if (Tok == ".")
return [=] { return Script->getSymbolValue(Tok, Location); };
// Tok is a literal number.
if (Optional<uint64_t> Val = parseInt(Tok))
return [=] { return *Val; };
// Tok is a symbol name.
if (!isValidCIdentifier(Tok))
setError("malformed number: " + Tok);
Script->ReferencedSymbols.push_back(Tok);
return [=] { return Script->getSymbolValue(Tok, Location); };
}
Expr ScriptParser::readTernary(Expr Cond) {
Expr L = readExpr();
expect(":");
Expr R = readExpr();
return [=] { return Cond().getValue() ? L() : R(); };
}
Expr ScriptParser::readParenExpr() {
expect("(");
Expr E = readExpr();
expect(")");
return E;
}
std::vector<StringRef> ScriptParser::readOutputSectionPhdrs() {
std::vector<StringRef> Phdrs;
while (!errorCount() && peek().startswith(":")) {
StringRef Tok = next();
Phdrs.push_back((Tok.size() == 1) ? next() : Tok.substr(1));
}
return Phdrs;
}
// Read a program header type name. The next token must be a
// name of a program header type or a constant (e.g. "0x3").
unsigned ScriptParser::readPhdrType() {
StringRef Tok = next();
if (Optional<uint64_t> Val = parseInt(Tok))
return *Val;
unsigned Ret = StringSwitch<unsigned>(Tok)
.Case("PT_NULL", PT_NULL)
.Case("PT_LOAD", PT_LOAD)
.Case("PT_DYNAMIC", PT_DYNAMIC)
.Case("PT_INTERP", PT_INTERP)
.Case("PT_NOTE", PT_NOTE)
.Case("PT_SHLIB", PT_SHLIB)
.Case("PT_PHDR", PT_PHDR)
.Case("PT_TLS", PT_TLS)
.Case("PT_GNU_EH_FRAME", PT_GNU_EH_FRAME)
.Case("PT_GNU_STACK", PT_GNU_STACK)
.Case("PT_GNU_RELRO", PT_GNU_RELRO)
.Case("PT_OPENBSD_RANDOMIZE", PT_OPENBSD_RANDOMIZE)
.Case("PT_OPENBSD_WXNEEDED", PT_OPENBSD_WXNEEDED)
.Case("PT_OPENBSD_BOOTDATA", PT_OPENBSD_BOOTDATA)
.Default(-1);
if (Ret == (unsigned)-1) {
setError("invalid program header type: " + Tok);
return PT_NULL;
}
return Ret;
}
// Reads an anonymous version declaration.
void ScriptParser::readAnonymousDeclaration() {
std::vector<SymbolVersion> Locals;
std::vector<SymbolVersion> Globals;
std::tie(Locals, Globals) = readSymbols();
for (SymbolVersion V : Locals) {
if (V.Name == "*")
Config->DefaultSymbolVersion = VER_NDX_LOCAL;
else
Config->VersionScriptLocals.push_back(V);
}
for (SymbolVersion V : Globals)
Config->VersionScriptGlobals.push_back(V);
expect(";");
}
// Reads a non-anonymous version definition,
// e.g. "VerStr { global: foo; bar; local: *; };".
void ScriptParser::readVersionDeclaration(StringRef VerStr) {
// Read a symbol list.
std::vector<SymbolVersion> Locals;
std::vector<SymbolVersion> Globals;
std::tie(Locals, Globals) = readSymbols();
for (SymbolVersion V : Locals) {
if (V.Name == "*")
Config->DefaultSymbolVersion = VER_NDX_LOCAL;
else
Config->VersionScriptLocals.push_back(V);
}
// Create a new version definition and add that to the global symbols.
VersionDefinition Ver;
Ver.Name = VerStr;
Ver.Globals = Globals;
// User-defined version number starts from 2 because 0 and 1 are
// reserved for VER_NDX_LOCAL and VER_NDX_GLOBAL, respectively.
Ver.Id = Config->VersionDefinitions.size() + 2;
Config->VersionDefinitions.push_back(Ver);
// Each version may have a parent version. For example, "Ver2"
// defined as "Ver2 { global: foo; local: *; } Ver1;" has "Ver1"
// as a parent. This version hierarchy is, probably against your
// instinct, purely for hint; the runtime doesn't care about it
// at all. In LLD, we simply ignore it.
if (peek() != ";")
skip();
expect(";");
}
static bool hasWildcard(StringRef S) {
return S.find_first_of("?*[") != StringRef::npos;
}
// Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };".
std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
ScriptParser::readSymbols() {
std::vector<SymbolVersion> Locals;
std::vector<SymbolVersion> Globals;
std::vector<SymbolVersion> *V = &Globals;
while (!errorCount()) {
if (consume("}"))
break;
if (consumeLabel("local")) {
V = &Locals;
continue;
}
if (consumeLabel("global")) {
V = &Globals;
continue;
}
if (consume("extern")) {
std::vector<SymbolVersion> Ext = readVersionExtern();
V->insert(V->end(), Ext.begin(), Ext.end());
} else {
StringRef Tok = next();
V->push_back({unquote(Tok), false, hasWildcard(Tok)});
}
expect(";");
}
return {Locals, Globals};
}
// Reads an "extern C++" directive, e.g.,
// "extern "C++" { ns::*; "f(int, double)"; };"
//
// The last semicolon is optional. E.g. this is OK:
// "extern "C++" { ns::*; "f(int, double)" };"
std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
StringRef Tok = next();
bool IsCXX = Tok == "\"C++\"";
if (!IsCXX && Tok != "\"C\"")
setError("Unknown language");
expect("{");
std::vector<SymbolVersion> Ret;
while (!errorCount() && peek() != "}") {
StringRef Tok = next();
bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok);
Ret.push_back({unquote(Tok), IsCXX, HasWildcard});
if (consume("}"))
return Ret;
expect(";");
}
expect("}");
return Ret;
}
uint64_t ScriptParser::readMemoryAssignment(StringRef S1, StringRef S2,
StringRef S3) {
if (!consume(S1) && !consume(S2) && !consume(S3)) {
setError("expected one of: " + S1 + ", " + S2 + ", or " + S3);
return 0;
}
expect("=");
return readExpr()().getValue();
}
// Parse the MEMORY command as specified in:
// https://sourceware.org/binutils/docs/ld/MEMORY.html
//
// MEMORY { name [(attr)] : ORIGIN = origin, LENGTH = len ... }
void ScriptParser::readMemory() {
expect("{");
while (!errorCount() && !consume("}")) {
StringRef Tok = next();
if (Tok == "INCLUDE") {
readInclude();
continue;
}
uint32_t Flags = 0;
uint32_t NegFlags = 0;
if (consume("(")) {
std::tie(Flags, NegFlags) = readMemoryAttributes();
expect(")");
}
expect(":");
uint64_t Origin = readMemoryAssignment("ORIGIN", "org", "o");
expect(",");
uint64_t Length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
MemoryRegion *MR = make<MemoryRegion>(Tok, Origin, Length, Flags, NegFlags);
if (!Script->MemoryRegions.insert({Tok, MR}).second)
setError("region '" + Tok + "' already defined");
}
}
// This function parses the attributes used to match against section
// flags when placing output sections in a memory region. These flags
// are only used when an explicit memory region name is not used.
std::pair<uint32_t, uint32_t> ScriptParser::readMemoryAttributes() {
uint32_t Flags = 0;
uint32_t NegFlags = 0;
bool Invert = false;
for (char C : next().lower()) {
uint32_t Flag = 0;
if (C == '!')
Invert = !Invert;
else if (C == 'w')
Flag = SHF_WRITE;
else if (C == 'x')
Flag = SHF_EXECINSTR;
else if (C == 'a')
Flag = SHF_ALLOC;
else if (C != 'r')
setError("invalid memory region attribute");
if (Invert)
NegFlags |= Flag;
else
Flags |= Flag;
}
return {Flags, NegFlags};
}
void elf::readLinkerScript(MemoryBufferRef MB) {
ScriptParser(MB).readLinkerScript();
}
void elf::readVersionScript(MemoryBufferRef MB) {
ScriptParser(MB).readVersionScript();
}
void elf::readDynamicList(MemoryBufferRef MB) {
ScriptParser(MB).readDynamicList();
}
void elf::readDefsym(StringRef Name, MemoryBufferRef MB) {
ScriptParser(MB).readDefsym(Name);
}