forked from OSchip/llvm-project
[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:
parent
55792b5ac4
commit
8cdf1c1edb
|
@ -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};
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) }
|
||||
}
|
Loading…
Reference in New Issue