Set max segment protection level.

The initial segment protection was also being used to set the maximum
segment protection level.  Instead, the maximum should be set according
to the architecture we are linking.  For example on Mac OS it should be
RWX on most pages, but on iOS is often on R_X.

rdar://problem/24515136

llvm-svn: 259966
This commit is contained in:
Pete Cooper 2016-02-06 00:51:16 +00:00
parent dc1414b3f9
commit b8fec3ea62
6 changed files with 205 additions and 18 deletions

View File

@ -172,7 +172,8 @@ struct Segment {
StringRef name;
Hex64 address;
Hex64 size;
VMProtect access;
VMProtect init_access;
VMProtect max_access;
};
/// Only used in normalized final linked images to specify on which dylibs

View File

@ -570,7 +570,7 @@ void MachOFileLayout::buildFileOffsets() {
llvm::dbgs() << "buildFileOffsets()\n");
for (const Segment &sg : _file.segments) {
_segInfo[&sg].fileOffset = fileOffset;
if ((_seg1addr == INT64_MAX) && sg.access)
if ((_seg1addr == INT64_MAX) && sg.init_access)
_seg1addr = sg.address;
DEBUG_WITH_TYPE("MachOFileLayout",
llvm::dbgs() << " segment=" << sg.name
@ -578,7 +578,7 @@ void MachOFileLayout::buildFileOffsets() {
uint32_t segFileSize = 0;
// A segment that is not zero-fill must use a least one page of disk space.
if (sg.access)
if (sg.init_access)
segFileSize = _file.pageSize;
for (const Section *s : _segInfo[&sg].sections) {
uint32_t sectOffset = s->address - sg.address;
@ -711,8 +711,8 @@ std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) {
cmd->vmsize = llvm::alignTo(linkeditSize, _file.pageSize);
cmd->fileoff = _startOfLinkEdit;
cmd->filesize = linkeditSize;
cmd->initprot = seg.access;
cmd->maxprot = seg.access;
cmd->initprot = seg.init_access;
cmd->maxprot = seg.max_access;
cmd->nsects = 0;
cmd->flags = 0;
if (_swap)
@ -731,8 +731,8 @@ std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) {
cmd->vmsize = seg.size;
cmd->fileoff = segInfo.fileOffset;
cmd->filesize = segInfo.fileSize;
cmd->maxprot = seg.access;
cmd->initprot = seg.access;
cmd->initprot = seg.init_access;
cmd->maxprot = seg.max_access;
cmd->nsects = segInfo.sections.size();
cmd->flags = 0;
if (_swap)

View File

@ -92,13 +92,15 @@ struct SegmentInfo {
StringRef name;
uint64_t address;
uint64_t size;
uint32_t access;
uint32_t init_access;
uint32_t max_access;
std::vector<SectionInfo*> sections;
uint32_t normalizedSegmentIndex;
};
SegmentInfo::SegmentInfo(StringRef n)
: name(n), address(0), size(0), access(0), normalizedSegmentIndex(0) {
: name(n), address(0), size(0), init_access(0), max_access(0),
normalizedSegmentIndex(0) {
}
class Util {
@ -432,12 +434,38 @@ SegmentInfo *Util::segmentForName(StringRef segName) {
return si;
}
auto *info = new (_allocator) SegmentInfo(segName);
// Set the initial segment protection.
if (segName.equals("__TEXT"))
info->access = VM_PROT_READ | VM_PROT_EXECUTE;
else if (segName.equals("__DATA"))
info->access = VM_PROT_READ | VM_PROT_WRITE;
info->init_access = VM_PROT_READ | VM_PROT_EXECUTE;
else if (segName.equals("__PAGEZERO"))
info->access = 0;
info->init_access = 0;
else if (segName.equals("__LINKEDIT"))
info->init_access = VM_PROT_READ;
else {
// All others default to read-write
info->init_access = VM_PROT_READ | VM_PROT_WRITE;
}
// Set max segment protection
// Note, its overkill to use a switch statement here, but makes it so much
// easier to use switch coverage to catch new cases.
switch (_ctx.os()) {
case lld::MachOLinkingContext::OS::unknown:
case lld::MachOLinkingContext::OS::macOSX:
case lld::MachOLinkingContext::OS::iOS_simulator:
if (segName.equals("__PAGEZERO")) {
info->max_access = 0;
break;
}
// All others default to all
info->max_access = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
break;
case lld::MachOLinkingContext::OS::iOS:
// iPhoneOS always uses same protection for max and initial
info->max_access = info->init_access;
break;
}
_segmentInfos.push_back(info);
return info;
}
@ -589,7 +617,8 @@ void Util::copySegmentInfo(NormalizedFile &file) {
seg.name = sgi->name;
seg.address = sgi->address;
seg.size = sgi->size;
seg.access = sgi->access;
seg.init_access = sgi->init_access;
seg.max_access = sgi->max_access;
file.segments.push_back(seg);
}
}

View File

@ -504,10 +504,11 @@ struct ScalarTraits<VMProtect> {
template <>
struct MappingTraits<Segment> {
static void mapping(IO &io, Segment& seg) {
io.mapRequired("name", seg.name);
io.mapRequired("address", seg.address);
io.mapRequired("size", seg.size);
io.mapRequired("access", seg.access);
io.mapRequired("name", seg.name);
io.mapRequired("address", seg.address);
io.mapRequired("size", seg.size);
io.mapRequired("init-access", seg.init_access);
io.mapRequired("max-access", seg.max_access);
}
};

View File

@ -0,0 +1,78 @@
# RUN: lld -flavor darwin -arch arm64 %s %p/Inputs/hello-world-arm64.yaml -o %t && llvm-objdump -private-headers %t | FileCheck %s
--- !mach-o
arch: arm64
file-type: MH_OBJECT
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
has-UUID: false
OS: unknown
sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
address: 0x0000000000000000
content: [ 0x00, 0x00 ]
global-symbols:
- name: _main
type: N_SECT
scope: [ N_EXT ]
sect: 1
value: 0x0000000000000000
- name: start
type: N_SECT
scope: [ N_EXT ]
sect: 1
value: 0x0000000000000001
...
# CHECK: Load command 0
# CHECK: cmd LC_SEGMENT_64
# CHECK: cmdsize 72
# CHECK: segname __PAGEZERO
# CHECK: vmaddr
# CHECK: vmsize
# CHECK: fileoff
# CHECK: filesize
# CHECK: maxprot ---
# CHECK: initprot ---
# CHECK: nsects 0
# CHECK: flags (none)
# CHECK: Load command 1
# CHECK: cmd LC_SEGMENT_64
# CHECK: cmdsize 152
# CHECK: segname __TEXT
# CHECK: vmaddr
# CHECK: vmsize
# CHECK: fileoff
# CHECK: filesize
# CHECK: maxprot r-x
# CHECK: initprot r-x
# CHECK: nsects 1
# CHECK: flags (none)
# CHECK: Section
# CHECK: sectname __text
# CHECK: segname __TEXT
# CHECK: addr
# CHECK: size
# CHECK: offset
# CHECK: align 2^0 (1)
# CHECK: reloff 0
# CHECK: nreloc 0
# CHECK: type S_REGULAR
# CHECK: attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
# CHECK: reserved1 0
# CHECK: reserved2 0
# CHECK: Load command 2
# CHECK: cmd LC_SEGMENT_64
# CHECK: cmdsize 72
# CHECK: segname __LINKEDIT
# CHECK: vmaddr
# CHECK: vmsize
# CHECK: fileoff
# CHECK: filesize
# CHECK: maxprot r--
# CHECK: initprot r--
# CHECK: nsects 0
# CHECK: flags (none)

View File

@ -0,0 +1,78 @@
# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/hello-world-x86_64.yaml -o %t && llvm-objdump -private-headers %t | FileCheck %s
--- !mach-o
arch: x86_64
file-type: MH_OBJECT
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
has-UUID: false
OS: unknown
sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
address: 0x0000000000000000
content: [ 0x00, 0x00 ]
global-symbols:
- name: _main
type: N_SECT
scope: [ N_EXT ]
sect: 1
value: 0x0000000000000000
- name: start
type: N_SECT
scope: [ N_EXT ]
sect: 1
value: 0x0000000000000001
...
# CHECK: Load command 0
# CHECK: cmd LC_SEGMENT_64
# CHECK: cmdsize 72
# CHECK: segname __PAGEZERO
# CHECK: vmaddr
# CHECK: vmsize
# CHECK: fileoff
# CHECK: filesize
# CHECK: maxprot ---
# CHECK: initprot ---
# CHECK: nsects 0
# CHECK: flags (none)
# CHECK: Load command 1
# CHECK: cmd LC_SEGMENT_64
# CHECK: cmdsize 152
# CHECK: segname __TEXT
# CHECK: vmaddr
# CHECK: vmsize
# CHECK: fileoff
# CHECK: filesize
# CHECK: maxprot rwx
# CHECK: initprot r-x
# CHECK: nsects 1
# CHECK: flags (none)
# CHECK: Section
# CHECK: sectname __text
# CHECK: segname __TEXT
# CHECK: addr
# CHECK: size
# CHECK: offset
# CHECK: align 2^0 (1)
# CHECK: reloff 0
# CHECK: nreloc 0
# CHECK: type S_REGULAR
# CHECK: attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
# CHECK: reserved1 0
# CHECK: reserved2 0
# CHECK: Load command 2
# CHECK: cmd LC_SEGMENT_64
# CHECK: cmdsize 72
# CHECK: segname __LINKEDIT
# CHECK: vmaddr
# CHECK: vmsize
# CHECK: fileoff
# CHECK: filesize
# CHECK: maxprot rwx
# CHECK: initprot r--
# CHECK: nsects 0
# CHECK: flags (none)