Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ 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 @@ 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,17 @@ Expr LMAExpr; }; +struct MemoryRegion { + MemoryRegion() : Name(""), Origin(0), Length(0), Index(0) {} + MemoryRegion(StringRef N, uint64_t O, uint64_t L) + : Name(N), Origin(O), Length(L), Index(O) {} + + std::string Name; + uint64_t Origin; + uint64_t Length; + uint64_t Index; +}; + class LinkerScriptBase { protected: ~LinkerScriptBase() = default; @@ -215,6 +228,8 @@ // List of section patterns specified with KEEP commands. They will // be kept even if they are unused and --gc-sections is specified. std::vector KeptSections; + + llvm::DenseMap MemoryRegions; }; extern ScriptConfiguration *ScriptConfig; @@ -276,6 +291,7 @@ uintX_t Dot; uintX_t LMAOffset = 0; OutputSectionBase *CurOutSec = nullptr; + MemoryRegion *CurMemRegion = nullptr; uintX_t ThreadBssOffset = 0; void switchTo(OutputSectionBase *Sec); void flush(); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -409,6 +409,18 @@ // .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) } CurOutSec->Size = Pos - CurOutSec->Addr; + if (CurMemRegion) { + CurMemRegion->Index += CurOutSec->Size; + uint64_t CurSize = CurMemRegion->Index - CurMemRegion->Origin; + if (CurSize > CurMemRegion->Length) { + uint64_t OverflowAmt = CurSize - CurMemRegion->Length; + error("a.out section `" + CurOutSec->Name + "' will not fit in region `" + + CurMemRegion->Name + "'"); + error("region `" + CurMemRegion->Name + "' overflowed by " + + Twine(OverflowAmt) + " bytes"); + } + } + if (IsTbss) ThreadBssOffset = Pos - Dot; else @@ -720,9 +732,19 @@ } auto *Cmd = cast(Base.get()); - if (Cmd->AddrExpr) + if (Cmd->AddrExpr) { Dot = Cmd->AddrExpr(Dot); + } else if (!Cmd->MemoryRegionName.empty()) { + auto It = Opt.MemoryRegions.find(Cmd->MemoryRegionName); + if (It != Opt.MemoryRegions.end()) { + CurMemRegion = &It->second; + Dot = CurMemRegion->Index; + } else { + warn("memory region `" + Cmd->MemoryRegionName + "' not declared"); + } + } assignOffsets(Cmd); + CurMemRegion = nullptr; } uintX_t MinVA = std::numeric_limits::max(); @@ -976,6 +998,7 @@ void readExtern(); void readGroup(); void readInclude(); + void readMemory(); void readOutput(); void readOutputArch(); void readOutputFormat(); @@ -1073,6 +1096,8 @@ readGroup(); } else if (Tok == "INCLUDE") { readInclude(); + } else if (Tok == "MEMORY") { + readMemory(); } else if (Tok == "OUTPUT") { readOutput(); } else if (Tok == "OUTPUT_ARCH") { @@ -1464,6 +1489,10 @@ setError("unknown command " + Tok); } } + + if (consume(">")) + Cmd->MemoryRegionName = next(); + Cmd->Phdrs = readOutputSectionPhdrs(); if (consume("=")) @@ -1948,6 +1977,45 @@ return Ret; } +void ScriptParser::readMemory() { + // As specified in: + // https://sourceware.org/binutils/docs/ld/MEMORY.html#MEMORY + + expect("{"); + while (!Error && !consume("}")) { + StringRef Name = next(); + if (consume("(")) { + StringRef AttrStr = next(); + expect(")"); + } + expect(":"); + + // `ORIGIN = `. + if (!(consume("ORIGIN") || consume("org") || consume("o"))) + setError("expected one of: ORIGIN, org, or o"); + expect("="); + uint64_t Origin; + // TODO: Fully support constant expressions. + StringRef Tok = next(); + if (!readInteger(Tok, Origin)) + setError("nonconstant expression for origin"); + + expect(","); + + // `LENGTH = `. + if (!(consume("LENGTH") || consume("len") || consume("l"))) + setError("expected one of: LENGTH, len, or l"); + expect("="); + uint64_t Length; + Tok = next(); + // TODO: Fully support constant expressions. + if (!readInteger(Tok, Length)) + setError("nonconstant expression for length"); + + Opt.MemoryRegions[Name] = MemoryRegion(Name, Origin, Length); + } +} + void elf::readLinkerScript(MemoryBufferRef MB) { ScriptParser(MB).readLinkerScript(); }