[lld] [LLD][ELF] Support 'i' and 'l' memory region attributes (PR #110937)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 2 16:11:10 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lld-elf
@llvm/pr-subscribers-lld
Author: Daniel Thornburgh (mysterymath)
<details>
<summary>Changes</summary>
This patch adds support for 'i' and 'l' memory region attributes for initialized sections. This decreases friction when porting linker scripts from GNU LD.
---
Full diff: https://github.com/llvm/llvm-project/pull/110937.diff
4 Files Affected:
- (modified) lld/ELF/LinkerScript.cpp (+10-1)
- (modified) lld/ELF/LinkerScript.h (+24-21)
- (modified) lld/ELF/ScriptParser.cpp (+19-24)
- (modified) lld/test/ELF/linkerscript/memory-attr.test (+17-9)
``````````diff
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index cce584ae4d8679..ee58922d4cd77d 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -404,6 +404,15 @@ bool InputSectionDescription::matchesFile(const InputFile *file) const {
return matchesFileCache->second;
}
+bool MemoryRegion::Attrs::matches(OutputSection *sec) const {
+ return (sec->flags & flags) || (~sec->flags & invFlags) ||
+ (initialized && sec->type != SHT_NOBITS);
+}
+
+bool MemoryRegion::compatibleWith(OutputSection *sec) const {
+ return !negAttrs.matches(sec) && attrs.matches(sec);
+}
+
bool SectionPattern::excludesFile(const InputFile *file) const {
if (excludedFilePat.empty())
return false;
@@ -1105,7 +1114,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->compatibleWith(sec->flags))
+ if (m->compatibleWith(sec))
return {m, nullptr};
}
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index db6a8c8e147ac6..e460d4615c1833 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -139,36 +139,39 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
// target memory. Instances of the struct are created by parsing the
// MEMORY command.
struct MemoryRegion {
- MemoryRegion(StringRef name, Expr origin, Expr length, uint32_t flags,
- uint32_t invFlags, uint32_t negFlags, uint32_t negInvFlags)
- : name(std::string(name)), origin(origin), length(length), flags(flags),
- invFlags(invFlags), negFlags(negFlags), negInvFlags(negInvFlags) {}
+ // A collection of attributes that match a set of sections.
+ struct Attrs {
+ // Match if any of these ELF section flags are present.
+ uint32_t flags = 0;
+ // Match if any of these ELF section flags are absent.
+ // For example, the memory region attribute "r" maps to SHF_WRITE.
+ uint32_t invFlags = 0;
+ // Match if the section type isn't SHT_NOBITS.
+ bool initialized = false;
+
+ bool matches(OutputSection *sec) const;
+ };
+
+ MemoryRegion(StringRef name, Expr origin, Expr length, Attrs attrs,
+ Attrs negAttrs)
+ : name(std::string(name)), origin(origin), length(length), attrs(attrs),
+ negAttrs(negAttrs) {}
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;
+
+ // Attributes that cause a section to match the region.
+ Attrs attrs;
+ // Attributes that cause a section not to match the region.
+ Attrs negAttrs;
+
uint64_t curPos = 0;
uint64_t getOrigin() const { return origin().getValue(); }
uint64_t getLength() const { return length().getValue(); }
- bool compatibleWith(uint32_t secFlags) const {
- if ((secFlags & negFlags) || (~secFlags & negInvFlags))
- return false;
- return (secFlags & flags) || (~secFlags & invFlags);
- }
+ bool compatibleWith(OutputSection *sec) const;
};
// This struct represents one section match pattern in SECTIONS() command.
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index b16b2e56473adc..c189558fd165ae 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -107,8 +107,8 @@ class ScriptParser final : ScriptLexer {
Expr getPageSize();
Expr readMemoryAssignment(StringRef, StringRef, StringRef);
- void readMemoryAttributes(uint32_t &flags, uint32_t &invFlags,
- uint32_t &negFlags, uint32_t &negInvFlags);
+ void readMemoryAttributes(MemoryRegion::Attrs &attrs,
+ MemoryRegion::Attrs &negAttrs);
Expr combine(StringRef op, Expr l, Expr r);
Expr readExpr();
@@ -1839,12 +1839,10 @@ void ScriptParser::readMemory() {
continue;
}
- uint32_t flags = 0;
- uint32_t invFlags = 0;
- uint32_t negFlags = 0;
- uint32_t negInvFlags = 0;
+ MemoryRegion::Attrs attrs;
+ MemoryRegion::Attrs negAttrs;
if (consume("(")) {
- readMemoryAttributes(flags, invFlags, negFlags, negInvFlags);
+ readMemoryAttributes(attrs, negAttrs);
expect(")");
}
expect(":");
@@ -1854,44 +1852,41 @@ 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, invFlags,
- negFlags, negInvFlags);
+ MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, attrs, negAttrs);
if (!ctx.script->memoryRegions.insert({tok, mr}).second)
setError("region '" + tok + "' already defined");
}
}
-// This function parses the attributes used to match against section
-// flags when placing output sections in a memory region. These flags
+// This function parses the attributes used to match against sections
+// when placing output sections in a memory region. These attributes
// are only used when an explicit memory region name is not used.
-void ScriptParser::readMemoryAttributes(uint32_t &flags, uint32_t &invFlags,
- uint32_t &negFlags,
- uint32_t &negInvFlags) {
+void ScriptParser::readMemoryAttributes(MemoryRegion::Attrs &attrs,
+ MemoryRegion::Attrs &negAttrs) {
bool invert = false;
for (char c : next().lower()) {
if (c == '!') {
invert = !invert;
- std::swap(flags, negFlags);
- std::swap(invFlags, negInvFlags);
+ std::swap(attrs, negAttrs);
continue;
}
if (c == 'w')
- flags |= SHF_WRITE;
+ attrs.flags |= SHF_WRITE;
else if (c == 'x')
- flags |= SHF_EXECINSTR;
+ attrs.flags |= SHF_EXECINSTR;
else if (c == 'a')
- flags |= SHF_ALLOC;
+ attrs.flags |= SHF_ALLOC;
else if (c == 'r')
- invFlags |= SHF_WRITE;
+ attrs.invFlags |= SHF_WRITE;
+ else if (c == 'i' || c == 'l')
+ attrs.initialized = true;
else
setError("invalid memory region attribute");
}
- if (invert) {
- std::swap(flags, negFlags);
- std::swap(invFlags, negInvFlags);
- }
+ if (invert)
+ std::swap(attrs, negAttrs);
}
void elf::readLinkerScript(Ctx &ctx, MemoryBufferRef mb) {
diff --git a/lld/test/ELF/linkerscript/memory-attr.test b/lld/test/ELF/linkerscript/memory-attr.test
index 5ea6e77ee2540b..89c92c48a39395 100644
--- a/lld/test/ELF/linkerscript/memory-attr.test
+++ b/lld/test/ELF/linkerscript/memory-attr.test
@@ -15,6 +15,9 @@ RUN: llvm-mc -filetype=obj -triple=x86_64 %ts/s -o %t.o
.data
.zero 8
+ .bss
+ .zero 8
+
#--- t1
## Check memory region attribute 'a'.
@@ -25,6 +28,7 @@ RUN: llvm-mc -filetype=obj -triple=x86_64 %ts/s -o %t.o
# TEST1: .text PROGBITS 0000000000002000
# TEST1-NEXT: .rodata PROGBITS 0000000000002008
# TEST1-NEXT: .data PROGBITS 0000000000002010
+# TEST1-NEXT: .bss NOBITS 0000000000002018
MEMORY
{
@@ -42,8 +46,8 @@ SECTIONS
}
#--- t2
-## Check that memory region attributes 'r', 'w', and 'x' are supported both in
-## positive and negative forms.
+## Check that memory region attributes 'r', 'w', 'x', 'i', and 'l' 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
@@ -51,20 +55,24 @@ SECTIONS
# TEST2: Name Type Address
# TEST2: .text PROGBITS 0000000000004000
# TEST2-NEXT: .rodata PROGBITS 0000000000003000
-# TEST2-NEXT: .data PROGBITS 0000000000002000
+# TEST2-NEXT: .data PROGBITS 0000000000005000
+# TEST2-NEXT: .bss NOBITS 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
+ ## '.data' and '.bss' are writable, so no sections should be assigned to this
+ ## region.
+ NOMEM (a!rw) : org = 0x1000, l = 1K
+ ## Only writable section '.bss' is allowed here, but not '.data' (initialized).
+ BSS (w!i) : 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
+ ROM (r!x) : org = 0x3000, l = 1K
## An executable section '.text' comes here.
- EXEC (x) : org = 0x4000, l = 1K
+ EXEC (x) : org = 0x4000, l = 1K
+ ## Only '.data' is allowed here (initialized).
+ DATA (l) : org = 0x5000, l = 1K
}
SECTIONS
``````````
</details>
https://github.com/llvm/llvm-project/pull/110937
More information about the llvm-commits
mailing list