Set max segment protection level.
authorPete Cooper <peter_cooper@apple.com>
Sat, 6 Feb 2016 00:51:16 +0000 (00:51 +0000)
committerPete Cooper <peter_cooper@apple.com>
Sat, 6 Feb 2016 00:51:16 +0000 (00:51 +0000)
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
lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
lld/test/mach-o/seg-protection-arm64.yaml [new file with mode: 0644]
lld/test/mach-o/seg-protection-x86_64.yaml [new file with mode: 0644]

index 6dab8ba..e01d72c 100644 (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
index 8a11a5c..b57afc0 100644 (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)
index 684c535..73f4e14 100644 (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);
   }
 }
index c795a72..5ae9204 100644 (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);
   }
 };
 
diff --git a/lld/test/mach-o/seg-protection-arm64.yaml b/lld/test/mach-o/seg-protection-arm64.yaml
new file mode 100644 (file)
index 0000000..f63b33a
--- /dev/null
@@ -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 (file)
index 0000000..474f72f
--- /dev/null
@@ -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)