diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/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. diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -977,7 +977,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}; } diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp --- a/lld/ELF/ScriptParser.cpp +++ b/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) { diff --git a/lld/test/ELF/linkerscript/memory-attr.test b/lld/test/ELF/linkerscript/memory-attr.test new file mode 100644 --- /dev/null +++ b/lld/test/ELF/linkerscript/memory-attr.test @@ -0,0 +1,75 @@ +REQUIRES: x86 + +RUN: split-file %s %ts +RUN: llvm-mc -filetype=obj -triple=x86_64 %ts/s -o %t.o + +## Check assigning sections to memory regions by attributes. + +#--- s + .text + .zero 8 + + .rodata + .zero 8 + + .data + .zero 8 + +#--- t1 +## Check memory region attribute 'a'. + +# RUN: ld.lld -o %t1 -T %ts/t1 %t.o +# RUN: llvm-readelf -S %t1 | FileCheck %s --check-prefix=TEST1 + +# TEST1: Name Type Address +# TEST1: .text PROGBITS 0000000000002000 +# TEST1-NEXT: .rodata PROGBITS 0000000000002008 +# TEST1-NEXT: .data PROGBITS 0000000000002010 + +MEMORY +{ + ## All sections have SHF_ALLOC attribute, so no one can be added here. + NOMEM (rwx!a) : org = 0x1000, l = 1K + ## All sections are assigned to this memory region. + MEM (a) : org = 0x2000, l = 1K +} + +SECTIONS +{ + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } +} + +#--- t2 +## Check that memory region attributes 'r', 'w', and 'x' are supported both in +## positive and negative forms. + +# RUN: ld.lld -o %t2 -T %ts/t2 %t.o +# RUN: llvm-readelf -S %t2 | FileCheck %s --check-prefix=TEST2 + +# TEST2: Name Type Address +# TEST2: .text PROGBITS 0000000000004000 +# TEST2-NEXT: .rodata PROGBITS 0000000000003000 +# TEST2-NEXT: .data PROGBITS 0000000000002000 + +MEMORY +{ + ## While all sections are allocatable, '.text' and '.rodata' are read-only and + ## '.data' is writable, so no sections should be assigned to this region. + NOMEM (a!rw) : org = 0x1000, l = 1K + ## Only writable section '.data' is allowed here. + RAM (w) : org = 0x2000, l = 1K + ## Executable sections are not allowed here, so only '.rodata' should be + ## assigned to the region. + ROM (r!x) : org = 0x3000, l = 1K + ## An executable section '.text' comes here. + EXEC (x) : org = 0x4000, l = 1K +} + +SECTIONS +{ + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } +}