summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/LinkerScript.cpp141
-rw-r--r--lld/ELF/LinkerScript.h21
-rw-r--r--lld/test/ELF/linkerscript/memory.s140
3 files changed, 301 insertions, 1 deletions
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index ca2614deb14..81b9aee9ca2 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -409,6 +409,19 @@ template <class ELFT> void LinkerScript<ELFT>::output(InputSection<ELFT> *S) {
// .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) }
CurOutSec->Size = Pos - CurOutSec->Addr;
+ // If there is a memory region associated with this input section, then
+ // place the section in that region and update the region index.
+ if (CurMemRegion) {
+ CurMemRegion->Offset += CurOutSec->Size;
+ uint64_t CurSize = CurMemRegion->Offset - CurMemRegion->Origin;
+ if (CurSize > CurMemRegion->Length) {
+ uint64_t OverflowAmt = CurSize - CurMemRegion->Length;
+ error("section '" + CurOutSec->Name + "' will not fit in region '" +
+ CurMemRegion->Name + "': overflowed by " + Twine(OverflowAmt) +
+ " bytes");
+ }
+ }
+
if (IsTbss)
ThreadBssOffset = Pos - Dot;
else
@@ -508,6 +521,42 @@ findSections(StringRef Name, const std::vector<OutputSectionBase *> &Sections) {
return Ret;
}
+// This function searches for a memory region to place the given output
+// section in. If found, a pointer to the appropriate memory region is
+// returned. Otherwise, a nullptr is returned.
+template <class ELFT>
+MemoryRegion *LinkerScript<ELFT>::findMemoryRegion(OutputSectionCommand *Cmd,
+ OutputSectionBase *Sec) {
+ // If a memory region name was specified in the output section command,
+ // then try to find that region first.
+ if (!Cmd->MemoryRegionName.empty()) {
+ auto It = Opt.MemoryRegions.find(Cmd->MemoryRegionName);
+ if (It != Opt.MemoryRegions.end())
+ return &It->second;
+ error("memory region '" + Cmd->MemoryRegionName + "' not declared");
+ return nullptr;
+ }
+
+ // The memory region name is empty, thus a suitable region must be
+ // searched for in the region map. If the region map is empty, just
+ // return. Note that this check doesn't happen at the very beginning
+ // so that uses of undeclared regions can be caught.
+ if (!Opt.MemoryRegions.size())
+ return nullptr;
+
+ // See if a region can be found by matching section flags.
+ for (auto &MRI : Opt.MemoryRegions) {
+ MemoryRegion &MR = MRI.second;
+ if ((MR.Flags & Sec->Flags) != 0 && (MR.NotFlags & Sec->Flags) == 0)
+ return &MR;
+ }
+
+ // Otherwise, no suitable region was found.
+ if (Sec->Flags & SHF_ALLOC)
+ error("no memory region specified for section '" + Sec->Name + "'");
+ return nullptr;
+}
+
// This function assigns offsets to input sections and an output section
// for a single sections command (e.g. ".text { *(.text); }").
template <class ELFT>
@@ -518,7 +567,13 @@ void LinkerScript<ELFT>::assignOffsets(OutputSectionCommand *Cmd) {
findSections<ELFT>(Cmd->Name, *OutputSections);
if (Sections.empty())
return;
- switchTo(Sections[0]);
+
+ OutputSectionBase *Sec = Sections[0];
+ // Try and find an appropriate memory region to assign offsets in.
+ CurMemRegion = findMemoryRegion(Cmd, Sec);
+ if (CurMemRegion)
+ Dot = CurMemRegion->Offset;
+ switchTo(Sec);
// Find the last section output location. We will output orphan sections
// there so that end symbols point to the correct location.
@@ -961,6 +1016,8 @@ private:
void readExtern();
void readGroup();
void readInclude();
+ void readMemory();
+ std::pair<uint32_t, uint32_t> readMemoryAttributes();
void readOutput();
void readOutputArch();
void readOutputFormat();
@@ -1058,6 +1115,8 @@ void ScriptParser::readLinkerScript() {
readGroup();
} else if (Tok == "INCLUDE") {
readInclude();
+ } else if (Tok == "MEMORY") {
+ readMemory();
} else if (Tok == "OUTPUT") {
readOutput();
} else if (Tok == "OUTPUT_ARCH") {
@@ -1453,6 +1512,10 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) {
setError("unknown command " + Tok);
}
}
+
+ if (consume(">"))
+ Cmd->MemoryRegionName = next();
+
Cmd->Phdrs = readOutputSectionPhdrs();
if (consume("="))
@@ -1937,6 +2000,82 @@ std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
return Ret;
}
+// Parse the MEMORY command As specified in:
+// https://sourceware.org/binutils/docs/ld/MEMORY.html#MEMORY
+void ScriptParser::readMemory() {
+ expect("{");
+ while (!Error && !consume("}")) {
+ StringRef Name = next();
+ uint32_t Flags = 0;
+ uint32_t NotFlags = 0;
+ if (consume("(")) {
+ std::tie(Flags, NotFlags) = readMemoryAttributes();
+ expect(")");
+ }
+ expect(":");
+
+ // Parse the ORIGIN.
+ if (!(consume("ORIGIN") || consume("org") || consume("o"))) {
+ setError("expected one of: ORIGIN, org, or o");
+ return;
+ }
+ expect("=");
+ uint64_t Origin;
+ // TODO: Fully support constant expressions.
+ if (!readInteger(next(), Origin)) {
+ setError("nonconstant expression for origin");
+ return;
+ }
+ expect(",");
+
+ // Parse the LENGTH.
+ if (!(consume("LENGTH") || consume("len") || consume("l"))) {
+ setError("expected one of: LENGTH, len, or l");
+ return;
+ }
+ expect("=");
+ uint64_t Length;
+ // TODO: Fully support constant expressions.
+ if (!readInteger(next(), Length)) {
+ setError("nonconstant expression for length");
+ return;
+ }
+ // Add the memory region to the region map (if it doesn't already exist).
+ auto It = Opt.MemoryRegions.find(Name);
+ if (It != Opt.MemoryRegions.end())
+ setError("region '" + Name + "' already defined");
+ else
+ Opt.MemoryRegions[Name] = {Name, Origin, Length, Origin, Flags, NotFlags};
+ }
+}
+
+// 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 NotFlags = 0;
+ bool Invert = false;
+ for (char C : next()) {
+ uint32_t Flag = 0;
+ if (C == '!')
+ Invert = !Invert;
+ else if (tolower(C) == 'w')
+ Flag = SHF_WRITE;
+ else if (tolower(C) == 'x')
+ Flag = SHF_EXECINSTR;
+ else if (tolower(C) == 'a')
+ Flag = SHF_ALLOC;
+ else if (tolower(C) != 'r')
+ setError("invalid memory region attribute");
+ if (Invert)
+ NotFlags |= Flag;
+ else
+ Flags |= Flag;
+ }
+ return {Flags, NotFlags};
+}
+
void elf::readLinkerScript(MemoryBufferRef MB) {
ScriptParser(MB).readLinkerScript();
}
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 505162f0ab4..8dd60405433 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -15,6 +15,7 @@
#include "Writer.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -127,6 +128,7 @@ struct OutputSectionCommand : BaseCommand {
uint32_t Filler = 0;
ConstraintKind Constraint = ConstraintKind::NoConstraint;
std::string Location;
+ std::string MemoryRegionName;
};
// This struct represents one section match pattern in SECTIONS() command.
@@ -187,6 +189,18 @@ struct PhdrsCommand {
Expr LMAExpr;
};
+// This struct is used to represent the location and size of regions of
+// target memory. Instances of the struct are created by parsing the
+// MEMORY command.
+struct MemoryRegion {
+ std::string Name;
+ uint64_t Origin;
+ uint64_t Length;
+ uint64_t Offset;
+ uint32_t Flags;
+ uint32_t NotFlags;
+};
+
class LinkerScriptBase {
protected:
~LinkerScriptBase() = default;
@@ -215,6 +229,9 @@ struct ScriptConfiguration {
// List of section patterns specified with KEEP commands. They will
// be kept even if they are unused and --gc-sections is specified.
std::vector<InputSectionDescription *> KeptSections;
+
+ // A map from memory region name to a memory region descriptor.
+ llvm::DenseMap<llvm::StringRef, MemoryRegion> MemoryRegions;
};
extern ScriptConfiguration *ScriptConfig;
@@ -273,9 +290,13 @@ private:
std::vector<size_t> getPhdrIndices(StringRef SectionName);
size_t getPhdrIndex(const Twine &Loc, StringRef PhdrName);
+ MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd,
+ OutputSectionBase *Sec);
+
uintX_t Dot;
uintX_t LMAOffset = 0;
OutputSectionBase *CurOutSec = nullptr;
+ MemoryRegion *CurMemRegion = nullptr;
uintX_t ThreadBssOffset = 0;
void switchTo(OutputSectionBase *Sec);
void flush();
diff --git a/lld/test/ELF/linkerscript/memory.s b/lld/test/ELF/linkerscript/memory.s
new file mode 100644
index 00000000000..c21c4b107f3
--- /dev/null
+++ b/lld/test/ELF/linkerscript/memory.s
@@ -0,0 +1,140 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+## Check simple RAM-only memory region.
+
+# RUN: echo "MEMORY { \
+# RUN: ram (rwx) : ORIGIN = 0x8000, LENGTH = 256K \
+# RUN: } \
+# RUN: SECTIONS { \
+# RUN: .text : { \
+# RUN: *(.text) \
+# RUN: } > ram \
+# RUN: .data : { \
+# RUN: *(.data) \
+# RUN: } > ram \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=RAM %s
+
+# RAM: 1 .text 00000001 0000000000008000 TEXT DATA
+# RAM-NEXT: 2 .data 00001000 0000000000008001 DATA
+
+## Check RAM and ROM memory regions.
+
+# RUN: echo "MEMORY { \
+# RUN: ram (rwx) : ORIGIN = 0x0, LENGTH = 1024M \
+# RUN: rom (rx) : org = 0x80000000, len = 64M \
+# RUN: } \
+# RUN: SECTIONS { \
+# RUN: .text : { \
+# RUN: *(.text) \
+# RUN: } > rom \
+# RUN: .data : { \
+# RUN: *(.data) \
+# RUN: } > ram \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=RAMROM %s
+
+# RAMROM: 1 .text 00000001 0000000080000000 TEXT DATA
+# RAMROM-NEXT: 2 .data 00001000 0000000000000000 DATA
+
+## Check memory region placement by attributes.
+
+# RUN: echo "MEMORY { \
+# RUN: ram (!rx) : ORIGIN = 0x0, LENGTH = 1024M \
+# RUN: rom (rx) : o = 0x80000000, l = 64M \
+# RUN: } \
+# RUN: SECTIONS { \
+# RUN: .text : { \
+# RUN: *(.text) \
+# RUN: } \
+# RUN: .data : { \
+# RUN: *(.data) \
+# RUN: } > ram \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=ATTRS %s
+
+# ATTRS: 1 .text 00000001 0000000080000000 TEXT DATA
+# ATTRS: 2 .data 00001000 0000000000000000 DATA
+
+## Check bad `ORIGIN`.
+
+# RUN: echo "MEMORY { ram (rwx) : ORIGI = 0x8000, LENGTH = 256K } }" > %t.script
+# RUN: not ld.lld -o %t2 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR1 %s
+# ERR1: {{.*}}.script:1: expected one of: ORIGIN, org, or o
+
+## Check bad `LENGTH`.
+
+# RUN: echo "MEMORY { ram (rwx) : ORIGIN = 0x8000, LENTH = 256K } }" > %t.script
+# RUN: not ld.lld -o %t2 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR2 %s
+# ERR2: {{.*}}.script:1: expected one of: LENGTH, len, or l
+
+## Check duplicate regions.
+
+# RUN: echo "MEMORY { ram (rwx) : o = 0x8, l = 256K ram (rx) : o = 0x0, l = 256K }" > %t.script
+# RUN: not ld.lld -o %t2 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR3 %s
+# ERR3: {{.*}}.script:1: region 'ram' already defined
+
+## Check no region available.
+
+# RUN: echo "MEMORY { \
+# RUN: ram (!rx) : ORIGIN = 0x8000, LENGTH = 256K \
+# RUN: } \
+# RUN: SECTIONS { \
+# RUN: .text : { \
+# RUN: *(.text) \
+# RUN: } \
+# RUN: .data : { \
+# RUN: *(.data) \
+# RUN: } > ram \
+# RUN: }" > %t.script
+# RUN: not ld.lld -o %t2 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR4 %s
+# ERR4: {{.*}}: no memory region specified for section '.text'
+
+## Check undeclared region.
+
+# RUN: echo "SECTIONS { .text : { *(.text) } > ram }" > %t.script
+# RUN: not ld.lld -o %t2 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR5 %s
+# ERR5: {{.*}}: memory region 'ram' not declared
+
+## Check region overflow.
+
+# RUN: echo "MEMORY { \
+# RUN: ram (rwx) : ORIGIN = 0x0, LENGTH = 2K \
+# RUN: } \
+# RUN: SECTIONS { \
+# RUN: .text : { \
+# RUN: *(.text) \
+# RUN: } > ram \
+# RUN: .data : { \
+# RUN: *(.data) \
+# RUN: } > ram \
+# RUN: }" > %t.script
+# RUN: not ld.lld -o %t2 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR6 %s
+# ERR6: {{.*}}: section '.data' will not fit in region 'ram': overflowed by 2049 bytes
+
+## Check invalid region attributes.
+
+# RUN: echo "MEMORY { ram (abc) : ORIGIN = 0x8000, LENGTH = 256K } }" > %t.script
+# RUN: not ld.lld -o %t2 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR7 %s
+# ERR7: {{.*}}.script:1: invalid memory region attribute
+
+.text
+.global _start
+_start:
+ nop
+
+.data
+b:
+ .long 1
+ .zero 4092
OpenPOWER on IntegriCloud