| /* ******************************************************************************* |
| * 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); |
| } |