Index: lld/ELF/LinkerScript.h =================================================================== --- lld/ELF/LinkerScript.h +++ lld/ELF/LinkerScript.h @@ -132,16 +132,32 @@ // MEMORY command. struct MemoryRegion { MemoryRegion(StringRef name, Expr origin, Expr length, uint32_t flags, - uint32_t negFlags) + uint32_t invFlags, uint32_t negFlags, uint32_t negInvFlags) : name(std::string(name)), origin(origin), length(length), flags(flags), - negFlags(negFlags) {} + invFlags(invFlags), negFlags(negFlags), negInvFlags(negInvFlags) {} std::string name; Expr origin; Expr length; + // A section can be assigned to the region if any of these ELF section flags + // are set... uint32_t flags; + // ... or any of these flags are not set. + // For example, the memory region attribute "r" maps to ~SHF_WRITE. + uint32_t invFlags; + // A section cannot be assigned to the region if any of these ELF section + // flags are set... uint32_t negFlags; + // ... or any of these flags are not set. + // For example, the memory region attribute "!r" maps to !(~SHF_WRITE). + uint32_t negInvFlags; uint64_t curPos = 0; + + bool compatibleWith(uint32_t secFlags) const { + if ((secFlags & negFlags) || (~secFlags & negInvFlags)) + return false; + return (secFlags & flags) || (~secFlags & invFlags); + } }; // This struct represents one section match pattern in SECTIONS() command. Index: lld/ELF/LinkerScript.cpp =================================================================== --- lld/ELF/LinkerScript.cpp +++ lld/ELF/LinkerScript.cpp @@ -917,7 +917,7 @@ // See if a region can be found by matching section flags. for (auto &pair : memoryRegions) { MemoryRegion *m = pair.second; - if ((m->flags & sec->flags) && (m->negFlags & sec->flags) == 0) + if (m->compatibleWith(sec->flags)) return {m, nullptr}; } Index: lld/ELF/ScriptParser.cpp =================================================================== --- lld/ELF/ScriptParser.cpp +++ lld/ELF/ScriptParser.cpp @@ -113,7 +113,8 @@ Expr getPageSize(); Expr readMemoryAssignment(StringRef, StringRef, StringRef); - std::pair readMemoryAttributes(); + void readMemoryAttributes(uint32_t &flags, uint32_t &invFlags, + uint32_t &negFlags, uint32_t &negInvFlags); Expr combine(StringRef op, Expr l, Expr r); Expr readExpr(); @@ -1614,9 +1615,11 @@ } uint32_t flags = 0; + uint32_t invFlags = 0; uint32_t negFlags = 0; + uint32_t negInvFlags = 0; if (consume("(")) { - std::tie(flags, negFlags) = readMemoryAttributes(); + readMemoryAttributes(flags, invFlags, negFlags, negInvFlags); expect(")"); } expect(":"); @@ -1626,7 +1629,8 @@ Expr length = readMemoryAssignment("LENGTH", "len", "l"); // Add the memory region to the region map. - MemoryRegion *mr = make(tok, origin, length, flags, negFlags); + MemoryRegion *mr = make(tok, origin, length, flags, invFlags, + negFlags, negInvFlags); if (!script->memoryRegions.insert({tok, mr}).second) setError("region '" + tok + "' already defined"); } @@ -1635,30 +1639,34 @@ // 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 ScriptParser::readMemoryAttributes() { - uint32_t flags = 0; - uint32_t negFlags = 0; +void ScriptParser::readMemoryAttributes(uint32_t &flags, uint32_t &invFlags, + uint32_t &negFlags, + uint32_t &negInvFlags) { bool invert = false; for (char c : next().lower()) { - uint32_t flag = 0; - if (c == '!') + if (c == '!') { invert = !invert; - else if (c == 'w') - flag = SHF_WRITE; + std::swap(flags, negFlags); + std::swap(invFlags, negInvFlags); + continue; + } + if (c == 'w') + flags |= SHF_WRITE; else if (c == 'x') - flag = SHF_EXECINSTR; + flags |= SHF_EXECINSTR; else if (c == 'a') - flag = SHF_ALLOC; - else if (c != 'r') + flags |= SHF_ALLOC; + else if (c == 'r') + invFlags |= SHF_WRITE; + else setError("invalid memory region attribute"); + } - if (invert) - negFlags |= flag; - else - flags |= flag; + if (invert) { + std::swap(flags, negFlags); + std::swap(invFlags, negInvFlags); } - return {flags, negFlags}; } void elf::readLinkerScript(MemoryBufferRef mb) { Index: lld/test/ELF/linkerscript/memory-readonly.test =================================================================== --- /dev/null +++ lld/test/ELF/linkerscript/memory-readonly.test @@ -0,0 +1,38 @@ +REQUIRES: x86 + +RUN: split-file %s %ts +RUN: llvm-mc -filetype=obj -triple=x86_64 %ts/s -o %t.o + +## Check that memory region attribute "r" is supported. + +RUN: ld.lld -o %t -T %ts/t %t.o +RUN: llvm-readelf -S %t | FileCheck %s + +CHECK: Name Type Address Off Size +CHECK: .text PROGBITS 0000000000009000 {{[0-9a-f]+}} 000004 +CHECK: .rodata PROGBITS 0000000000009004 {{[0-9a-f]+}} 000008 +CHECK: .data PROGBITS 0000000000008000 {{[0-9a-f]+}} 000010 + +#--- s + .text + .zero 4 + + .rodata + .zero 8 + + .data + .zero 0x10 + +#--- t +MEMORY +{ + RAM (a!r) : ORIGIN = 0x8000, LENGTH = 0x1000 + ROM (rx) : ORIGIN = 0x9000, LENGTH = 0x1000 +} + +SECTIONS +{ + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } +}