[lld] [LLD][ELF] Support 'i' and 'l' memory region attributes (PR #110937)
Daniel Thornburgh via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 2 16:10:38 PDT 2024
https://github.com/mysterymath created https://github.com/llvm/llvm-project/pull/110937
This patch adds support for 'i' and 'l' memory region attributes for initialized sections. This decreases friction when porting linker scripts from GNU LD.
>From a3088fceeef5fe777a478bd51be37f74c8e57d55 Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Tue, 1 Oct 2024 16:20:28 -0700
Subject: [PATCH] [LLD][ELF] Support 'i' and 'l' memory region attributes
This patch adds support for 'i' and 'l' memory region attributes for
initialized sections. This decreases friction when porting linker
scripts from GNU LD.
---
lld/ELF/LinkerScript.cpp | 11 +++++-
lld/ELF/LinkerScript.h | 45 ++++++++++++----------
lld/ELF/ScriptParser.cpp | 43 +++++++++------------
lld/test/ELF/linkerscript/memory-attr.test | 26 ++++++++-----
4 files changed, 70 insertions(+), 55 deletions(-)
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
More information about the llvm-commits
mailing list