[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
This commit is contained in:
Igor Kudrin 2021-11-24 12:17:03 +07:00
parent 55792b5ac4
commit 8cdf1c1edb
4 changed files with 121 additions and 22 deletions

View File

@ -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};
}

View File

@ -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.

View File

@ -113,7 +113,8 @@ private:
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;
else if (c == 'x')
flag = SHF_EXECINSTR;
else if (c == 'a')
flag = SHF_ALLOC;
else if (c != 'r')
setError("invalid memory region attribute");
if (invert)
negFlags |= flag;
else
flags |= flag;
std::swap(flags, negFlags);
std::swap(invFlags, negInvFlags);
continue;
}
if (c == 'w')
flags |= SHF_WRITE;
else if (c == 'x')
flags |= SHF_EXECINSTR;
else if (c == 'a')
flags |= SHF_ALLOC;
else if (c == 'r')
invFlags |= SHF_WRITE;
else
setError("invalid memory region attribute");
}
if (invert) {
std::swap(flags, negFlags);
std::swap(invFlags, negInvFlags);
}
return {flags, negFlags};
}
void elf::readLinkerScript(MemoryBufferRef mb) {

View File

@ -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) }
}