blob: 28a1b09285e73008ac2477837f3951b41f9111d3 [file] [log] [blame]
/* *******************************************************************************
* Copyright (c) 2013-2019 Google, Inc. All rights reserved.
* *******************************************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Mach-O file parsing support shared with non-core. */
#include "../globals.h"
#include "../module_shared.h"
#include "os_private.h"
#include "module_private.h"
#include "memquery_macos.h"
#include "module_macos_dyld.h"
#include <mach-o/ldsyms.h> /* _mh_dylib_header */
#include <mach-o/loader.h> /* mach_header */
#include <mach-o/dyld_images.h>
#include <mach/thread_status.h> /* i386_thread_state_t */
#include <mach-o/nlist.h>
#include <mach-o/fat.h>
#include <sys/syscall.h>
#include <stddef.h> /* offsetof */
#include <dlfcn.h>
/* Like is_elf_so_header(), if size == 0 then safe-reads the header; else
* assumes that [base, base+size) is readable.
*/
bool
is_macho_header(app_pc base, size_t size)
{
struct mach_header hdr_safe;
struct mach_header *hdr;
if (base == NULL)
return false;
if (size >= sizeof(hdr_safe)) {
hdr = (struct mach_header *)base;
} else {
if (!d_r_safe_read(base, sizeof(hdr_safe), &hdr_safe))
return false;
hdr = &hdr_safe;
}
ASSERT(offsetof(struct mach_header, filetype) ==
offsetof(struct mach_header_64, filetype));
if ((hdr->magic == MH_MAGIC && hdr->cputype == CPU_TYPE_X86) ||
(hdr->magic == MH_MAGIC_64 &&
(hdr->cputype == CPU_TYPE_X86_64 || hdr->cputype == CPU_TYPE_ARM64))) {
/* We shouldn't see MH_PRELOAD as it can't be loaded by the kernel */
if (hdr->filetype == MH_EXECUTE || hdr->filetype == MH_DYLIB ||
hdr->filetype == MH_BUNDLE || hdr->filetype == MH_DYLINKER ||
hdr->filetype == MH_FVMLIB) {
return true;
}
}
return false;
}
static bool
platform_from_macho(file_t f, dr_platform_t *platform)
{
struct mach_header mach_hdr;
if (os_read(f, &mach_hdr, sizeof(mach_hdr)) != sizeof(mach_hdr))
return false;
if (!is_macho_header((app_pc)&mach_hdr, sizeof(mach_hdr)))
return false;
switch (mach_hdr.cputype) {
case CPU_TYPE_ARM64: *platform = DR_PLATFORM_64BIT; break;
case CPU_TYPE_X86_64: *platform = DR_PLATFORM_64BIT; break;
case CPU_TYPE_X86: *platform = DR_PLATFORM_32BIT; break;
default: return false;
}
return true;
}
bool
module_get_platform(file_t f, dr_platform_t *platform, dr_platform_t *alt_platform)
{
struct fat_header fat_hdr;
/* Both headers start with a 32-bit magic # */
uint32_t magic;
if (os_read(f, &magic, sizeof(magic)) != sizeof(magic) || !os_seek(f, 0, SEEK_SET))
return false;
if (magic == FAT_CIGAM) { /* big-endian */
/* This is a "fat" or "universal" binary */
struct fat_arch arch;
uint num, i;
bool found_main = false, found_alt = false;
dr_platform_t local_alt = DR_PLATFORM_NONE;
int64 cur_pos;
if (os_read(f, &fat_hdr, sizeof(fat_hdr)) != sizeof(fat_hdr))
return false;
/* OSSwapInt32 is a macro, so there's no lib dependence here */
num = OSSwapInt32(fat_hdr.nfat_arch);
for (i = 0; i < num; i++) {
if (os_read(f, &arch, sizeof(arch)) != sizeof(arch))
return false;
cur_pos = os_tell(f);
/* The primary platform is the one that will be used on an execve,
* which is the one that matches the kernel's bitwidth.
*/
if ((kernel_is_64bit() && OSSwapInt32(arch.cputype) == CPU_TYPE_X86_64) ||
(!kernel_is_64bit() && OSSwapInt32(arch.cputype) == CPU_TYPE_X86)) {
/* Line up right before the Mach-O header */
if (!os_seek(f, OSSwapInt32(arch.offset), SEEK_SET))
return false;
if (!platform_from_macho(f, platform))
return false;
found_main = true;
if (found_alt)
break;
} else if (OSSwapInt32(arch.cputype) == CPU_TYPE_X86_64 ||
OSSwapInt32(arch.cputype) == CPU_TYPE_X86) {
/* Line up right before the Mach-O header */
if (!os_seek(f, OSSwapInt32(arch.offset), SEEK_SET))
return false;
if (platform_from_macho(f, &local_alt)) {
found_alt = true;
if (found_main)
break;
}
}
if (!os_seek(f, cur_pos, SEEK_SET))
return false;
}
if (!found_main && found_alt) {
*platform = local_alt;
local_alt = DR_PLATFORM_NONE;
}
if (alt_platform != NULL)
*alt_platform = local_alt;
return (found_main || found_alt);
} else if (alt_platform != NULL)
*alt_platform = DR_PLATFORM_NONE;
return platform_from_macho(f, platform);
}