[lld] r292875 - [LinkerScript] Implement `MEMORY` command

Meador Inge via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 23 18:34:01 PST 2017


Author: meadori
Date: Mon Jan 23 20:34:00 2017
New Revision: 292875

URL: http://llvm.org/viewvc/llvm-project?rev=292875&view=rev
Log:
[LinkerScript] Implement `MEMORY` command

As specified here:

* https://sourceware.org/binutils/docs/ld/MEMORY.html#MEMORY

There are two deviations from what is specified for GNU ld:

  1. Only integer constants and *not* constant expressions
     are allowed in `LENGTH` and `ORIGIN` initializations.

  2. The `I` and `L` attributes are *not* implemented.

With (1) there is currently no easy way to evaluate integer
only constant expressions.  This can be enhanced in the
future.

With (2) it isn't clear how these flags map to the `SHF_*`
flags or if they even make sense for an ELF linker.

Differential Revision: https://reviews.llvm.org/D28911

Added:
    lld/trunk/test/ELF/linkerscript/memory.s
Modified:
    lld/trunk/ELF/LinkerScript.cpp
    lld/trunk/ELF/LinkerScript.h

Modified: lld/trunk/ELF/LinkerScript.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/LinkerScript.cpp?rev=292875&r1=292874&r2=292875&view=diff
==============================================================================
--- lld/trunk/ELF/LinkerScript.cpp (original)
+++ lld/trunk/ELF/LinkerScript.cpp Mon Jan 23 20:34:00 2017
@@ -409,6 +409,19 @@ template <class ELFT> void LinkerScript<
   // .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::
   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(O
       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::readOutputSectionDescripti
       setError("unknown command " + Tok);
     }
   }
+
+  if (consume(">"))
+    Cmd->MemoryRegionName = next();
+
   Cmd->Phdrs = readOutputSectionPhdrs();
 
   if (consume("="))
@@ -1937,6 +2000,82 @@ std::vector<SymbolVersion> ScriptParser:
   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();
 }

Modified: lld/trunk/ELF/LinkerScript.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/LinkerScript.h?rev=292875&r1=292874&r2=292875&view=diff
==============================================================================
--- lld/trunk/ELF/LinkerScript.h (original)
+++ lld/trunk/ELF/LinkerScript.h Mon Jan 23 20:34:00 2017
@@ -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 : BaseComman
   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();

Added: lld/trunk/test/ELF/linkerscript/memory.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/linkerscript/memory.s?rev=292875&view=auto
==============================================================================
--- lld/trunk/test/ELF/linkerscript/memory.s (added)
+++ lld/trunk/test/ELF/linkerscript/memory.s Mon Jan 23 20:34:00 2017
@@ -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




More information about the llvm-commits mailing list