运行在10.6到10.9的OS X应用程序不能在10.10(约塞米蒂)上运行 - 为什么?

时间:2014-10-14 02:41:28

标签: xcode osx-yosemite

一年多来,我一直在使用XCode 4.5使用OS X 10.7 sdk构建应用程序,目标部署为10.6。

该应用程序在10.6,10.7,10.8和10.9上运行良好。但是当我在10.10上运行它时,我在控制台中收到一条消息说"无法强制执行硬页面零"对于应用程序。

谷歌搜索"无法强制执行硬页面零"没有任何帮助。

有谁知道这意味着什么?

一些有用的信息是,使用XCode 5构建应用程序并不会导致问题,所以我认为它与旧的XCode 4.5有关。

如果它是一个简单的解决方案,我宁愿坚持使用旧的XCode,因为如果我不必,我会犹豫是否转移到XCode 5。

非常感谢任何见解。

编辑1:我发现以32位模式打开应用程序可以在Yosemite上运行。所以它只有64位模式才有问题。

编辑2:这是Ken要求的otool的输出:

Load command 0
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __TEXT
   vmaddr 0x00000001007f9000
   vmsize 0x0000000000225dba
  fileoff 0
 filesize 2251422
  maxprot rwx
 initprot rwx
   nsects 0
    flags (none)
Load command 1
        cmd LC_UNIXTHREAD
    cmdsize 184
     flavor x86_THREAD_STATE64
      count x86_THREAD_STATE64_COUNT
   rax  0x0000000000000000 rbx 0x0000000000000000 rcx  0x0000000000000000
   rdx  0x0000000000000000 rdi 0x0000000000000000 rsi  0x0000000000000000
   rbp  0x0000000000000000 rsp 0x0000000000000000 r8   0x0000000000000000
    r9  0x0000000000000000 r10 0x0000000000000000 r11  0x0000000000000000
   r12  0x0000000000000000 r13 0x0000000000000000 r14  0x0000000000000000
   r15  0x0000000000000000 rip 0x0000000100a1e3d8
rflags  0x0000000000000000 cs  0x0000000000000000 fs   0x0000000000000000
    gs  0x0000000000000000

2 个答案:

答案 0 :(得分:9)

来自OS X 10.10的Apple内核(xnu)现在强制实施了一个“硬页面零”'在过去的情况并非如此。请注意,此要求仅适用于64位MachO可执行文件。

通过确保__PAGEZERO细分,您的应用程序将解决该问题。重要的是,此__PAGEZERO段必须具有0x0(NULL)的vmaddr和至少0x1000的vmsize。具体名称' __ PAGEZERO'对于该段实际上并不需要,但大多数编译器将以这种方式使用它。

硬(有限权限)PAGEZERO使得很难利用NULL deference软件漏洞。

xnu源代码中的以下代码片段解释了:

// From xnu-2782.1.97/bsd/kern/mach_loader.c
load_return_t
load_machfile(
    struct image_params *imgp,
    struct mach_header  *header,
    thread_t        thread,
    vm_map_t        new_map,
    load_result_t       *result
)
{
    ...
    boolean_t enforce_hard_pagezero = TRUE;
    ...
    // Second vm_map_create() argument sets map->min_offset to zero.
    map = vm_map_create(pmap,
            0,
            vm_compute_max_offset((imgp->ip_flags & IMGPF_IS_64BIT)),
            TRUE);
    ...
    #if __x86_64__
    /*
     * On x86, for compatibility, don't enforce the hard page-zero restriction for 32-bit binaries.
     */         
    if ((imgp->ip_flags & IMGPF_IS_64BIT) == 0) {
        enforce_hard_pagezero = FALSE;
    }
    #endif
    /*
     * Check to see if the page zero is enforced by the map->min_offset.
     */
    // Note: vm_map_has_hard_pagezero(map, 0x1000) checks if map->min_offset >= 0x1000
    // Refer xnu-2782.1.97/osfmk/vm/vm_map.c
    if (enforce_hard_pagezero && (vm_map_has_hard_pagezero(map, 0x1000) == FALSE)) {
        if (create_map) {
            vm_map_deallocate(map); /* will lose pmap reference too */
        }
        printf("Cannot enforce a hard page-zero for %s\n", imgp->ip_strings);
        return (LOAD_BADMACHO);
    }

在段加载代码中将map-> min_offset提升到零以上:

// From xnu-2782.1.97/bsd/kern/mach_loader.c
static
load_return_t
load_segment(
    struct load_command     *lcp,
    uint32_t            filetype,
    void *              control,
    off_t               pager_offset,
    off_t               macho_size,
    struct vnode            *vp,
    vm_map_t            map,
    int64_t             slide,
    load_result_t       *result
)
{
...
/*
 *  Round sizes to page size.
 */
seg_size = round_page_64(scp->vmsize);
map_size = round_page_64(scp->filesize);
map_addr = trunc_page_64(scp->vmaddr); /* JVXXX note that in XNU TOT this is round instead of trunc for 64 bits */

seg_size = vm_map_round_page(seg_size, vm_map_page_mask(map));
map_size = vm_map_round_page(map_size, vm_map_page_mask(map));

...
// This if test is key, checking for a 0x0 vmaddr, vmsize, and initprot and maxprot
// memory protections. 
// Note a segment name of "__PAGEZERO" is not actually required.
if (map_addr == 0 &&
    map_size == 0 &&
    seg_size != 0 &&
    (scp->initprot & VM_PROT_ALL) == VM_PROT_NONE &&
    (scp->maxprot & VM_PROT_ALL) == VM_PROT_NONE) {
    /*
     * For PIE, extend page zero rather than moving it.  Extending
     * page zero keeps early allocations from falling predictably
     * between the end of page zero and the beginning of the first
     * slid segment.
     */
    seg_size += slide;
    slide = 0;

    /*
     * This is a "page zero" segment:  it starts at address 0,
     * is not mapped from the binary file and is not accessible.
     * User-space should never be able to access that memory, so
     * make it completely off limits by raising the VM map's
     * minimum offset.
     */
    ret = vm_map_raise_min_offset(map, seg_size);


// From xnu-2782.1.97/osfmk/vm/vm_map.c
/*
 * Raise a VM map's minimum offset.
 * To strictly enforce "page zero" reservation.
 */
kern_return_t
vm_map_raise_min_offset(
    vm_map_t    map,
    vm_map_offset_t new_min_offset)
{
...
}

至于为什么你的老Xcode 4.x没有设置它,它似乎很奇怪 - 也许有一个项目文件设置,但是更新的Xcode 5附带的clang编译器和链接器的版本(甚至可以Xcode 6)应该更接近地反映现代OS X 10.10内核的要求。

注意:早期iOS版本中存在一种硬页面零强制执行形式,但以前在OS X中没有。

Valgrind是一个工具套件,它提供了许多调试和分析工具,可帮助您使程序更快,更正确,这也遇到了与新的OS X 10.10内核要求相关的问题:https://bugs.kde.org/show_bug.cgi?id=339045

答案 1 :(得分:6)

我遇到了一个非常类似的问题。 otool -lV显示了类似的段结构。我发现这个问题实际上是由使用UPX(3.08)的可执行打包引起的。如果您已将UPX应用于二进制文件,请运行upx -d解压缩二进制文件并尝试再次在Yosemite上运行它。我刚刚提交的错误报告中描述了我的问题的相关讨论:

https://sourceforge.net/p/upx/bugs/238/