[lld] 8cdf1c1 - [ELF] Support the "read-only" memory region attribute
Igor Kudrin via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 23 21:18:20 PST 2021
Author: Igor Kudrin
Date: 2021-11-24T12:17:09+07:00
New Revision: 8cdf1c1edb937b192d162f299127fad8d9dc0faa
URL: https://github.com/llvm/llvm-project/commit/8cdf1c1edb937b192d162f299127fad8d9dc0faa
DIFF: https://github.com/llvm/llvm-project/commit/8cdf1c1edb937b192d162f299127fad8d9dc0faa.diff
LOG: [ELF] Support the "read-only" memory region attribute
The attribute 'r' allows (or disallows for the negative case) read-only
sections, i.e. ones without the SHF_WRITE flag, to be assigned to the
memory region. Before the patch, lld could put a section in the wrong
region or fail with "error: no memory region specified for section".
Differential Revision: https://reviews.llvm.org/D113771
Added:
lld/test/ELF/linkerscript/memory-attr.test
Modified:
lld/ELF/LinkerScript.cpp
lld/ELF/LinkerScript.h
lld/ELF/ScriptParser.cpp
Removed:
################################################################################
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 40127baf40d6b..5e09e5dde39d1 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -977,7 +977,7 @@ LinkerScript::findMemoryRegion(OutputSection *sec, MemoryRegion *hint) {
// 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/LinkerScript.h b/lld/ELF/LinkerScript.h
index b366da4f274ee..7faa49a794f9a 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -132,16 +132,32 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
// 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/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index ad3b3e61ad594..cc18a59da2369 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -113,7 +113,8 @@ class ScriptParser final : ScriptLexer {
Expr getPageSize();
Expr readMemoryAssignment(StringRef, StringRef, StringRef);
- std::pair<uint32_t, uint32_t> 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 @@ void ScriptParser::readMemory() {
}
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 @@ void ScriptParser::readMemory() {
Expr length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
- MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, negFlags);
+ MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, invFlags,
+ negFlags, negInvFlags);
if (!script->memoryRegions.insert({tok, mr}).second)
setError("region '" + tok + "' already defined");
}
@@ -1635,30 +1639,34 @@ void ScriptParser::readMemory() {
// 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 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
index 0000000000000..5ea6e77ee2540
--- /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) }
+}
More information about the llvm-commits
mailing list