From b8fec3ea626863a71e217c0ea75a74f8451a651c Mon Sep 17 00:00:00 2001 From: Pete Cooper Date: Sat, 6 Feb 2016 00:51:16 +0000 Subject: [PATCH] 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 --- lld/lib/ReaderWriter/MachO/MachONormalizedFile.h | 3 +- .../MachO/MachONormalizedFileBinaryWriter.cpp | 12 ++-- .../MachO/MachONormalizedFileFromAtoms.cpp | 43 ++++++++++-- .../ReaderWriter/MachO/MachONormalizedFileYAML.cpp | 9 +-- lld/test/mach-o/seg-protection-arm64.yaml | 78 ++++++++++++++++++++++ lld/test/mach-o/seg-protection-x86_64.yaml | 78 ++++++++++++++++++++++ 6 files changed, 205 insertions(+), 18 deletions(-) create mode 100644 lld/test/mach-o/seg-protection-arm64.yaml create mode 100644 lld/test/mach-o/seg-protection-x86_64.yaml diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h b/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h index 6dab8ba..e01d72c 100644 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h +++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFile.h @@ -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 diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp index 8a11a5c..b57afc0 100644 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp +++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp @@ -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) diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp index 684c535..73f4e14 100644 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp +++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp @@ -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 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); } } diff --git a/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp index c795a72..5ae9204 100644 --- a/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp +++ b/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp @@ -504,10 +504,11 @@ struct ScalarTraits { template <> struct MappingTraits { 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); } }; diff --git a/lld/test/mach-o/seg-protection-arm64.yaml b/lld/test/mach-o/seg-protection-arm64.yaml new file mode 100644 index 0000000..f63b33a --- /dev/null +++ b/lld/test/mach-o/seg-protection-arm64.yaml @@ -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) diff --git a/lld/test/mach-o/seg-protection-x86_64.yaml b/lld/test/mach-o/seg-protection-x86_64.yaml new file mode 100644 index 0000000..474f72f --- /dev/null +++ b/lld/test/mach-o/seg-protection-x86_64.yaml @@ -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) -- 2.7.4