├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── Libraries └── launch-cache │ ├── Architectures.hpp │ ├── CacheFileAbstraction.hpp │ ├── FileAbstraction.hpp │ ├── MachOFileAbstraction.hpp │ ├── dsc_iterator.cpp │ ├── dsc_iterator.h │ └── dyld_cache_format.h ├── Makefile ├── Makefile.arm ├── Makefile.common ├── Makefile.x86_64 ├── README.md ├── get_requirements.sh ├── include ├── CoreSymbolication.h ├── Headers.h ├── SCBinaryInfo.h ├── SCMethodInfo.h ├── SCSymbolInfo.h ├── SCSymbolicator.h ├── binary.h ├── demangle.h ├── libsymbolicate.h ├── methods.h └── sharedCache.h ├── layout └── DEBIAN │ └── control └── lib ├── SCBinaryInfo.xm ├── SCMethodInfo.mm ├── SCSymbolInfo.mm ├── SCSymbolicator.xm ├── binary.mm ├── demangle.mm ├── methods.mm └── sharedCache.mm /.gitignore: -------------------------------------------------------------------------------- 1 | **/theos 2 | **/theos/* 3 | **/.theos/* 4 | *.o 5 | */obj/* 6 | **/obj/* 7 | _/* 8 | *.deb 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | > # Version 1.9.0 2 | > - - - 3 | > * NEW: arm64 4 | > * Added arm64 slice. 5 | > * MOD: armv7(s) 6 | > * Removed armv7 and armv7s slices. 7 | > * FIX: iOS 9 8 | > * Fixed inability to symbolicate items from the 64-bit cache. 9 | 10 | - - - 11 | 12 | > # Version 1.8.0.2 13 | > - - - 14 | > * FIX: iOS 9 15 | > * Temporarily disabled symbolication of items from 64-bit cache in order to prevent crashing. 16 | 17 | - - - 18 | 19 | > # Version 1.8.0.1 20 | > - - - 21 | > * FIX: iOS 9 22 | > * As per Jay Freeman (saurik): "iOS 9 changed the 32-bit pagesize on 64-bit CPUs from 4096 bytes to 16384: all 32-bit binaries must now be compiled with -Wl,-segalign,4000.". 23 | 24 | - - - 25 | 26 | > # Version 1.8.0 27 | > - - - 28 | > * NEW: iOS 8 / OS X 10.10 29 | > * Symbolication now works on iOS 8 and OS X 10.10. 30 | > * MOD: CoreSymbolication 31 | > * Replaced use of Symbolication.framework with CoreSymbolication.framework. 32 | > * MOD: iOS 3 33 | > * Due to the use of CoreSymbolication, iOS 3 is no longer supported. 34 | > * The original CrashReporter ("CrashReporter iOS 3" ) can be used for iOS 3. 35 | 36 | - - - 37 | 38 | > # Version 1.7.0.3 39 | > - - - 40 | > * MOD: iOS 8 / OS X 10.10 41 | > * Added dummy classes to replace those missing from the Symbolication framework. 42 | > * Software that depends on this library will no longer crash, but will not (yet) be able to symbolicate. 43 | 44 | - - - 45 | 46 | > # Version 1.7.0.2 47 | > - - - 48 | > * FIX: Fixed support for "arm" slices with no cpu subtype. 49 | 50 | - - - 51 | 52 | > # Version 1.7.0.1 53 | > - - - 54 | > * FIX: Fixed support for symbolication of stripped app processes. 55 | 56 | - - - 57 | 58 | > # Version 1.7.0 59 | > - - - 60 | > * NEW: Added support for symbolication of SpringBoard (and other stripped binaries) on 64-bit devices. 61 | > * Symbolication only works for methods, not functions (due to stripping). 62 | 63 | - - - 64 | 65 | > # Version 1.6.0 66 | > - - - 67 | > * MOD: Moved crash report parsing portion of library to a separate library, libcrashreport. 68 | 69 | - - - 70 | 71 | > # Version 1.5.0.1 72 | > - - - 73 | > * FIX: Would cause crashes when reading package details with malformed or missing data. 74 | 75 | - - - 76 | 77 | > # Version 1.5.0 78 | > - - - 79 | > * NEW: Add package details to binary images that come from a debian package (dpkg). 80 | > * It is possible that multiple versions of a package could contain the same binary image... other contained files, such as a configuration or data file that the binary uses, may have changed. 81 | > * Due to this, the package details retrieved from the symbolicating device may not be the correct details for the package on the crashing device. This can be true even if the symbolicating and crashing devices are the same... if the package is up/downgraded between the time of the crash and the time of the symbolication. 82 | > * NEW: Add install date of package to binary images that come from a debian package (dpkg). 83 | > * This is only added if the device processing the log is the same device that crashed. 84 | > * FIX: For each binary image in crash report, when symbolicating, be sure to load the exact same binary. 85 | > * If the exact same binary is not available on the symbolicating device, symbolication for that binary will be skipped. 86 | 87 | - - - 88 | 89 | > # Version 1.4.0 90 | > - - - 91 | > * NEW: "Binary Images" output is now separated into different sections, depending on filter type. 92 | > * For filter type "file", it is sectioned into "Blamable" and "Filtered", as determined by the filter file. 93 | > * For filter type "none", it is sectioned into "dpkg", "App Store", "Other", based upon the source of the file. 94 | > * For filter type "none", it is unsectioned. 95 | 96 | - - - 97 | 98 | > # Version 1.3.0 99 | > - - - 100 | > * NEW: Now adds "symbolicated" property to symbolicated files. 101 | > * NEW: Added new "isSymbolicated" property to CRCrashReport. 102 | 103 | - - - 104 | 105 | > # Version 1.2.1 106 | > - - - 107 | > * FIX: Memory leak when parsing IPS files. 108 | 109 | - - - 110 | 111 | > # Version 1.2.0 112 | > - - - 113 | > * MOD: Added armv7 and armv7s slices. 114 | 115 | - - - 116 | 117 | > # Version 1.1.0 118 | > - - - 119 | > * MOD: The crashed process is no longer eligible for inclusion in the blame list. 120 | > * This is because if libsymbolicate is unable to determine blame, it is obvious that the crashed process is the most likely candidate. 121 | 122 | - - - 123 | 124 | > # Version 1.0.1 125 | > - - - 126 | > * MOD: Small speed improvement when symbolicating. 127 | > * FIX: Updated description did not include backtrace of the exception. 128 | > * FIX: Memory leak. 129 | 130 | - - - 131 | 132 | > # Version 1.0.0 133 | > - - - 134 | > * Initial release. 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Lance Fetters (aka. ashikase) 2 | 3 | This project is based upon CrashReporter, originally developed by KennyTM~. 4 | http://networkpx.googlecode.com/svn/trunk/CrashReporter/?r=623 5 | Copyright (C) 2009 KennyTM~ 6 | 7 | CrashReporter was released under the GPL v3 license. Though this library is 8 | based upon that code, it has been released under the LGPL with permission from 9 | the original developer, KennyTM~. 10 | 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the GNU Lesser General Public License as published by 13 | the Free Software Foundation, either version 3 of the License, or 14 | (at your option) any later version. 15 | 16 | This program is distributed in the hope that it will be useful, 17 | but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | GNU Lesser General Public License for more details. 20 | 21 | The GNU Lesser General Public License can be found at . 22 | -------------------------------------------------------------------------------- /Libraries/launch-cache/Architectures.hpp: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 | * 3 | * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. 4 | * 5 | * @APPLE_LICENSE_HEADER_START@ 6 | * 7 | * This file contains Original Code and/or Modifications of Original Code 8 | * as defined in and that are subject to the Apple Public Source License 9 | * Version 2.0 (the 'License'). You may not use this file except in 10 | * compliance with the License. Please obtain a copy of the License at 11 | * http://www.opensource.apple.com/apsl/ and read it before using this 12 | * file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 | * Please see the License for the specific language governing rights and 20 | * limitations under the License. 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | 25 | #ifndef __ARCHITECTURES__ 26 | #define __ARCHITECTURES__ 27 | 28 | #include "FileAbstraction.hpp" 29 | 30 | 31 | // 32 | // Architectures 33 | // 34 | struct x86 35 | { 36 | typedef Pointer32 P; 37 | 38 | }; 39 | 40 | struct x86_64 41 | { 42 | typedef Pointer64 P; 43 | }; 44 | 45 | struct arm 46 | { 47 | typedef Pointer32 P; 48 | 49 | }; 50 | 51 | struct arm64 52 | { 53 | typedef Pointer64 P; 54 | }; 55 | 56 | 57 | 58 | 59 | #endif // __ARCHITECTURES__ 60 | 61 | 62 | -------------------------------------------------------------------------------- /Libraries/launch-cache/CacheFileAbstraction.hpp: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 | * 3 | * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 4 | * 5 | * @APPLE_LICENSE_HEADER_START@ 6 | * 7 | * This file contains Original Code and/or Modifications of Original Code 8 | * as defined in and that are subject to the Apple Public Source License 9 | * Version 2.0 (the 'License'). You may not use this file except in 10 | * compliance with the License. Please obtain a copy of the License at 11 | * http://www.opensource.apple.com/apsl/ and read it before using this 12 | * file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 | * Please see the License for the specific language governing rights and 20 | * limitations under the License. 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | #ifndef __DYLD_CACHE_ABSTRACTION__ 25 | #define __DYLD_CACHE_ABSTRACTION__ 26 | 27 | #include "dyld_cache_format.h" 28 | 29 | #include "FileAbstraction.hpp" 30 | #include "Architectures.hpp" 31 | 32 | template 33 | class dyldCacheHeader { 34 | public: 35 | const char* magic() const INLINE { return fields.magic; } 36 | void set_magic(const char* value) INLINE { memcpy(fields.magic, value, 16); } 37 | 38 | uint32_t mappingOffset() const INLINE { return E::get32(fields.mappingOffset); } 39 | void set_mappingOffset(uint32_t value) INLINE { E::set32(fields.mappingOffset, value); } 40 | 41 | uint32_t mappingCount() const INLINE { return E::get32(fields.mappingCount); } 42 | void set_mappingCount(uint32_t value) INLINE { E::set32(fields.mappingCount, value); } 43 | 44 | uint32_t imagesOffset() const INLINE { return E::get32(fields.imagesOffset); } 45 | void set_imagesOffset(uint32_t value) INLINE { E::set32(fields.imagesOffset, value); } 46 | 47 | uint32_t imagesCount() const INLINE { return E::get32(fields.imagesCount); } 48 | void set_imagesCount(uint32_t value) INLINE { E::set32(fields.imagesCount, value); } 49 | 50 | uint64_t dyldBaseAddress() const INLINE { return E::get64(fields.dyldBaseAddress); } 51 | void set_dyldBaseAddress(uint64_t value) INLINE { E::set64(fields.dyldBaseAddress, value); } 52 | 53 | uint64_t codeSignatureOffset() const INLINE { return E::get64(fields.codeSignatureOffset); } 54 | void set_codeSignatureOffset(uint64_t value) INLINE { E::set64(fields.codeSignatureOffset, value); } 55 | 56 | uint64_t codeSignatureSize() const INLINE { return E::get64(fields.codeSignatureSize); } 57 | void set_codeSignatureSize(uint64_t value) INLINE { E::set64(fields.codeSignatureSize, value); } 58 | 59 | uint64_t slideInfoOffset() const INLINE { return E::get64(fields.slideInfoOffset); } 60 | void set_slideInfoOffset(uint64_t value) INLINE { E::set64(fields.slideInfoOffset, value); } 61 | 62 | uint64_t slideInfoSize() const INLINE { return E::get64(fields.slideInfoSize); } 63 | void set_slideInfoSize(uint64_t value) INLINE { E::set64(fields.slideInfoSize, value); } 64 | 65 | uint64_t localSymbolsOffset() const INLINE { return E::get64(fields.localSymbolsOffset); } 66 | void set_localSymbolsOffset(uint64_t value) INLINE { E::set64(fields.localSymbolsOffset, value); } 67 | 68 | uint64_t localSymbolsSize() const INLINE { return E::get64(fields.localSymbolsSize); } 69 | void set_localSymbolsSize(uint64_t value) INLINE { E::set64(fields.localSymbolsSize, value); } 70 | 71 | const uint8_t* uuid() const INLINE { return fields.uuid; } 72 | void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } 73 | 74 | private: 75 | dyld_cache_header fields; 76 | }; 77 | 78 | 79 | template 80 | class dyldCacheFileMapping { 81 | public: 82 | uint64_t address() const INLINE { return E::get64(fields.address); } 83 | void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } 84 | 85 | uint64_t size() const INLINE { return E::get64(fields.size); } 86 | void set_size(uint64_t value) INLINE { E::set64(fields.size, value); } 87 | 88 | uint64_t file_offset() const INLINE { return E::get64(fields.fileOffset); } 89 | void set_file_offset(uint64_t value) INLINE { E::set64(fields.fileOffset, value); } 90 | 91 | uint32_t max_prot() const INLINE { return E::get32(fields.maxProt); } 92 | void set_max_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.maxProt, value); } 93 | 94 | uint32_t init_prot() const INLINE { return E::get32(fields.initProt); } 95 | void set_init_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.initProt, value); } 96 | 97 | private: 98 | dyld_cache_mapping_info fields; 99 | }; 100 | 101 | 102 | template 103 | class dyldCacheImageInfo { 104 | public: 105 | uint64_t address() const INLINE { return E::get64(fields.address); } 106 | void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } 107 | 108 | uint64_t modTime() const INLINE { return E::get64(fields.modTime); } 109 | void set_modTime(uint64_t value) INLINE { E::set64(fields.modTime, value); } 110 | 111 | uint64_t inode() const INLINE { return E::get64(fields.inode); } 112 | void set_inode(uint64_t value) INLINE { E::set64(fields.inode, value); } 113 | 114 | uint32_t pathFileOffset() const INLINE { return E::get32(fields.pathFileOffset); } 115 | void set_pathFileOffset(uint32_t value) INLINE { E::set32(fields.pathFileOffset, value); fields.pad=0; } 116 | 117 | private: 118 | dyld_cache_image_info fields; 119 | }; 120 | 121 | template 122 | class dyldCacheSlideInfo { 123 | public: 124 | uint32_t version() const INLINE { return E::get32(fields.version); } 125 | void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } 126 | 127 | uint32_t toc_offset() const INLINE { return E::get32(fields.toc_offset); } 128 | void set_toc_offset(uint32_t value) INLINE { E::set32(fields.toc_offset, value); } 129 | 130 | uint32_t toc_count() const INLINE { return E::get32(fields.toc_count); } 131 | void set_toc_count(uint32_t value) INLINE { E::set32(fields.toc_count, value); } 132 | 133 | uint32_t entries_offset() const INLINE { return E::get32(fields.entries_offset); } 134 | void set_entries_offset(uint32_t value) INLINE { E::set32(fields.entries_offset, value); } 135 | 136 | uint32_t entries_count() const INLINE { return E::get32(fields.entries_count); } 137 | void set_entries_count(uint32_t value) INLINE { E::set32(fields.entries_count, value); } 138 | 139 | uint32_t entries_size() const INLINE { return E::get32(fields.entries_size); } 140 | void set_entries_size(uint32_t value) INLINE { E::set32(fields.entries_size, value); } 141 | 142 | uint16_t toc(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index]); } 143 | void set_toc(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index], value); } 144 | 145 | private: 146 | dyld_cache_slide_info fields; 147 | }; 148 | 149 | 150 | struct dyldCacheSlideInfoEntry { 151 | uint8_t bits[4096/(8*4)]; // 128-byte bitmap 152 | }; 153 | 154 | 155 | 156 | template 157 | class dyldCacheLocalSymbolsInfo { 158 | public: 159 | uint32_t nlistOffset() const INLINE { return E::get32(fields.nlistOffset); } 160 | void set_nlistOffset(uint32_t value) INLINE { E::set32(fields.nlistOffset, value); } 161 | 162 | uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } 163 | void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } 164 | 165 | uint32_t stringsOffset() const INLINE { return E::get32(fields.stringsOffset); } 166 | void set_stringsOffset(uint32_t value) INLINE { E::set32(fields.stringsOffset, value); } 167 | 168 | uint32_t stringsSize() const INLINE { return E::get32(fields.stringsSize); } 169 | void set_stringsSize(uint32_t value) INLINE { E::set32(fields.stringsSize, value); } 170 | 171 | uint32_t entriesOffset() const INLINE { return E::get32(fields.entriesOffset); } 172 | void set_entriesOffset(uint32_t value) INLINE { E::set32(fields.entriesOffset, value); } 173 | 174 | uint32_t entriesCount() const INLINE { return E::get32(fields.entriesCount); } 175 | void set_entriesCount(uint32_t value) INLINE { E::set32(fields.entriesCount, value); } 176 | 177 | private: 178 | dyld_cache_local_symbols_info fields; 179 | }; 180 | 181 | 182 | template 183 | class dyldCacheLocalSymbolEntry { 184 | public: 185 | uint32_t dylibOffset() const INLINE { return E::get32(fields.dylibOffset); } 186 | void set_dylibOffset(uint32_t value) INLINE { E::set32(fields.dylibOffset, value); } 187 | 188 | uint32_t nlistStartIndex() const INLINE { return E::get32(fields.nlistStartIndex); } 189 | void set_nlistStartIndex(uint32_t value) INLINE { E::set32(fields.nlistStartIndex, value); } 190 | 191 | uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } 192 | void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } 193 | 194 | private: 195 | dyld_cache_local_symbols_entry fields; 196 | }; 197 | 198 | 199 | 200 | 201 | #endif // __DYLD_CACHE_ABSTRACTION__ 202 | 203 | 204 | -------------------------------------------------------------------------------- /Libraries/launch-cache/FileAbstraction.hpp: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 | * 3 | * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. 4 | * 5 | * @APPLE_LICENSE_HEADER_START@ 6 | * 7 | * This file contains Original Code and/or Modifications of Original Code 8 | * as defined in and that are subject to the Apple Public Source License 9 | * Version 2.0 (the 'License'). You may not use this file except in 10 | * compliance with the License. Please obtain a copy of the License at 11 | * http://www.opensource.apple.com/apsl/ and read it before using this 12 | * file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 | * Please see the License for the specific language governing rights and 20 | * limitations under the License. 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | #ifndef __FILE_ABSTRACTION__ 25 | #define __FILE_ABSTRACTION__ 26 | 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #ifdef __OPTIMIZE__ 33 | #define INLINE __attribute__((always_inline)) 34 | #else 35 | #define INLINE 36 | #endif 37 | 38 | // 39 | // This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants 40 | // 41 | // For example: to make a utility that handles 32-bit little enidan files use: Pointer32 42 | // 43 | // 44 | // get16() read a 16-bit number from an E endian struct 45 | // set16() write a 16-bit number to an E endian struct 46 | // get32() read a 32-bit number from an E endian struct 47 | // set32() write a 32-bit number to an E endian struct 48 | // get64() read a 64-bit number from an E endian struct 49 | // set64() write a 64-bit number to an E endian struct 50 | // 51 | // getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) 52 | // setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) 53 | // 54 | // getBitsRaw() read a bit field from a struct with native endianness 55 | // setBitsRaw() write a bit field from a struct with native endianness 56 | // 57 | 58 | class BigEndian 59 | { 60 | public: 61 | static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } 62 | static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } 63 | 64 | static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } 65 | static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } 66 | 67 | static int32_t get32(const int32_t& from) INLINE { return OSReadBigInt32(&from, 0); } 68 | static void set32(int32_t& into, int32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } 69 | 70 | static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } 71 | static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } 72 | 73 | static uint32_t getBits(const uint32_t& from, 74 | uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } 75 | static void setBits(uint32_t& into, uint32_t value, 76 | uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } 77 | 78 | static uint32_t getBitsRaw(const uint32_t& from, 79 | uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< 123 | class Pointer32 124 | { 125 | public: 126 | typedef uint32_t uint_t; 127 | typedef _E E; 128 | 129 | static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } 130 | static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); } 131 | 132 | // Round to a P-size boundary 133 | template 134 | static T round_up(T value) { return (value+3) & ~(T)3; } 135 | template 136 | static T round_down(T value) { return value & ~(T)3; } 137 | }; 138 | 139 | 140 | template 141 | class Pointer64 142 | { 143 | public: 144 | typedef uint64_t uint_t; 145 | typedef _E E; 146 | 147 | static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } 148 | static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } 149 | 150 | // Round to a P-size boundary 151 | template 152 | static T round_up(T value) { return (value+7) & ~(T)7; } 153 | template 154 | static T round_down(T value) { return value & ~(T)7; } 155 | }; 156 | 157 | 158 | 159 | 160 | 161 | #endif // __FILE_ABSTRACTION__ 162 | 163 | 164 | -------------------------------------------------------------------------------- /Libraries/launch-cache/MachOFileAbstraction.hpp: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 | * 3 | * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. 4 | * 5 | * @APPLE_LICENSE_HEADER_START@ 6 | * 7 | * This file contains Original Code and/or Modifications of Original Code 8 | * as defined in and that are subject to the Apple Public Source License 9 | * Version 2.0 (the 'License'). You may not use this file except in 10 | * compliance with the License. Please obtain a copy of the License at 11 | * http://www.opensource.apple.com/apsl/ and read it before using this 12 | * file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 | * Please see the License for the specific language governing rights and 20 | * limitations under the License. 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | #ifndef __MACH_O_FILE_ABSTRACTION__ 25 | #define __MACH_O_FILE_ABSTRACTION__ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | // suport older versions of mach-o/loader.h 33 | #ifndef LC_UUID 34 | #define LC_UUID 0x1b 35 | struct uuid_command { 36 | uint32_t cmd; /* LC_UUID */ 37 | uint32_t cmdsize; /* sizeof(struct uuid_command) */ 38 | uint8_t uuid[16]; /* the 128-bit uuid */ 39 | }; 40 | #endif 41 | 42 | #ifndef S_16BYTE_LITERALS 43 | #define S_16BYTE_LITERALS 0xE 44 | #endif 45 | 46 | #ifndef CPU_SUBTYPE_ARM_V5TEJ 47 | #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) 48 | #endif 49 | #ifndef CPU_SUBTYPE_ARM_XSCALE 50 | #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) 51 | #endif 52 | #ifndef CPU_SUBTYPE_ARM_V7 53 | #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) 54 | #endif 55 | #ifndef CPU_SUBTYPE_ARM_V7F 56 | #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) 57 | #endif 58 | #ifndef CPU_SUBTYPE_ARM_V7K 59 | #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) 60 | #endif 61 | #ifndef CPU_SUBTYPE_ARM_V7S 62 | #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) 63 | #endif 64 | 65 | #ifndef LC_LOAD_UPWARD_DYLIB 66 | #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ 67 | #endif 68 | 69 | #ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 70 | #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 71 | #endif 72 | #ifndef EXPORT_SYMBOL_FLAGS_REEXPORT 73 | #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 74 | #endif 75 | 76 | #ifndef LC_FUNCTION_STARTS 77 | #define LC_FUNCTION_STARTS 0x26 78 | #endif 79 | 80 | #ifndef LC_DATA_IN_CODE 81 | #define LC_DATA_IN_CODE 0x29 82 | #endif 83 | 84 | #ifndef LC_DYLIB_CODE_SIGN_DRS 85 | #define LC_DYLIB_CODE_SIGN_DRS 0x2B 86 | #endif 87 | 88 | 89 | 90 | #include "FileAbstraction.hpp" 91 | #include "Architectures.hpp" 92 | 93 | // utility to pair together a cpu-type and cpu-sub-type 94 | struct ArchPair 95 | { 96 | uint32_t arch; 97 | uint32_t subtype; 98 | 99 | ArchPair(uint32_t cputype, uint32_t cpusubtype) : arch(cputype), subtype(cpusubtype) {} 100 | 101 | bool operator<(const ArchPair& other) const { 102 | if ( this->arch != other.arch ) 103 | return (this->arch < other.arch); 104 | return (this->subtype < other.subtype); 105 | } 106 | }; 107 | 108 | 109 | // 110 | // This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness 111 | // 112 | 113 | // 114 | // mach-o load command 115 | // 116 | template 117 | class macho_load_command { 118 | public: 119 | uint32_t cmd() const INLINE { return E::get32(command.cmd); } 120 | void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } 121 | 122 | uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } 123 | void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } 124 | 125 | typedef typename P::E E; 126 | private: 127 | load_command command; 128 | }; 129 | 130 | 131 | // 132 | // mach-o segment load command 133 | // 134 | template struct macho_segment_content {}; 135 | template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; 136 | template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; 137 | template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; 138 | template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; 139 | 140 | template 141 | class macho_segment_command { 142 | public: 143 | uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } 144 | void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } 145 | 146 | uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } 147 | void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } 148 | 149 | const char* segname() const INLINE { return segment.fields.segname; } 150 | void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } 151 | 152 | uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } 153 | void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } 154 | 155 | uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } 156 | void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } 157 | 158 | uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } 159 | void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } 160 | 161 | uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } 162 | void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } 163 | 164 | uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } 165 | void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } 166 | 167 | uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } 168 | void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } 169 | 170 | uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } 171 | void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } 172 | 173 | uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } 174 | void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } 175 | 176 | enum { 177 | CMD = macho_segment_content

::CMD 178 | }; 179 | 180 | typedef typename P::E E; 181 | private: 182 | macho_segment_content

segment; 183 | }; 184 | 185 | 186 | // 187 | // mach-o section 188 | // 189 | template struct macho_section_content {}; 190 | template <> struct macho_section_content > { section fields; }; 191 | template <> struct macho_section_content > { section_64 fields; }; 192 | template <> struct macho_section_content > { section fields; }; 193 | template <> struct macho_section_content > { section_64 fields; }; 194 | 195 | template 196 | class macho_section { 197 | public: 198 | const char* sectname() const INLINE { return section.fields.sectname; } 199 | void set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); } 200 | 201 | const char* segname() const INLINE { return section.fields.segname; } 202 | void set_segname(const char* value) INLINE { strncpy(section.fields.segname, value, 16); } 203 | 204 | uint64_t addr() const INLINE { return P::getP(section.fields.addr); } 205 | void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); } 206 | 207 | uint64_t size() const INLINE { return P::getP(section.fields.size); } 208 | void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); } 209 | 210 | uint32_t offset() const INLINE { return E::get32(section.fields.offset); } 211 | void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); } 212 | 213 | uint32_t align() const INLINE { return E::get32(section.fields.align); } 214 | void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); } 215 | 216 | uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); } 217 | void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); } 218 | 219 | uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); } 220 | void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); } 221 | 222 | uint32_t flags() const INLINE { return E::get32(section.fields.flags); } 223 | void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); } 224 | 225 | uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); } 226 | void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); } 227 | 228 | uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); } 229 | void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); } 230 | 231 | typedef typename P::E E; 232 | private: 233 | macho_section_content

section; 234 | }; 235 | 236 | 237 | // 238 | // mach-o dylib load command 239 | // 240 | template 241 | class macho_dylib_command { 242 | public: 243 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 244 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 245 | 246 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 247 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 248 | 249 | uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); } 250 | void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); } 251 | 252 | uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); } 253 | void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); } 254 | 255 | uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); } 256 | void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); } 257 | 258 | uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); } 259 | void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); } 260 | 261 | const char* name() const INLINE { return (const char*)&fields + name_offset(); } 262 | void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } 263 | 264 | typedef typename P::E E; 265 | private: 266 | dylib_command fields; 267 | }; 268 | 269 | 270 | // 271 | // mach-o dylinker load command 272 | // 273 | template 274 | class macho_dylinker_command { 275 | public: 276 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 277 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 278 | 279 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 280 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 281 | 282 | uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); } 283 | void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); } 284 | 285 | const char* name() const INLINE { return (const char*)&fields + name_offset(); } 286 | void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } 287 | 288 | typedef typename P::E E; 289 | private: 290 | dylinker_command fields; 291 | }; 292 | 293 | 294 | // 295 | // mach-o sub_framework load command 296 | // 297 | template 298 | class macho_sub_framework_command { 299 | public: 300 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 301 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 302 | 303 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 304 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 305 | 306 | uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); } 307 | void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); } 308 | 309 | const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); } 310 | void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); } 311 | 312 | typedef typename P::E E; 313 | private: 314 | sub_framework_command fields; 315 | }; 316 | 317 | 318 | // 319 | // mach-o sub_client load command 320 | // 321 | template 322 | class macho_sub_client_command { 323 | public: 324 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 325 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 326 | 327 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 328 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 329 | 330 | uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); } 331 | void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); } 332 | 333 | const char* client() const INLINE { return (const char*)&fields + client_offset(); } 334 | void set_client_offset() INLINE { set_client_offset(sizeof(fields)); } 335 | 336 | typedef typename P::E E; 337 | private: 338 | sub_client_command fields; 339 | }; 340 | 341 | 342 | // 343 | // mach-o sub_umbrella load command 344 | // 345 | template 346 | class macho_sub_umbrella_command { 347 | public: 348 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 349 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 350 | 351 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 352 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 353 | 354 | uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); } 355 | void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); } 356 | 357 | const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); } 358 | void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); } 359 | 360 | typedef typename P::E E; 361 | private: 362 | sub_umbrella_command fields; 363 | }; 364 | 365 | 366 | // 367 | // mach-o sub_library load command 368 | // 369 | template 370 | class macho_sub_library_command { 371 | public: 372 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 373 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 374 | 375 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 376 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 377 | 378 | uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); } 379 | void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); } 380 | 381 | const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); } 382 | void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); } 383 | 384 | typedef typename P::E E; 385 | private: 386 | sub_library_command fields; 387 | }; 388 | 389 | 390 | // 391 | // mach-o uuid load command 392 | // 393 | template 394 | class macho_uuid_command { 395 | public: 396 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 397 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 398 | 399 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 400 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 401 | 402 | const uint8_t* uuid() const INLINE { return fields.uuid; } 403 | void set_uuid(uint8_t value[16]) INLINE { memcpy(&fields.uuid, value, 16); } 404 | 405 | typedef typename P::E E; 406 | private: 407 | uuid_command fields; 408 | }; 409 | 410 | 411 | // 412 | // mach-o routines load command 413 | // 414 | template struct macho_routines_content {}; 415 | template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; 416 | template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; 417 | template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; 418 | template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; 419 | 420 | template 421 | class macho_routines_command { 422 | public: 423 | uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); } 424 | void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); } 425 | 426 | uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); } 427 | void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); } 428 | 429 | uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); } 430 | void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); } 431 | 432 | uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); } 433 | void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); } 434 | 435 | uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); } 436 | void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); } 437 | 438 | uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); } 439 | void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); } 440 | 441 | uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); } 442 | void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); } 443 | 444 | uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); } 445 | void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); } 446 | 447 | uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); } 448 | void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); } 449 | 450 | uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); } 451 | void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); } 452 | 453 | typedef typename P::E E; 454 | enum { 455 | CMD = macho_routines_content

::CMD 456 | }; 457 | private: 458 | macho_routines_content

routines; 459 | }; 460 | 461 | 462 | // 463 | // mach-o symbol table load command 464 | // 465 | template 466 | class macho_symtab_command { 467 | public: 468 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 469 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 470 | 471 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 472 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 473 | 474 | uint32_t symoff() const INLINE { return E::get32(fields.symoff); } 475 | void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); } 476 | 477 | uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); } 478 | void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); } 479 | 480 | uint32_t stroff() const INLINE { return E::get32(fields.stroff); } 481 | void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); } 482 | 483 | uint32_t strsize() const INLINE { return E::get32(fields.strsize); } 484 | void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); } 485 | 486 | 487 | typedef typename P::E E; 488 | private: 489 | symtab_command fields; 490 | }; 491 | 492 | 493 | // 494 | // mach-o dynamic symbol table load command 495 | // 496 | template 497 | class macho_dysymtab_command { 498 | public: 499 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 500 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 501 | 502 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 503 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 504 | 505 | uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); } 506 | void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); } 507 | 508 | uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); } 509 | void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); } 510 | 511 | uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); } 512 | void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); } 513 | 514 | uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); } 515 | void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); } 516 | 517 | uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); } 518 | void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); } 519 | 520 | uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); } 521 | void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); } 522 | 523 | uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); } 524 | void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); } 525 | 526 | uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); } 527 | void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); } 528 | 529 | uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); } 530 | void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); } 531 | 532 | uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); } 533 | void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); } 534 | 535 | uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); } 536 | void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); } 537 | 538 | uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); } 539 | void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); } 540 | 541 | uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); } 542 | void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); } 543 | 544 | uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); } 545 | void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); } 546 | 547 | uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); } 548 | void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); } 549 | 550 | uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); } 551 | void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); } 552 | 553 | uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); } 554 | void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); } 555 | 556 | uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); } 557 | void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); } 558 | 559 | typedef typename P::E E; 560 | private: 561 | dysymtab_command fields; 562 | }; 563 | 564 | 565 | // 566 | // mach-o two-level hints load command 567 | // 568 | template 569 | class macho_twolevel_hints_command { 570 | public: 571 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 572 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 573 | 574 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 575 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 576 | 577 | uint32_t offset() const INLINE { return E::get32(fields.offset); } 578 | void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); } 579 | 580 | uint32_t nhints() const INLINE { return E::get32(fields.nhints); } 581 | void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); } 582 | 583 | typedef typename P::E E; 584 | private: 585 | twolevel_hints_command fields; 586 | }; 587 | 588 | 589 | // 590 | // mach-o threads load command 591 | // 592 | template 593 | class macho_thread_command { 594 | public: 595 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 596 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 597 | 598 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 599 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 600 | 601 | uint32_t flavor() const INLINE { return E::get32(fields_flavor); } 602 | void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); } 603 | 604 | uint32_t count() const INLINE { return E::get32(fields_count); } 605 | void set_count(uint32_t value) INLINE { E::set32(fields_count, value); } 606 | 607 | uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); } 608 | void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); } 609 | 610 | typedef typename P::E E; 611 | typedef typename P::uint_t pint_t; 612 | private: 613 | struct thread_command fields; 614 | uint32_t fields_flavor; 615 | uint32_t fields_count; 616 | pint_t thread_registers[1]; 617 | }; 618 | 619 | 620 | // 621 | // mach-o misc data 622 | // 623 | template 624 | class macho_linkedit_data_command { 625 | public: 626 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 627 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 628 | 629 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 630 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 631 | 632 | uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); } 633 | void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); } 634 | 635 | uint32_t datasize() const INLINE { return E::get32(fields.datasize); } 636 | void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); } 637 | 638 | 639 | typedef typename P::E E; 640 | private: 641 | linkedit_data_command fields; 642 | }; 643 | 644 | 645 | // 646 | // mach-o symbol table entry 647 | // 648 | template struct macho_nlist_content {}; 649 | template <> struct macho_nlist_content > { struct nlist fields; }; 650 | template <> struct macho_nlist_content > { struct nlist_64 fields; }; 651 | template <> struct macho_nlist_content > { struct nlist fields; }; 652 | template <> struct macho_nlist_content > { struct nlist_64 fields; }; 653 | 654 | template 655 | class macho_nlist { 656 | public: 657 | uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); } 658 | void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); } 659 | 660 | uint8_t n_type() const INLINE { return entry.fields.n_type; } 661 | void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; } 662 | 663 | uint8_t n_sect() const INLINE { return entry.fields.n_sect; } 664 | void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; } 665 | 666 | uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); } 667 | void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); } 668 | 669 | uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); } 670 | void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); } 671 | 672 | typedef typename P::E E; 673 | private: 674 | macho_nlist_content

entry; 675 | }; 676 | 677 | 678 | 679 | // 680 | // mach-o relocation info 681 | // 682 | template 683 | class macho_relocation_info { 684 | public: 685 | uint32_t r_address() const INLINE { return E::get32(address); } 686 | void set_r_address(uint32_t value) INLINE { E::set32(address, value); } 687 | 688 | uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } 689 | void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } 690 | 691 | bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } 692 | void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } 693 | 694 | uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } 695 | void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } 696 | 697 | bool r_extern() const INLINE { return E::getBits(other, 27, 1); } 698 | void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } 699 | 700 | uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } 701 | void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } 702 | 703 | void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } 704 | 705 | typedef typename P::E E; 706 | private: 707 | uint32_t address; 708 | uint32_t other; 709 | }; 710 | 711 | 712 | // 713 | // mach-o scattered relocation info 714 | // The bit fields are always in big-endian order (see mach-o/reloc.h) 715 | // 716 | template 717 | class macho_scattered_relocation_info { 718 | public: 719 | bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } 720 | void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } 721 | 722 | bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } 723 | void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } 724 | 725 | uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } 726 | void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } 727 | 728 | uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } 729 | void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } 730 | 731 | uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } 732 | void set_r_address(uint32_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } 733 | 734 | uint32_t r_value() const INLINE { return E::get32(value); } 735 | void set_r_value(uint32_t x) INLINE { E::set32(value, x); } 736 | 737 | uint32_t r_other() const INLINE { return other; } 738 | 739 | typedef typename P::E E; 740 | private: 741 | uint32_t other; 742 | uint32_t value; 743 | }; 744 | 745 | 746 | // 747 | // mach-o file header 748 | // 749 | template struct macho_header_content {}; 750 | template <> struct macho_header_content > { mach_header fields; }; 751 | template <> struct macho_header_content > { mach_header_64 fields; }; 752 | template <> struct macho_header_content > { mach_header fields; }; 753 | template <> struct macho_header_content > { mach_header_64 fields; }; 754 | 755 | template 756 | class macho_header { 757 | public: 758 | uint32_t magic() const INLINE { return E::get32(header.fields.magic); } 759 | void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } 760 | 761 | uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } 762 | void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } 763 | 764 | uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } 765 | void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } 766 | 767 | uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } 768 | void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } 769 | 770 | uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } 771 | void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } 772 | 773 | uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } 774 | void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } 775 | 776 | uint32_t flags() const INLINE { return E::get32(header.fields.flags); } 777 | void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } 778 | 779 | uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } 780 | void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } 781 | 782 | const macho_segment_command

* getSegment(const char *segname) const 783 | { 784 | const macho_load_command

* cmds = (macho_load_command

*)((uint8_t*)this + sizeof(macho_header

)); 785 | uint32_t cmd_count = this->ncmds(); 786 | const macho_load_command

* cmd = cmds; 787 | for (uint32_t i = 0; i < cmd_count; ++i) { 788 | if ( cmd->cmd() == macho_segment_command

::CMD ) { 789 | const macho_segment_command

* segcmd = (macho_segment_command

*)cmd; 790 | if (0 == strncmp(segname, segcmd->segname(), 16)) { 791 | return segcmd; 792 | } 793 | } 794 | cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); 795 | } 796 | return NULL; 797 | } 798 | 799 | const macho_section

* getSection(const char *segname, const char *sectname) const 800 | { 801 | const macho_segment_command

* segcmd = getSegment(segname); 802 | if (!segcmd) return NULL; 803 | 804 | const macho_section

* sectcmd = (macho_section

*)(segcmd+1); 805 | uint32_t section_count = segcmd->nsects(); 806 | for (uint32_t j = 0; j < section_count; ++j) { 807 | if (0 == ::strncmp(sectcmd[j].sectname(), sectname, 16)) { 808 | return sectcmd+j; 809 | } 810 | } 811 | 812 | return NULL; 813 | } 814 | 815 | const macho_load_command

* getLoadCommand(int query) const 816 | { 817 | const macho_load_command

* cmds = (macho_load_command

*)((uint8_t*)this + sizeof(macho_header

)); 818 | uint32_t cmd_count = this->ncmds(); 819 | const macho_load_command

* cmd = cmds; 820 | for (uint32_t i = 0; i < cmd_count; ++i) { 821 | if ( cmd->cmd() == query ) { 822 | return cmd; 823 | } 824 | cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); 825 | } 826 | return NULL; 827 | } 828 | 829 | typedef typename P::E E; 830 | private: 831 | macho_header_content

header; 832 | }; 833 | 834 | 835 | 836 | // 837 | // compressed dyld info load command 838 | // 839 | template 840 | class macho_dyld_info_command { 841 | public: 842 | uint32_t cmd() const INLINE { return E::get32(fields.cmd); } 843 | void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } 844 | 845 | uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } 846 | void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } 847 | 848 | uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); } 849 | void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); } 850 | 851 | uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); } 852 | void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); } 853 | 854 | uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); } 855 | void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); } 856 | 857 | uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); } 858 | void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); } 859 | 860 | uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); } 861 | void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); } 862 | 863 | uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); } 864 | void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); } 865 | 866 | uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); } 867 | void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); } 868 | 869 | uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); } 870 | void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); } 871 | 872 | uint32_t export_off() const INLINE { return E::get32(fields.export_off); } 873 | void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); } 874 | 875 | uint32_t export_size() const INLINE { return E::get32(fields.export_size); } 876 | void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); } 877 | 878 | 879 | typedef typename P::E E; 880 | private: 881 | dyld_info_command fields; 882 | }; 883 | 884 | #ifndef NO_ULEB 885 | inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { 886 | uint64_t result = 0; 887 | int bit = 0; 888 | do { 889 | if (p == end) 890 | throw "malformed uleb128 extends beyond trie"; 891 | 892 | uint64_t slice = *p & 0x7f; 893 | 894 | if (bit >= 64 || slice << bit >> bit != slice) 895 | throw "uleb128 too big for 64-bits"; 896 | else { 897 | result |= (slice << bit); 898 | bit += 7; 899 | } 900 | } 901 | while (*p++ & 0x80); 902 | return result; 903 | } 904 | 905 | 906 | static int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) 907 | { 908 | int64_t result = 0; 909 | int bit = 0; 910 | uint8_t byte; 911 | do { 912 | if (p == end) 913 | throw "malformed sleb128"; 914 | byte = *p++; 915 | result |= ((byte & 0x7f) << bit); 916 | bit += 7; 917 | } while (byte & 0x80); 918 | // sign extend negative numbers 919 | if ( (byte & 0x40) != 0 ) 920 | result |= (-1LL) << bit; 921 | return result; 922 | } 923 | 924 | #endif 925 | 926 | 927 | #endif // __MACH_O_FILE_ABSTRACTION__ 928 | 929 | 930 | -------------------------------------------------------------------------------- /Libraries/launch-cache/dsc_iterator.cpp: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 | * 3 | * Copyright (c) 2009-2012 Apple Inc. All rights reserved. 4 | * 5 | * @APPLE_LICENSE_HEADER_START@ 6 | * 7 | * This file contains Original Code and/or Modifications of Original Code 8 | * as defined in and that are subject to the Apple Public Source License 9 | * Version 2.0 (the 'License'). You may not use this file except in 10 | * compliance with the License. Please obtain a copy of the License at 11 | * http://www.opensource.apple.com/apsl/ and read it before using this 12 | * file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 | * Please see the License for the specific language governing rights and 20 | * limitations under the License. 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | 30 | #include "dsc_iterator.h" 31 | #include "dyld_cache_format.h" 32 | #define NO_ULEB 33 | #include "Architectures.hpp" 34 | #include "MachOFileAbstraction.hpp" 35 | #include "CacheFileAbstraction.hpp" 36 | 37 | 38 | 39 | namespace dyld { 40 | 41 | 42 | // convert an address in the shared region where the cache would normally be mapped, into an address where the cache is currently mapped 43 | template 44 | const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr) 45 | { 46 | const dyldCacheHeader* header = (dyldCacheHeader*)cache; 47 | const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; 48 | for (uint32_t i=0; i < header->mappingCount(); ++i) { 49 | if ( (mappings[i].address() <= addr) && (addr < (mappings[i].address() + mappings[i].size())) ) { 50 | uint32_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address(); 51 | const uint8_t* result = &cache[cacheOffset]; 52 | if ( result < cacheEnd ) 53 | return result; 54 | else 55 | return NULL; 56 | } 57 | } 58 | return NULL; 59 | } 60 | 61 | // call the callback block on each segment in this image 62 | template 63 | int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, const uint8_t* machHeader, 64 | void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) 65 | { 66 | typedef typename A::P P; 67 | typedef typename A::P::E E; 68 | dyld_shared_cache_dylib_info dylibInfo; 69 | dyld_shared_cache_segment_info segInfo; 70 | dylibInfo.version = 1; 71 | dylibInfo.isAlias = (dylibPath < (char*)firstSeg); // paths for aliases are store between cache header and first segment 72 | dylibInfo.machHeader = machHeader; 73 | dylibInfo.path = dylibPath; 74 | const macho_header

* mh = (const macho_header

*)machHeader; 75 | const macho_load_command

* const cmds = (macho_load_command

*)(machHeader + sizeof(macho_header

)); 76 | if ( (machHeader+ mh->sizeofcmds()) > cacheEnd ) 77 | return -1; 78 | const uint32_t cmd_count = mh->ncmds(); 79 | const macho_load_command

* cmd = cmds; 80 | // scan for LC_UUID 81 | dylibInfo.uuid = NULL; 82 | for (uint32_t i = 0; i < cmd_count; ++i) { 83 | if ( cmd->cmd() == LC_UUID ) { 84 | const uuid_command* uc = (const uuid_command*)cmd; 85 | dylibInfo.uuid = &uc->uuid; 86 | break; 87 | } 88 | cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); 89 | } 90 | // callback for each LC_SEGMENT 91 | cmd = cmds; 92 | for (uint32_t i = 0; i < cmd_count; ++i) { 93 | if ( cmd->cmd() == macho_segment_command

::CMD ) { 94 | macho_segment_command

* segCmd = (macho_segment_command

*)cmd; 95 | uint64_t fileOffset = segCmd->fileoff(); 96 | // work around until is fixed 97 | if ( fileOffset == 0 ) { 98 | fileOffset = (machHeader - cache); 99 | } 100 | uint64_t sizem = segCmd->vmsize(); 101 | if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { 102 | // clip LINKEDIT size if bigger than cache file 103 | if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) ) 104 | sizem = (cacheEnd-cache)-fileOffset; 105 | } 106 | segInfo.version = 1; 107 | segInfo.name = segCmd->segname(); 108 | segInfo.fileOffset = fileOffset; 109 | segInfo.fileSize = sizem; 110 | segInfo.address = segCmd->vmaddr(); 111 | callback(&dylibInfo, &segInfo); 112 | } 113 | cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); 114 | } 115 | return 0; 116 | } 117 | 118 | 119 | // call walkSegments on each image in the cache 120 | template 121 | int walkImages(const uint8_t* cache, uint32_t size, void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) 122 | { 123 | // Sanity check there is at least a header 124 | if ( (size > 0) && (size < 0x7000) ) 125 | return -1; 126 | typedef typename A::P::E E; 127 | typedef typename A::P P; 128 | const dyldCacheHeader* header = (dyldCacheHeader*)cache; 129 | const dyldCacheImageInfo* dylibs = (dyldCacheImageInfo*)&cache[header->imagesOffset()]; 130 | const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; 131 | uint64_t greatestMappingOffset = 0; 132 | for (uint32_t i=0; i < header->mappingCount(); ++i) { 133 | if ( (size != 0) && (mappings[i].file_offset() > size) ) 134 | return -1; 135 | uint64_t endOffset = mappings[i].file_offset()+mappings[i].size(); 136 | if ( (size != 0) && (endOffset > size) ) 137 | return -1; 138 | if ( endOffset > greatestMappingOffset ) 139 | greatestMappingOffset = endOffset; 140 | } 141 | const uint8_t* cacheEnd = &cache[size]; 142 | if ( size == 0 ) { 143 | // Zero size means old API is being used, assume all mapped 144 | cacheEnd = &cache[greatestMappingOffset]; 145 | } 146 | else { 147 | // verifiy mappings are not bigger than size 148 | if ( size < greatestMappingOffset ) 149 | return -1; 150 | } 151 | // verify all image infos are mapped 152 | if ( (const uint8_t*)&dylibs[header->imagesCount()] > cacheEnd ) 153 | return -1; 154 | const uint8_t* firstSeg = NULL; 155 | for (uint32_t i=0; i < header->imagesCount(); ++i) { 156 | const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); 157 | if ( (const uint8_t*)dylibPath > cacheEnd ) 158 | return -1; 159 | const uint8_t* machHeader = mappedAddress(cache, cacheEnd, dylibs[i].address()); 160 | if ( machHeader == NULL ) 161 | return -1; 162 | if ( machHeader > cacheEnd ) 163 | return -1; 164 | if ( firstSeg == NULL ) 165 | firstSeg = machHeader; 166 | int result = walkSegments(cache, cacheEnd, firstSeg, dylibPath, machHeader, callback); 167 | if ( result != 0 ) 168 | return result; 169 | } 170 | return 0; 171 | } 172 | 173 | } 174 | 175 | 176 | // Given a pointer to an in-memory copy of a dyld shared cache file, 177 | // this routine will call the callback block once for each segment 178 | // in each dylib in the shared cache file. 179 | // Returns -1 if there was an error, otherwise 0. 180 | extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, 181 | void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { 182 | const uint8_t* cache = (uint8_t*)shared_cache_file; 183 | if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) 184 | return dyld::walkImages(cache, shared_cache_size, callback); 185 | else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) 186 | return dyld::walkImages(cache, shared_cache_size, callback); 187 | else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) 188 | return dyld::walkImages(cache, shared_cache_size, callback); 189 | else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) 190 | return dyld::walkImages(cache, shared_cache_size, callback); 191 | else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 ) 192 | return dyld::walkImages(cache, shared_cache_size, callback); 193 | else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 ) 194 | return dyld::walkImages(cache, shared_cache_size, callback); 195 | else if ( strcmp((char*)cache, "dyld_v1 arm64") == 0 ) 196 | return dyld::walkImages(cache, shared_cache_size, callback); 197 | else 198 | return -1; 199 | } 200 | 201 | 202 | // implement old version by calling new version 203 | int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) 204 | { 205 | return dyld_shared_cache_iterate(shared_cache_file, 0, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { 206 | callback(dylibInfo->path, segInfo->name, segInfo->fileOffset, segInfo->fileSize, segInfo->address, 0); 207 | }); 208 | } 209 | 210 | // implement non-block version by calling block version 211 | int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData) 212 | { 213 | return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, 214 | uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { 215 | (*func)(dylibName, segName, offset, size, mappedddress, slide, userData); 216 | }); 217 | } 218 | 219 | 220 | // implement non-slide version by wrapping slide version in block 221 | int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) 222 | { 223 | dyld_shared_cache_iterator_slide_t wrapper_cb = ^(const char* dylibName, const char* segName, uint64_t offset, 224 | uint64_t size, uint64_t mappedddress, uint64_t slide) { 225 | callback(dylibName, segName, offset, size, mappedddress); 226 | }; 227 | return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, wrapper_cb); 228 | } 229 | 230 | // implement non-slide,non-block version by wrapping slide version in block 231 | int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t func, void* userData) 232 | { 233 | return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, 234 | uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { 235 | (*func)(dylibName, segName, offset, size, mappedddress, userData); 236 | }); 237 | } 238 | 239 | -------------------------------------------------------------------------------- /Libraries/launch-cache/dsc_iterator.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 | * 3 | * Copyright (c) 2009-2012 Apple Inc. All rights reserved. 4 | * 5 | * @APPLE_LICENSE_HEADER_START@ 6 | * 7 | * This file contains Original Code and/or Modifications of Original Code 8 | * as defined in and that are subject to the Apple Public Source License 9 | * Version 2.0 (the 'License'). You may not use this file except in 10 | * compliance with the License. Please obtain a copy of the License at 11 | * http://www.opensource.apple.com/apsl/ and read it before using this 12 | * file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 | * Please see the License for the specific language governing rights and 20 | * limitations under the License. 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | struct dyld_shared_cache_dylib_info { 29 | uint32_t version; // initial version 1 30 | uint32_t isAlias; // this is alternate path (symlink) 31 | const void* machHeader; // of dylib in mapped cached file 32 | const char* path; // of dylib 33 | const uuid_t* uuid; // of dylib, or NULL is missing 34 | // above fields all exist in version 1 35 | }; 36 | typedef struct dyld_shared_cache_dylib_info dyld_shared_cache_dylib_info; 37 | 38 | struct dyld_shared_cache_segment_info { 39 | uint64_t version; // initial version 1 40 | const char* name; // of segment 41 | uint64_t fileOffset; // of segment in cache file 42 | uint64_t fileSize; // of segment 43 | uint64_t address; // of segment when cache mapped with ASLR (sliding) off 44 | // above fields all exist in version 1 45 | }; 46 | typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info; 47 | 48 | 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | 53 | 54 | // Given a pointer and size of an in-memory copy of a dyld shared cache file, 55 | // this routine will call the callback block once for each segment in each dylib 56 | // in the shared cache file. 57 | // Returns -1 if there was an error, otherwise 0. 58 | extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, 59 | void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)); 60 | 61 | 62 | 63 | // 64 | // The following iterator functions are deprecated: 65 | // 66 | typedef void (^dyld_shared_cache_iterator_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress); 67 | typedef void (^dyld_shared_cache_iterator_slide_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide); 68 | typedef void (*dyld_shared_cache_iterator_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, void* userData); 69 | typedef void (*dyld_shared_cache_iterator_slide_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, uint64_t slide, void* userData); 70 | 71 | extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) __attribute__((deprecated)); 72 | extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) __attribute__((deprecated)); 73 | extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData) __attribute__((deprecated)); 74 | extern int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t callback, void* userData) __attribute__((deprecated)); 75 | 76 | 77 | #ifdef __cplusplus 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /Libraries/launch-cache/dyld_cache_format.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 | * 3 | * Copyright (c) 2006-2009 Apple Inc. All rights reserved. 4 | * 5 | * @APPLE_LICENSE_HEADER_START@ 6 | * 7 | * This file contains Original Code and/or Modifications of Original Code 8 | * as defined in and that are subject to the Apple Public Source License 9 | * Version 2.0 (the 'License'). You may not use this file except in 10 | * compliance with the License. Please obtain a copy of the License at 11 | * http://www.opensource.apple.com/apsl/ and read it before using this 12 | * file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 | * Please see the License for the specific language governing rights and 20 | * limitations under the License. 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | #ifndef __DYLD_CACHE_FORMAT__ 25 | #define __DYLD_CACHE_FORMAT__ 26 | 27 | #include 28 | #include 29 | //#include 30 | 31 | 32 | struct dyld_cache_header 33 | { 34 | char magic[16]; // e.g. "dyld_v0 i386" 35 | uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info 36 | uint32_t mappingCount; // number of dyld_cache_mapping_info entries 37 | uint32_t imagesOffset; // file offset to first dyld_cache_image_info 38 | uint32_t imagesCount; // number of dyld_cache_image_info entries 39 | uint64_t dyldBaseAddress; // base address of dyld when cache was built 40 | uint64_t codeSignatureOffset; // file offset of code signature blob 41 | uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) 42 | uint64_t slideInfoOffset; // file offset of kernel slid info 43 | uint64_t slideInfoSize; // size of kernel slid info 44 | uint64_t localSymbolsOffset; // file offset of where local symbols are stored 45 | uint64_t localSymbolsSize; // size of local symbols information 46 | uint8_t uuid[16]; // unique value for each shared cache file 47 | }; 48 | 49 | struct dyld_cache_mapping_info { 50 | uint64_t address; 51 | uint64_t size; 52 | uint64_t fileOffset; 53 | uint32_t maxProt; 54 | uint32_t initProt; 55 | }; 56 | 57 | struct dyld_cache_image_info 58 | { 59 | uint64_t address; 60 | uint64_t modTime; 61 | uint64_t inode; 62 | uint32_t pathFileOffset; 63 | uint32_t pad; 64 | }; 65 | 66 | struct dyld_cache_slide_info 67 | { 68 | uint32_t version; // currently 1 69 | uint32_t toc_offset; 70 | uint32_t toc_count; 71 | uint32_t entries_offset; 72 | uint32_t entries_count; 73 | uint32_t entries_size; // currently 128 74 | // uint16_t toc[toc_count]; 75 | // entrybitmap entries[entries_count]; 76 | }; 77 | 78 | 79 | struct dyld_cache_local_symbols_info 80 | { 81 | uint32_t nlistOffset; // offset into this chunk of nlist entries 82 | uint32_t nlistCount; // count of nlist entries 83 | uint32_t stringsOffset; // offset into this chunk of string pool 84 | uint32_t stringsSize; // byte count of string pool 85 | uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry 86 | uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array 87 | }; 88 | 89 | struct dyld_cache_local_symbols_entry 90 | { 91 | uint32_t dylibOffset; // offset in cache file of start of dylib 92 | uint32_t nlistStartIndex; // start index of locals for this dylib 93 | uint32_t nlistCount; // number of local symbols for this dylib 94 | }; 95 | 96 | 97 | 98 | #define MACOSX_DYLD_SHARED_CACHE_DIR "/var/db/dyld/" 99 | #define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" 100 | #define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" 101 | 102 | 103 | 104 | #endif // __DYLD_CACHE_FORMAT__ 105 | 106 | 107 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | make -f Makefile.x86_64 3 | make -f Makefile.arm 4 | lipo -create obj/libsymbolicate.dylib obj/macosx/libsymbolicate.dylib -output libsymbolicate.dylib 5 | mv libsymbolicate.dylib obj/libsymbolicate.dylib 6 | 7 | clean: 8 | make -f Makefile.x86_64 clean 9 | make -f Makefile.arm clean 10 | 11 | distclean: 12 | make -f Makefile.x86_64 distclean 13 | make -f Makefile.arm distclean 14 | 15 | package: build 16 | make -f Makefile.arm package 17 | 18 | sdk: 19 | make -f Makefile.arm sdk 20 | 21 | install: 22 | make -f Makefile.arm install 23 | -------------------------------------------------------------------------------- /Makefile.arm: -------------------------------------------------------------------------------- 1 | export ARCHS = armv6 arm64 2 | export SDKVERSION = 8.4 3 | export TARGET = iphone:clang 4 | export TARGET_IPHONEOS_DEPLOYMENT_VERSION = 3.0 5 | export ADDITIONAL_LDFLAGS = -Wl,-segalign,4000 6 | 7 | include Makefile.common 8 | -------------------------------------------------------------------------------- /Makefile.common: -------------------------------------------------------------------------------- 1 | LIBRARY_NAME = libsymbolicate 2 | PKG_ID = jp.ashikase.libsymbolicate 3 | 4 | libsymbolicate_INSTALL_PATH = /usr/lib 5 | libsymbolicate_OBJC_FILES = \ 6 | Libraries/launch-cache/dsc_iterator.cpp \ 7 | lib/SCBinaryInfo.mm \ 8 | lib/SCMethodInfo.mm \ 9 | lib/SCSymbolicator.mm \ 10 | lib/SCSymbolInfo.mm \ 11 | lib/binary.mm \ 12 | lib/demangle.mm \ 13 | lib/sharedCache.mm \ 14 | lib/methods.mm 15 | libsymbolicate_PRIVATE_FRAMEWORKS = CoreSymbolication Symbolication 16 | ADDITIONAL_CFLAGS = -DPKG_ID=\"$(PKG_ID)\" -ILibraries -Iinclude -Wno-unused-local-typedef 17 | 18 | include theos/makefiles/common.mk 19 | include $(THEOS)/makefiles/library.mk 20 | 21 | after-stage:: 22 | # Remove repository-related files. 23 | - find $(THEOS_STAGING_DIR) -name '.gitkeep' -delete 24 | # Copy header files to include directory. 25 | - mkdir -p $(THEOS_STAGING_DIR)/usr/include/$(LIBRARY_NAME)/ 26 | - cp $(THEOS_PROJECT_DIR)/include/*.h $(THEOS_STAGING_DIR)/usr/include/$(LIBRARY_NAME)/ 27 | 28 | distclean: clean 29 | - rm -f $(THEOS_PROJECT_DIR)/$(PKG_ID)*.deb 30 | - rm -f $(THEOS_PROJECT_DIR)/.theos/packages/* 31 | 32 | sdk: 33 | - mkdir -p $(THEOS)/include/$(LIBRARY_NAME)/ 34 | - cp $(THEOS_PROJECT_DIR)/include/*.h $(THEOS)/include/$(LIBRARY_NAME)/ 35 | - cp $(THEOS_OBJ_DIR)/$(LIBRARY_NAME).dylib $(THEOS)/lib/ 36 | -------------------------------------------------------------------------------- /Makefile.x86_64: -------------------------------------------------------------------------------- 1 | export ARCHS = x86_64 2 | export TARGET = macosx:clang 3 | export TARGET_MACOSX_DEPLOYMENT_VERSION = 10.6 4 | 5 | include Makefile.common 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | Crash log files generated by iOS's ReportCrash daemon do not contain symbol names, making them difficult to interpret. 5 | Symbolication is the act of looking up symbol names and adding them to these files. 6 | 7 | This project provides a library for parsing and symbolicating crash logs, both on and off device. 8 | 9 | Credit 10 | ===== 11 | 12 | This was originally based off of code used in [CrashReporter](http://code.google.com/p/networkpx/wiki/Using_CrashReporter), an iOS app by [kennytm](https://github.com/kennytm). 13 | 14 | This project also makes use of the following: 15 | * [RegexKitLite framework](http://regexkit.sourceforge.net) 16 | * [dyld](https://opensource.apple.com/source/dyld/dyld-239.4/) 17 | -------------------------------------------------------------------------------- /get_requirements.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Description: Script to fetch requirements for building this project. 4 | # Last-modified: 2013-02-20 14:31:57 5 | # 6 | # Note that this script is not perfect and does not handle all errors. 7 | # Any improvements are welcome. 8 | 9 | 10 | # Either Git or Subversion is needed to retrieve Theos. 11 | GIT=$(type -P git) 12 | SVN=$(type -P svn) 13 | if [ -z "$GIT" -a -z "$SVN" ]; then 14 | echo "ERROR: This script requires either 'git' or 'svn' to be installed." 15 | exit 1 16 | fi 17 | 18 | # Either wget or curl is needed to download package list and ldid. 19 | WGET=$(type -P wget) 20 | CURL=$(type -P curl) 21 | if [ -z "$WGET" -a -z "$CURL" ]; then 22 | echo "ERROR: This script requires either 'wget' or 'curl' to be installed." 23 | exit 1 24 | fi 25 | 26 | # Download Theos 27 | echo "Checking for Theos..." 28 | if [ -z "$THEOS" ]; then 29 | echo "Downloading Theos..." 30 | if [ ! -z "$GIT" ]; then 31 | git clone --quiet git://github.com/DHowett/theos.git theos 32 | else 33 | svn co http://svn.howett.net/svn/theos/trunk theos 34 | fi 35 | else 36 | ln -sn "$THEOS" theos 37 | fi 38 | 39 | # Download ldid 40 | echo "Checking for ldid..." 41 | if [ -z "$(type -P ldid)" ]; then 42 | echo "Downloading ldid..." 43 | if [ "$(uname)" == "Darwin" ]; then 44 | if [ ! -z "$WGET" ]; then 45 | wget -q http://dl.dropbox.com/u/3157793/ldid 46 | else 47 | curl -s http://dl.dropbox.com/u/3157793/ldid > ldid 48 | fi 49 | mv ldid theos/bin/ldid 50 | chmod +x theos/bin/ldid 51 | else 52 | echo "... No pre-built version of ldid is available for your system." 53 | echo "... You will need to provide your own copy of ldid." 54 | fi 55 | fi 56 | 57 | # Check if .deb creation tools are available (optional) 58 | echo "Checking for dpkg-deb..." 59 | if [ -z "$(type -P dpkg-deb)" ]; then 60 | echo "... dpkg-deb not found." 61 | echo "... If you wish to create a .deb package, you will need the 'dpkg-deb' tool." 62 | fi 63 | 64 | echo "Done." 65 | -------------------------------------------------------------------------------- /include/CoreSymbolication.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | // NOTE: The information in this file is based off of (and modified from): 11 | // https://github.com/mountainstorm/CoreSymbolication.git 12 | 13 | #ifndef SYMBOLICATE_CORESYMBOLICATION_H_ 14 | #define SYMBOLICATE_CORESYMBOLICATION_H_ 15 | 16 | typedef struct _CSTypeRef { 17 | void *csCppData; 18 | void *csCppObj; 19 | } CSTypeRef; 20 | 21 | typedef CSTypeRef CSSourceInfoRef; 22 | typedef CSTypeRef CSSymbolicatorRef; 23 | typedef CSTypeRef CSSymbolOwnerRef; 24 | typedef CSTypeRef CSSymbolRef; 25 | 26 | typedef struct _CSRange { 27 | unsigned long long location; 28 | unsigned long long length; 29 | } CSRange; 30 | 31 | typedef int (^CSSymbolIterator)(CSSymbolRef symbol); 32 | 33 | typedef struct _CSArchitecture { 34 | cpu_type_t cpu_type; 35 | cpu_subtype_t cpu_subtype; 36 | } CSArchitecture; 37 | 38 | #define kCSNow 0x80000000u 39 | 40 | extern "C" { 41 | // Allocation-related functions. 42 | Boolean CSIsNull(CSTypeRef cs); 43 | void CSRelease(CSTypeRef cs); 44 | 45 | // CSSourceInfo 46 | int CSSourceInfoGetLineNumber(CSSourceInfoRef info); 47 | const char * CSSourceInfoGetPath(CSSourceInfoRef info); 48 | 49 | // CSSymbolicator 50 | CSSymbolicatorRef CSSymbolicatorCreateWithPathAndArchitecture(const char *path, CSArchitecture arch); 51 | CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithUUIDAtTime(CSSymbolicatorRef symbolicator, CFUUIDRef uuid, uint64_t time); 52 | 53 | // CSSymbolOwner 54 | long CSSymbolOwnerForeachSymbol(CSSymbolOwnerRef owner, CSSymbolIterator block); 55 | uint64_t CSSymbolOwnerGetBaseAddress(CSSymbolOwnerRef owner); 56 | CSSourceInfoRef CSSymbolOwnerGetSourceInfoWithAddress(CSSymbolOwnerRef owner, uint64_t addr); 57 | CSSymbolRef CSSymbolOwnerGetSymbolWithAddress(CSSymbolOwnerRef owner, uint64_t addr); 58 | int CSSymbolOwnerIsAOut(CSSymbolOwnerRef owner); 59 | 60 | // CSSymbol 61 | const char * CSSymbolGetName(CSSymbolRef sym); 62 | CSRange CSSymbolGetRange(CSSymbolRef sym); 63 | Boolean CSSymbolIsObjcMethod(CSSymbolRef sym); 64 | Boolean CSSymbolIsFunction(CSSymbolRef sym); 65 | } 66 | 67 | #endif // SYMBOLICATE_CORESYMBOLICATION_H_ 68 | 69 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 70 | -------------------------------------------------------------------------------- /include/Headers.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #ifndef SYMBOLICATE_HEADERS_H_ 11 | #define SYMBOLICATE_HEADERS_H_ 12 | 13 | #endif // SYMBOLICATE_HEADERS_H_ 14 | 15 | /* vim: set ft=objc ff=unix sw=4 ts=4 tw=80 expandtab: */ 16 | -------------------------------------------------------------------------------- /include/SCBinaryInfo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #import 11 | 12 | #include "Headers.h" 13 | 14 | @class SCSymbolInfo; 15 | 16 | @interface SCBinaryInfo : NSObject 17 | @property(nonatomic, readonly) uint64_t address; 18 | @property(nonatomic, readonly) NSString *architecture; 19 | @property(nonatomic, readonly) uint64_t baseAddress; 20 | @property(nonatomic, readonly, getter = isEncrypted) BOOL encrypted; 21 | @property(nonatomic, readonly, getter = isExecutable) BOOL executable; 22 | @property(nonatomic, readonly, getter = isFromSharedCache) BOOL fromSharedCache; 23 | @property(nonatomic, readonly) NSArray *methods; 24 | @property(nonatomic, readonly) NSString *path; 25 | @property(nonatomic, readonly) NSString *uuid; 26 | @property(nonatomic, readonly) int64_t slide; 27 | @property(nonatomic, readonly) NSArray *symbolAddresses; 28 | - (id)initWithPath:(NSString *)path address:(uint64_t)address architecture:(NSString *)architecture uuid:(NSString *)uuid; 29 | - (SCSymbolInfo *)sourceInfoForAddress:(uint64_t)address; 30 | - (SCSymbolInfo *)symbolInfoForAddress:(uint64_t)address; 31 | @end 32 | 33 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 34 | -------------------------------------------------------------------------------- /include/SCMethodInfo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | @interface SCMethodInfo : NSObject 11 | @property(nonatomic, assign) uint64_t address; 12 | @property(nonatomic, copy) NSString *name; 13 | @end 14 | 15 | CFComparisonResult reversedCompareMethodInfos(SCMethodInfo *a, SCMethodInfo *b); 16 | 17 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 18 | -------------------------------------------------------------------------------- /include/SCSymbolInfo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | typedef struct _SCAddressRange { 11 | uint64_t location; 12 | uint64_t length; 13 | } SCAddressRange; 14 | 15 | @interface SCSymbolInfo : NSObject 16 | @property(nonatomic, copy) NSString *name; 17 | @property(nonatomic) SCAddressRange addressRange; 18 | @property(nonatomic) uint64_t offset; 19 | @property(nonatomic, copy) NSString *sourcePath; 20 | @property(nonatomic) NSUInteger sourceLineNumber; 21 | @end 22 | 23 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 24 | -------------------------------------------------------------------------------- /include/SCSymbolicator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | @class SCBinaryInfo; 11 | @class SCSymbolInfo; 12 | @class VMUMemory_File; 13 | 14 | @interface SCSymbolicator : NSObject 15 | @property(nonatomic, copy) NSString *architecture; 16 | @property(nonatomic, copy) NSDictionary *symbolMaps; 17 | @property(nonatomic, copy) NSString *systemRoot; 18 | @property(nonatomic, readonly) NSString *sharedCachePath; 19 | + (SCSymbolicator *)sharedInstance; 20 | - (SCSymbolInfo *)symbolInfoForAddress:(uint64_t)address inBinary:(SCBinaryInfo *)binaryInfo; 21 | @end 22 | 23 | /* vim: set ft=objc ff=unix sw=4 ts=4 tw=80 expandtab: */ 24 | -------------------------------------------------------------------------------- /include/binary.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #ifndef SYMBOLICATE_BINARY_H_ 11 | #define SYMBOLICATE_BINARY_H_ 12 | 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | BOOL offsetAndSizeOfBinaryInFile(const char *filepath, cpu_type_t cputype, cpu_subtype_t cpusubtype, off_t *offset, size_t *size); 20 | BOOL isEncrypted(const char *filepath, cpu_type_t cputype, cpu_subtype_t cpusubtype); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif // SYMBOLICATE_BINARY_H_ 27 | 28 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 29 | -------------------------------------------------------------------------------- /include/demangle.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #ifndef SYMBOLICATE_DEMANGLE_H_ 11 | #define SYMBOLICATE_DEMANGLE_H_ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | NSString *demangle(NSString *mangled); 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | 23 | #endif // SYMBOLICATE_DEMANGLE_H_ 24 | 25 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 26 | -------------------------------------------------------------------------------- /include/libsymbolicate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #import "SCBinaryInfo.h" 11 | #import "SCMethodInfo.h" 12 | #import "SCSymbolInfo.h" 13 | #import "SCSymbolicator.h" 14 | 15 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 16 | -------------------------------------------------------------------------------- /include/methods.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #ifndef SYMBOLICATE_METHODS_H_ 11 | #define SYMBOLICATE_METHODS_H_ 12 | 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | NSArray *methodsForBinaryFile(const char *filepath, cpu_type_t cputype, cpu_subtype_t cpusubtype); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif // SYMBOLICATE_METHODS_H_ 26 | 27 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 28 | -------------------------------------------------------------------------------- /include/sharedCache.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #ifndef SYMBOLICATE_LOCALSYMBOLS_H_ 11 | #define SYMBOLICATE_LOCALSYMBOLS_H_ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | uint64_t offsetOfDylibInSharedCache(const char *sharedCachePath, const char *filepath); 18 | const char *nameForLocalSymbol(const char *sharedCachePath, uint64_t dylibOffset, uint64_t symbolAddress); 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | 24 | #endif // SYMBOLICATE_LOCALSYMBOLS_H_ 25 | 26 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 27 | -------------------------------------------------------------------------------- /layout/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: jp.ashikase.libsymbolicate 2 | Name: libsymbolicate 3 | Version: 1.9.0 4 | Description: Library to symbolicate memory addresses. 5 | This library is based upon CrashReporter by kennytm. 6 | Architecture: iphoneos-arm 7 | Pre-Depends: firmware (>=4.0) 8 | Depiction: http://moreinfo.thebigboss.org/moreinfo/depiction.php?file=libsymbolicateData 9 | Homepage: http://github.com/ashikase/libsymbolicate 10 | Maintainer: BigBoss 11 | Author: Lance Fetters (ashikase) 12 | Sponsor: thebigboss.org 13 | Section: Development 14 | Tag: role::developer 15 | dev: ashikase 16 | -------------------------------------------------------------------------------- /lib/SCBinaryInfo.xm: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #import "SCBinaryInfo.h" 11 | 12 | #import "SCMethodInfo.h" 13 | #import "SCSymbolicator.h" 14 | #import "SCSymbolInfo.h" 15 | #import "binary.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include "CoreSymbolication.h" 21 | #include "methods.h" 22 | 23 | // ABI types. 24 | #ifndef CPU_ARCH_ABI64 25 | #define CPU_ARCH_ABI64 0x01000000 26 | #endif 27 | 28 | // CPU types. 29 | #ifndef CPU_TYPE_ARM 30 | #define CPU_TYPE_ARM 12 31 | #endif 32 | 33 | #ifndef CPU_TYPE_ARM64 34 | #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) 35 | #endif 36 | 37 | // ARM subtypes. 38 | #ifndef CPU_SUBTYPE_ARM_ALL 39 | #define CPU_SUBTYPE_ARM_ALL 0 40 | #endif 41 | 42 | #ifndef CPU_SUBTYPE_ARM_V6 43 | #define CPU_SUBTYPE_ARM_V6 6 44 | #endif 45 | 46 | #ifndef CPU_SUBTYPE_ARM_V7 47 | #define CPU_SUBTYPE_ARM_V7 9 48 | #endif 49 | 50 | #ifndef CPU_SUBTYPE_ARM_V7F 51 | #define CPU_SUBTYPE_ARM_V7F 10 // Cortex A9 52 | #endif 53 | 54 | #ifndef CPU_SUBTYPE_ARM_V7S 55 | #define CPU_SUBTYPE_ARM_V7S 11 // Swift 56 | #endif 57 | 58 | #ifndef CPU_SUBTYPE_ARM_V7K 59 | #define CPU_SUBTYPE_ARM_V7K 12 // Kirkwood40 60 | #endif 61 | 62 | // ARM64 subtypes. 63 | #ifndef CPU_SUBTYPE_ARM64_ALL 64 | #define CPU_SUBTYPE_ARM64_ALL 0 65 | #endif 66 | 67 | #ifndef CPU_SUBTYPE_ARM64_V8 68 | #define CPU_SUBTYPE_ARM64_V8 1 69 | #endif 70 | 71 | // NOTE: CoreSymbolication provides a similar function, but it is not available 72 | // in earlier versions of iOS. 73 | // TODO: Determine from which version the function is available. 74 | static CSArchitecture architectureForName(const char *name) { 75 | CSArchitecture arch; 76 | 77 | if (strcmp(name, "arm64") == 0) { 78 | arch.cpu_type = CPU_TYPE_ARM64; 79 | arch.cpu_subtype = CPU_SUBTYPE_ARM64_ALL; 80 | } else if ( 81 | (strcmp(name, "armv7s") == 0) || 82 | (strcmp(name, "armv7k") == 0) || 83 | (strcmp(name, "armv7f") == 0)) { 84 | arch.cpu_type = CPU_TYPE_ARM; 85 | arch.cpu_subtype = CPU_SUBTYPE_ARM_V7S; 86 | } else if (strcmp(name, "armv7") == 0) { 87 | arch.cpu_type = CPU_TYPE_ARM; 88 | arch.cpu_subtype = CPU_SUBTYPE_ARM_V7; 89 | } else if (strcmp(name, "armv6") == 0) { 90 | arch.cpu_type = CPU_TYPE_ARM; 91 | arch.cpu_subtype = CPU_SUBTYPE_ARM_V6; 92 | } else if (strcmp(name, "arm") == 0) { 93 | arch.cpu_type = CPU_TYPE_ARM; 94 | arch.cpu_subtype = CPU_SUBTYPE_ARM_ALL; 95 | } else { 96 | arch.cpu_type = 0; 97 | arch.cpu_subtype = 0; 98 | } 99 | 100 | return arch; 101 | } 102 | 103 | // NOTE: CFUUIDCreateFromString() does not support unhyphenated UUID strings. 104 | // UUID must be hyphenated, must follow pattern "8-4-4-4-12". 105 | CFUUIDRef CFUUIDCreateFromUnformattedCString(const char *string) { 106 | CFUUIDRef uuid = NULL; 107 | 108 | if (strlen(string) >= 32) { 109 | // Create buffer large enough to hold UUID, four hyphens, and null char. 110 | char buf[37]; 111 | 112 | unsigned i = 0; 113 | unsigned j = 0; 114 | 115 | for (; i < 8; ++i, ++j) { 116 | buf[j] = string[i]; 117 | } 118 | 119 | buf[j++] = '-'; 120 | 121 | for (; i < 12; ++i, ++j) { 122 | buf[j] = string[i]; 123 | } 124 | 125 | buf[j++] = '-'; 126 | 127 | for (; i < 16; ++i, ++j) { 128 | buf[j] = string[i]; 129 | } 130 | 131 | buf[j++] = '-'; 132 | 133 | for (; i < 20; ++i, ++j) { 134 | buf[j] = string[i]; 135 | } 136 | 137 | buf[j++] = '-'; 138 | 139 | for (; i < 32; ++i, ++j) { 140 | buf[j] = string[i]; 141 | } 142 | 143 | buf[j] = '\0'; 144 | 145 | CFStringRef stringRef = CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingASCII); 146 | if (stringRef != NULL) { 147 | uuid = CFUUIDCreateFromString(kCFAllocatorDefault, stringRef); 148 | CFRelease(stringRef); 149 | } 150 | } 151 | 152 | return uuid; 153 | } 154 | 155 | @implementation SCBinaryInfo { 156 | CSSymbolicatorRef symbolicator_; 157 | CSSymbolOwnerRef owner_; 158 | 159 | BOOL hasExtractedMethods_; 160 | BOOL hasExtractedOwner_; 161 | } 162 | 163 | @synthesize address = address_; 164 | @synthesize architecture = architecture_; 165 | @synthesize fromSharedCache = fromSharedCache_; 166 | @synthesize methods = methods_; 167 | @synthesize path = path_; 168 | @synthesize symbolAddresses = symbolAddresses_; 169 | @synthesize uuid = uuid_; 170 | 171 | @dynamic encrypted; 172 | @dynamic executable; 173 | @dynamic slide; 174 | 175 | #pragma mark - Creation & Destruction 176 | 177 | - (id)initWithPath:(NSString *)path address:(uint64_t)address architecture:(NSString *)architecture uuid:(NSString *)uuid { 178 | self = [super init]; 179 | if (self != nil) { 180 | path_ = [path copy]; 181 | address_ = address; 182 | architecture_ = [architecture copy]; 183 | uuid_ = [uuid copy]; 184 | } 185 | return self; 186 | } 187 | 188 | - (void)dealloc { 189 | if (!CSIsNull(symbolicator_)) { 190 | CSRelease(symbolicator_); 191 | } 192 | 193 | [architecture_ release]; 194 | [methods_ release]; 195 | [path_ release]; 196 | [uuid_ release]; 197 | [symbolAddresses_ release]; 198 | [super dealloc]; 199 | } 200 | 201 | #pragma mark - Properties 202 | 203 | // NOTE: This is the virtual address of the __TEXT segment. 204 | - (uint64_t)baseAddress { 205 | uint64_t baseAddress = 0; 206 | CSSymbolOwnerRef owner = [self owner]; 207 | if (!CSIsNull(owner)) { 208 | baseAddress = CSSymbolOwnerGetBaseAddress(owner); 209 | } 210 | return baseAddress; 211 | } 212 | 213 | - (BOOL)isEncrypted { 214 | CSArchitecture arch = architectureForName([[self architecture] UTF8String]); 215 | return isEncrypted([[self path] UTF8String], arch.cpu_type, arch.cpu_subtype); 216 | } 217 | 218 | - (BOOL)isExecutable { 219 | BOOL isExecutable = NO; 220 | 221 | CSSymbolOwnerRef owner = [self owner]; 222 | if (!CSIsNull(owner)) { 223 | isExecutable = (BOOL)CSSymbolOwnerIsAOut(owner); 224 | } 225 | 226 | return isExecutable; 227 | } 228 | 229 | // NOTE: This method is used when CoreSymbolication fails to find a name for a 230 | // symbol. Therefore, this method must not rely on CoreSymbolication. 231 | - (NSArray *)methods { 232 | if (methods_ == nil) { 233 | if (!hasExtractedMethods_) { 234 | hasExtractedMethods_ = YES; 235 | 236 | CSArchitecture arch = architectureForName([[self architecture] UTF8String]); 237 | methods_ = [methodsForBinaryFile([[self path] UTF8String], arch.cpu_type, arch.cpu_subtype) retain]; 238 | } 239 | } 240 | return methods_; 241 | } 242 | 243 | - (int64_t)slide { 244 | return ([self baseAddress] - [self address]); 245 | } 246 | 247 | // NOTE: The symbol addresses array is sorted greatest to least so that it can 248 | // be used with CFArrayBSearchValues(). 249 | - (NSArray *)symbolAddresses { 250 | if (symbolAddresses_ == nil) { 251 | NSMutableArray *addresses = [[NSMutableArray alloc] init]; 252 | 253 | CSSymbolOwnerRef owner = [self owner]; 254 | if (!CSIsNull(owner)) { 255 | CSSymbolOwnerForeachSymbol(owner, ^(CSSymbolRef symbol) { 256 | if (CSSymbolIsFunction(symbol)) { 257 | CSRange range = CSSymbolGetRange(symbol); 258 | NSNumber *symbolAddress = [[NSNumber alloc] initWithUnsignedLongLong:(range.location)]; 259 | [addresses addObject:symbolAddress]; 260 | [symbolAddress release]; 261 | } 262 | return 0; 263 | }); 264 | } 265 | NSArray *sortedAddresses = [addresses sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))CFNumberCompare context:NULL]; 266 | [addresses release]; 267 | 268 | NSMutableArray *reverseSortedAddresses = [[NSMutableArray alloc] init]; 269 | for (NSNumber *number in [sortedAddresses reverseObjectEnumerator]) { 270 | [reverseSortedAddresses addObject:number]; 271 | } 272 | symbolAddresses_ = reverseSortedAddresses; 273 | } 274 | return symbolAddresses_; 275 | } 276 | 277 | #pragma mark - Public Methods 278 | 279 | - (SCSymbolInfo *)sourceInfoForAddress:(uint64_t)address { 280 | SCSymbolInfo *symbolInfo = nil; 281 | 282 | const char *path = NULL; 283 | unsigned lineNumber = 0; 284 | 285 | CSSymbolOwnerRef owner = [self owner]; 286 | if (!CSIsNull(owner)) { 287 | CSSourceInfoRef sourceInfo = CSSymbolOwnerGetSourceInfoWithAddress(owner, address); 288 | if (!CSIsNull(sourceInfo)) { 289 | lineNumber = CSSourceInfoGetLineNumber(sourceInfo); 290 | path = CSSourceInfoGetPath(sourceInfo); 291 | } 292 | } 293 | 294 | if (path != nil) { 295 | symbolInfo = [[[SCSymbolInfo alloc] init] autorelease]; 296 | [symbolInfo setSourceLineNumber:lineNumber]; 297 | 298 | NSString *string = [[NSString alloc] initWithUTF8String:path]; 299 | [symbolInfo setSourcePath:string]; 300 | [string release]; 301 | } 302 | 303 | return symbolInfo; 304 | } 305 | 306 | - (SCSymbolInfo *)symbolInfoForAddress:(uint64_t)address { 307 | SCSymbolInfo *symbolInfo = nil; 308 | 309 | const char *name = NULL; 310 | SCAddressRange addressRange; 311 | 312 | CSSymbolOwnerRef owner = [self owner]; 313 | if (!CSIsNull(owner)) { 314 | CSSymbolRef symbol = CSSymbolOwnerGetSymbolWithAddress(owner, address); 315 | if (!CSIsNull(symbol)) { 316 | CSRange range = CSSymbolGetRange(symbol); 317 | addressRange = (SCAddressRange){range.location, range.length}; 318 | name = CSSymbolGetName(symbol); 319 | } 320 | } 321 | 322 | if (name != nil) { 323 | symbolInfo = [[[SCSymbolInfo alloc] init] autorelease]; 324 | [symbolInfo setAddressRange:addressRange]; 325 | 326 | NSString *string = [[NSString alloc] initWithUTF8String:name]; 327 | [symbolInfo setName:string]; 328 | [string release]; 329 | } 330 | 331 | return symbolInfo; 332 | } 333 | 334 | #pragma mark - Private Methods 335 | 336 | - (CSSymbolicatorRef)symbolicator { 337 | if (CSIsNull(symbolicator_)) { 338 | CSArchitecture arch = architectureForName([[self architecture] UTF8String]); 339 | if (arch.cpu_type != 0) { 340 | CSSymbolicatorRef symbolicator = CSSymbolicatorCreateWithPathAndArchitecture([[self path] UTF8String], arch); 341 | if (!CSIsNull(symbolicator)) { 342 | symbolicator_ = symbolicator; 343 | } 344 | } 345 | } 346 | 347 | return symbolicator_; 348 | } 349 | 350 | - (CSSymbolOwnerRef)owner { 351 | if (CSIsNull(owner_)) { 352 | if (!hasExtractedOwner_) { 353 | hasExtractedOwner_ = YES; 354 | 355 | CSSymbolicatorRef symbolicator = [self symbolicator]; 356 | if (!CSIsNull(symbolicator)) { 357 | // NOTE: Must ignore "<>" characters in UUID string. 358 | // FIXME: Do not store the UUID with these characters included. 359 | CFUUIDRef uuid = CFUUIDCreateFromUnformattedCString(&([[self uuid] UTF8String][1])); 360 | CSSymbolOwnerRef owner = CSSymbolicatorGetSymbolOwnerWithUUIDAtTime(symbolicator, uuid, kCSNow); 361 | if (!CSIsNull(owner)) { 362 | owner_ = owner; 363 | } else { 364 | fprintf(stderr, "WARNING: Device does not contain binary with matching UUID for file: %s\n", [[self path] UTF8String]); 365 | } 366 | CFRelease(uuid); 367 | } 368 | } 369 | } 370 | return owner_; 371 | } 372 | 373 | @end 374 | 375 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 376 | -------------------------------------------------------------------------------- /lib/SCMethodInfo.mm: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #import "SCMethodInfo.h" 11 | 12 | @implementation SCMethodInfo @end 13 | 14 | CFComparisonResult reversedCompareMethodInfos(SCMethodInfo *a, SCMethodInfo *b) { 15 | uint64_t aAddr = [a address]; 16 | uint64_t bAddr = [b address]; 17 | return (aAddr > bAddr) ? kCFCompareLessThan : (aAddr < bAddr) ? kCFCompareGreaterThan : kCFCompareEqualTo; 18 | } 19 | 20 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 21 | -------------------------------------------------------------------------------- /lib/SCSymbolInfo.mm: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #import "SCSymbolInfo.h" 11 | 12 | @implementation SCSymbolInfo 13 | 14 | @synthesize name = name_; 15 | @synthesize sourcePath = sourcePath_; 16 | 17 | - (void)dealloc { 18 | [name_ release]; 19 | [sourcePath_ release]; 20 | [super dealloc]; 21 | } 22 | 23 | @end 24 | 25 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 26 | -------------------------------------------------------------------------------- /lib/SCSymbolicator.xm: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #import "SCSymbolicator.h" 11 | 12 | #import "SCBinaryInfo.h" 13 | #import "SCMethodInfo.h" 14 | #import "SCSymbolInfo.h" 15 | 16 | #include 17 | #include 18 | #include "demangle.h" 19 | #include "sharedCache.h" 20 | 21 | @implementation SCSymbolicator 22 | 23 | @synthesize architecture = architecture_; 24 | @synthesize symbolMaps = symbolMaps_; 25 | @synthesize systemRoot = systemRoot_; 26 | 27 | + (instancetype)sharedInstance { 28 | static dispatch_once_t once; 29 | static id instance; 30 | dispatch_once(&once, ^{ 31 | instance = [self new]; 32 | }); 33 | return instance; 34 | } 35 | 36 | - (void)dealloc { 37 | [architecture_ release]; 38 | [symbolMaps_ release]; 39 | [systemRoot_ release]; 40 | [super dealloc]; 41 | } 42 | 43 | - (NSString *)architecture { 44 | return architecture_ ?: @"armv7"; 45 | } 46 | 47 | - (NSString *)systemRoot { 48 | return systemRoot_ ?: @"/"; 49 | } 50 | 51 | - (NSString *)sharedCachePath { 52 | NSString *sharedCachePath = @"/System/Library/Caches/com.apple.dyld/dyld_shared_cache_"; 53 | 54 | // Prepend the system root. 55 | sharedCachePath = [[self systemRoot] stringByAppendingPathComponent:sharedCachePath]; 56 | 57 | // Add the architecture and return. 58 | return [sharedCachePath stringByAppendingString:[self architecture]]; 59 | } 60 | 61 | CFComparisonResult reverseCompareUnsignedLongLong(CFNumberRef a, CFNumberRef b) { 62 | unsigned long long aValue; 63 | unsigned long long bValue; 64 | CFNumberGetValue(a, kCFNumberLongLongType, &aValue); 65 | CFNumberGetValue(b, kCFNumberLongLongType, &bValue); 66 | if (bValue < aValue) return kCFCompareLessThan; 67 | if (bValue > aValue) return kCFCompareGreaterThan; 68 | return kCFCompareEqualTo; 69 | } 70 | 71 | - (SCSymbolInfo *)symbolInfoForAddress:(uint64_t)address inBinary:(SCBinaryInfo *)binaryInfo { 72 | SCSymbolInfo *symbolInfo = nil; 73 | 74 | if (binaryInfo != nil) { 75 | address += [binaryInfo slide]; 76 | symbolInfo = [binaryInfo sourceInfoForAddress:address]; 77 | if (symbolInfo == nil) { 78 | // Determine symbol address. 79 | // NOTE: Only possible if LC_FUNCTION_STARTS exists in the binary. 80 | uint64_t symbolAddress = 0; 81 | NSArray *symbolAddresses = [binaryInfo symbolAddresses]; 82 | NSUInteger count = [symbolAddresses count]; 83 | if (count != 0) { 84 | NSNumber *targetAddress = [[NSNumber alloc] initWithUnsignedLongLong:address]; 85 | CFIndex matchIndex = CFArrayBSearchValues((CFArrayRef)symbolAddresses, CFRangeMake(0, count), targetAddress, (CFComparatorFunction)reverseCompareUnsignedLongLong, NULL); 86 | [targetAddress release]; 87 | if (matchIndex < (CFIndex)count) { 88 | symbolAddress = [[symbolAddresses objectAtIndex:matchIndex] unsignedLongLongValue]; 89 | } 90 | } 91 | 92 | // Attempt to retrieve symbol name and hex offset. 93 | // NOTE: (symbolAddress & ~1) is to account for Thumb. 94 | NSString *name = nil; 95 | uint64_t offset = 0; 96 | symbolInfo = [binaryInfo symbolInfoForAddress:address]; 97 | if (symbolInfo != nil && ([symbolInfo addressRange].location == (symbolAddress & ~1) || symbolAddress == 0)) { 98 | name = [symbolInfo name]; 99 | if ([name isEqualToString:@""]) { 100 | NSString *sharedCachePath = [self sharedCachePath]; 101 | if (sharedCachePath != nil) { 102 | // NOTE: In the past, the dylib offset was retrieved via 103 | // -[VMUMachOHeader address]. For some unknown 104 | // reason, the value retrieved using our own 105 | // function always differs by 0x200000. 106 | // TODO: Determine the reason for this difference. 107 | const char *cachePath = [sharedCachePath UTF8String]; 108 | uint64_t dylibOffset = offsetOfDylibInSharedCache(cachePath, [[binaryInfo path] UTF8String]); 109 | const char *localName = nameForLocalSymbol(cachePath, dylibOffset, [symbolInfo addressRange].location); 110 | if ((localName != NULL) && (strlen(localName) > 0)) { 111 | name = [NSString stringWithCString:localName encoding:NSASCIIStringEncoding]; 112 | } else { 113 | fprintf(stderr, "Unable to determine name for: %s, 0x%08llx\n", [[binaryInfo path] UTF8String], [symbolInfo addressRange].location); 114 | } 115 | } 116 | } 117 | // Attempt to demangle name 118 | // NOTE: It seems that Apple's demangler fails for some 119 | // names, so we attempt to do it ourselves. 120 | [symbolInfo setName:demangle(name)]; 121 | [symbolInfo setOffset:(address - [symbolInfo addressRange].location)]; 122 | } else { 123 | NSDictionary *symbolMap = [[self symbolMaps] objectForKey:[binaryInfo path]]; 124 | if (symbolMap != nil) { 125 | for (NSNumber *number in [[[symbolMap allKeys] sortedArrayUsingSelector:@selector(compare:)] reverseObjectEnumerator]) { 126 | uint64_t mapSymbolAddress = [number unsignedLongLongValue]; 127 | if (address > mapSymbolAddress) { 128 | name = demangle([symbolMap objectForKey:number]); 129 | offset = address - mapSymbolAddress; 130 | break; 131 | } 132 | } 133 | } else if (![binaryInfo isEncrypted]) { 134 | // Determine methods, attempt to match with symbol address. 135 | if (symbolAddress != 0) { 136 | SCMethodInfo *method = nil; 137 | NSArray *methods = [binaryInfo methods]; 138 | count = [methods count]; 139 | if (count != 0) { 140 | SCMethodInfo *targetMethod = [SCMethodInfo new]; 141 | [targetMethod setAddress:address]; 142 | CFIndex matchIndex = CFArrayBSearchValues((CFArrayRef)methods, CFRangeMake(0, count), targetMethod, (CFComparatorFunction)reversedCompareMethodInfos, NULL); 143 | [targetMethod release]; 144 | 145 | if (matchIndex < (CFIndex)count) { 146 | method = [methods objectAtIndex:matchIndex]; 147 | } 148 | } 149 | 150 | if (method != nil && [method address] >= symbolAddress) { 151 | name = [method name]; 152 | offset = address - [method address]; 153 | } else { 154 | uint64_t textStart = [binaryInfo baseAddress]; 155 | name = [NSString stringWithFormat:@"0x%08llx", (symbolAddress - textStart)]; 156 | offset = address - symbolAddress; 157 | } 158 | } 159 | } 160 | 161 | if (name != nil) { 162 | symbolInfo = [[[SCSymbolInfo alloc] init] autorelease]; 163 | [symbolInfo setName:name]; 164 | [symbolInfo setOffset:offset]; 165 | } 166 | } 167 | } 168 | } 169 | 170 | return symbolInfo; 171 | } 172 | 173 | @end 174 | 175 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 176 | -------------------------------------------------------------------------------- /lib/binary.mm: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #include "binary.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | BOOL offsetAndSizeOfBinaryInFile(const char *filepath, cpu_type_t cputype, cpu_subtype_t cpusubtype, off_t *offset, size_t *size) { 18 | off_t offsetOfBinary = 0; 19 | size_t sizeOfBinary = 0; 20 | 21 | // Open the file. 22 | int fd = open(filepath, O_RDONLY); 23 | if (fd < 0) { 24 | fprintf(stderr, "ERROR: Failed to open file: %s\n", filepath); 25 | return NO; 26 | } 27 | 28 | // Determine the file type. 29 | // NOTE: Both fat and mach-o file types (and all other such types, 30 | // presumably) start with a uint32_t sized "magic" type identifier. 31 | uint32_t magic; 32 | if (read(fd, &magic, sizeof(magic)) < 0) { 33 | fprintf(stderr, "ERROR: Failed to read magic for file: %s\n", filepath); 34 | close(fd); 35 | return NO; 36 | } 37 | 38 | // Determine offset and size of binary to map. 39 | if ((magic == FAT_MAGIC) || (magic == FAT_CIGAM)) { 40 | BOOL isSwapped = (magic == FAT_CIGAM); 41 | 42 | uint32_t nfat_arch; 43 | if (read(fd, &nfat_arch, sizeof(nfat_arch)) < 0) { 44 | fprintf(stderr, "ERROR: Failed to read number of binaries contained in fat file: %s\n", filepath); 45 | close(fd); 46 | return NO; 47 | } 48 | if (isSwapped) { 49 | nfat_arch = OSSwapInt32(nfat_arch); 50 | } 51 | 52 | size_t archsSize = nfat_arch * sizeof(fat_arch); 53 | fat_arch *archs = reinterpret_cast(malloc(archsSize)); 54 | if (archs != NULL) { 55 | if (read(fd, archs, archsSize) < 0) { 56 | fprintf(stderr, "ERROR: Failed to read architecture structs contained in fat file: %s\n", filepath); 57 | free(archs); 58 | close(fd); 59 | return NO; 60 | } 61 | 62 | // Get offset and size of binary matching requested architecture. 63 | for (uint32_t i = 0; i < nfat_arch; ++i) { 64 | cpu_type_t type = archs[i].cputype; 65 | cpu_subtype_t subtype = archs[i].cpusubtype; 66 | if (isSwapped) { 67 | type = OSSwapInt32(type); 68 | subtype = OSSwapInt32(subtype); 69 | } 70 | 71 | if ((type == cputype) && (subtype == cpusubtype)) { 72 | // TODO: Do we need to take the "align" member into account? 73 | offsetOfBinary = archs[i].offset; 74 | sizeOfBinary = archs[i].size; 75 | if (isSwapped) { 76 | offsetOfBinary = OSSwapInt32(offsetOfBinary); 77 | sizeOfBinary = OSSwapInt32(sizeOfBinary); 78 | } 79 | break; 80 | } 81 | } 82 | free(archs); 83 | 84 | if (sizeOfBinary > 0) { 85 | // Read magic of contained architecture. 86 | // NOTE: We want to reposition the offset of the file descriptor 87 | // for reading cpu type information later on. 88 | if (lseek(fd, offsetOfBinary, SEEK_SET) < 0) { 89 | fprintf(stderr, "ERROR: Failed to seek to offset of contained architecture in fat file: %s\n", filepath); 90 | close(fd); 91 | return NO; 92 | } 93 | 94 | if (read(fd, &magic, sizeof(magic)) < 0) { 95 | fprintf(stderr, "ERROR: Failed to read magic of contained architecture in fat file: %s\n", filepath); 96 | close(fd); 97 | return NO; 98 | } 99 | } else { 100 | fprintf(stderr, "ERROR: Requested architecture \"%u %u\" not found in fat file: %s\n", cputype, cpusubtype, filepath); 101 | close(fd); 102 | return NO; 103 | } 104 | } 105 | } 106 | 107 | // Confirm binary is Mach-O. 108 | if ((magic == MH_MAGIC_64) || (magic == MH_CIGAM_64) || (magic == MH_MAGIC) || (magic == MH_CIGAM)) { 109 | if (sizeOfBinary == 0) { 110 | // Set size to size of file. 111 | struct stat st; 112 | if (fstat(fd, &st) < 0) { 113 | fprintf(stderr, "ERROR: Failed to fstat() file: %s\n", filepath); 114 | close(fd); 115 | return NO; 116 | } 117 | sizeOfBinary = st.st_size;; 118 | } 119 | } else { 120 | fprintf(stderr, "ERROR: Unknown magic \"0x%x\"for binary in file: %s\n", magic, filepath); 121 | close(fd); 122 | return NO; 123 | } 124 | 125 | // Confirm binary matches the requested architecture. 126 | // NOTE: The first six members of 32-bit and 64-bit mach header have the 127 | // same name and type. 128 | cpu_type_t type; 129 | cpu_subtype_t subtype; 130 | BOOL isSwapped = ((magic == MH_CIGAM) || (magic == MH_CIGAM_64)); 131 | if ((read(fd, &type, sizeof(type)) < 0) || 132 | (read(fd, &subtype, sizeof(subtype)) < 0)) { 133 | fprintf(stderr, "ERROR: Failed to read cpu type information of binary in file: %s\n", filepath); 134 | close(fd); 135 | return NO; 136 | } 137 | if (isSwapped) { 138 | type = OSSwapInt32(type); 139 | subtype = OSSwapInt32(subtype); 140 | } 141 | if ((type != cputype) || (subtype != cpusubtype)) { 142 | fprintf(stderr, "ERROR: Requested architecture \"%u %u\" not found in file: %s\n", cputype, cpusubtype, filepath); 143 | close(fd); 144 | return NO; 145 | } 146 | 147 | // Clean-up. 148 | close(fd); 149 | 150 | // Output. 151 | if (offset != NULL) { 152 | *offset = offsetOfBinary; 153 | } 154 | if (size != NULL) { 155 | *size = sizeOfBinary; 156 | } 157 | 158 | return YES; 159 | } 160 | 161 | BOOL isEncrypted(const char *filepath, cpu_type_t cputype, cpu_subtype_t cpusubtype) { 162 | BOOL isEncrypted = NO; 163 | 164 | // Determine offset and size of the requested architecture in the file. 165 | // NOTE: File may contain multiple architectures, or incorrect architecture. 166 | off_t offset; 167 | size_t size; 168 | if (!offsetAndSizeOfBinaryInFile(filepath, cputype, cpusubtype, &offset, &size)) { 169 | fprintf(stderr, "ERROR: Failed to determine offset and size of requested architecture in file: %s\n", filepath); 170 | return nil; 171 | } 172 | 173 | // Open the file. 174 | int fd = open(filepath, O_RDONLY); 175 | if (fd < 0) { 176 | fprintf(stderr, "ERROR: Failed to open file: %s\n", filepath); 177 | return nil; 178 | } 179 | 180 | // Map the binary. 181 | void *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset); 182 | close(fd); 183 | 184 | // Determine encryption status. 185 | if (data != MAP_FAILED) { 186 | // Determine if requested architecture is 32-bit or 64-bit. 187 | BOOL is32Bit = !(cputype & CPU_ARCH_ABI64); 188 | size_t headerSize = (is32Bit ? sizeof(mach_header) : sizeof(mach_header_64)); 189 | 190 | // Search for encryption info load command. 191 | uint8_t *memory = reinterpret_cast(data); 192 | mach_header *header = reinterpret_cast(memory); 193 | uint32_t ncmds = header->ncmds; 194 | load_command *cmd = reinterpret_cast(memory + headerSize); 195 | for (uint32_t i = 0; i < ncmds; ++i) { 196 | if ((cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64)) { 197 | // NOTE: Both 32-bit and 64-bit encryption info structs are the 198 | // same, except for padding at the end. 199 | encryption_info_command *enc = reinterpret_cast(cmd); 200 | isEncrypted = (enc->cryptid != 0); 201 | break; 202 | } 203 | 204 | // Prepare next command. 205 | cmd = reinterpret_cast(reinterpret_cast(cmd) + cmd->cmdsize); 206 | } 207 | 208 | munmap(data, size); 209 | } else { 210 | fprintf(stderr, "ERROR: Failed to mmap file: %s\n", filepath); 211 | } 212 | 213 | return isEncrypted; 214 | } 215 | 216 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 217 | -------------------------------------------------------------------------------- /lib/demangle.mm: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #import "demangle.h" 11 | 12 | #include 13 | 14 | /* 15 | Demangle names generated by Logos preprocessor. 16 | Original code provided by Dustin Howett (creator of Logos). 17 | 18 | Supports the following formats: 19 | _logos_ 20 | static_metaclass_lookup$CLASS 21 | static_class_lookup$CLASS 22 | meta_ 23 | orig$GROUP$CLASS$SELECTOR$PARTS$ 24 | method$GROUP$CLASS$SELECTOR$PARTS$ 25 | super$GROUP$CLASS$SELECTOR$PARTS$ 26 | orig$GROUP$CLASS$SELECTOR$PARTS$ 27 | method$GROUP$CLASS$SELECTOR$PARTS$ 28 | super$GROUP$CLASS$SELECTOR$PARTS$ 29 | */ 30 | static NSString *logos_demangle(NSString *mangled) { 31 | NSString *symname = nil; 32 | if ([mangled hasPrefix:@"__Z"]) { 33 | const char *temp = [mangled UTF8String]; 34 | char *charp = NULL; 35 | int symlen = 0; 36 | symlen = (int)strtoul(temp+4, &charp, 10); 37 | symname = [[[NSString alloc] initWithBytes:charp length:symlen encoding:NSUTF8StringEncoding] autorelease]; 38 | } else if ([mangled hasPrefix:@"__l"]) { 39 | symname = [mangled substringFromIndex:1]; 40 | } else { 41 | symname = [[mangled copy] autorelease]; 42 | } 43 | if (![symname hasPrefix:@"_logos"]) return mangled; 44 | 45 | NSString *stripped = nil; 46 | NSInteger paren = [symname rangeOfString:@"("].location; 47 | if (paren != NSNotFound) { 48 | stripped = [symname substringFromIndex:paren]; 49 | symname = [symname substringToIndex:paren]; 50 | } 51 | 52 | NSArray *comp = [symname componentsSeparatedByString:@"$"]; 53 | 54 | // Lookup Functions -- always have two or more components 55 | if (comp.count < 2) return mangled; 56 | NSString *sigil = [[comp objectAtIndex:0] substringFromIndex:7]; 57 | BOOL metabit = NO; 58 | if ([sigil hasSuffix:@"lookup"]) { 59 | metabit = [sigil hasPrefix:@"static_meta"]; 60 | return [NSString stringWithFormat:@"Logos class lookup for %c%@%@", metabit ? '+' : '-', [comp objectAtIndex:1], stripped ?: @""]; 61 | } 62 | 63 | // Hook Functions -- always have three or more components 64 | if (comp.count < 3) return mangled; 65 | if ([sigil hasPrefix:@"meta"]) { 66 | metabit = true; 67 | sigil = [sigil substringFromIndex:5]; 68 | } 69 | 70 | NSString *type = nil; 71 | if ([sigil isEqualToString:@"method"]) { 72 | type = @"hook"; 73 | } else if ([sigil isEqualToString:@"super"]) { 74 | type = @"supercall thunk"; 75 | } 76 | if (type == nil) return mangled; 77 | 78 | NSString *group = [comp objectAtIndex:1]; 79 | NSString *klass = [comp objectAtIndex:2]; 80 | NSString *selector = [[comp subarrayWithRange:(NSRange){3, comp.count - 3}] componentsJoinedByString:@":"]; 81 | return [NSString stringWithFormat:@"Logos %@ for %c[%@(%@) %@]%@", type, metabit ? '+' : '-', klass, group, selector, stripped ?: @""]; 82 | } 83 | 84 | NSString *demangle(NSString *mangled) { 85 | NSString *demangled = mangled; 86 | 87 | const char *before = [mangled cStringUsingEncoding:NSASCIIStringEncoding]; 88 | if (strlen(before) > 0) { 89 | // NOTE: When attempting to demangle name, skip initial underscore. 90 | int status; 91 | char *after = abi::__cxa_demangle(before + 1, NULL, NULL, &status); 92 | if (after != NULL) { 93 | demangled = [NSString stringWithCString:after encoding:NSASCIIStringEncoding]; 94 | } 95 | free(after); 96 | } 97 | 98 | return logos_demangle(demangled); 99 | } 100 | 101 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 102 | -------------------------------------------------------------------------------- /lib/methods.mm: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #include "methods.h" 11 | 12 | #include 13 | #include 14 | 15 | #import "SCMethodInfo.h" 16 | #import "binary.h" 17 | 18 | #define RO_META (1 << 0) 19 | #define RW_FUTURE (1 << 30) 20 | #define RW_REALIZED (1 << 31) 21 | 22 | // Declare necessary types. 23 | // NOTE: Type information is from objc-runtime-new.h (from objc4-551.1 source). 24 | // Struct declarations have been altered to allow accessing only required 25 | // information, and to differentiate 32 and 64 bit versions. 26 | 27 | struct objc_class { 28 | uint32_t isa; // Actually declared in superstruct objc_object. 29 | uint32_t superclass; 30 | uint32_t buckets; // Part of cache_t struct. 31 | uint16_t shiftmask; // Part of cache_t struct. 32 | uint16_t occupied; // Part of cache_t struct. 33 | uint32_t data_NEVER_USE; // class_rw/ro_t * plus custom rr/alloc flags 34 | 35 | #define CLASS_FAST_FLAG_MASK 3 36 | uint32_t data() { 37 | return (data_NEVER_USE & ~CLASS_FAST_FLAG_MASK); 38 | } 39 | }; 40 | 41 | struct objc_class_64 { 42 | uint64_t isa; // Actually declared in superstruct objc_object. 43 | uint64_t superclass; 44 | uint64_t buckets; // Part of cache_t struct. 45 | uint32_t shiftmask; // Part of cache_t struct. 46 | uint32_t occupied; // Part of cache_t struct. 47 | uint64_t data_NEVER_USE; // class_rw/ro_t * plus custom rr/alloc flags 48 | 49 | #define CLASS_FAST_FLAG_MASK 3 50 | uint64_t data() { 51 | return (data_NEVER_USE & ~CLASS_FAST_FLAG_MASK); 52 | } 53 | }; 54 | 55 | struct class_ro_t { 56 | uint32_t flags; 57 | uint32_t instanceStart; 58 | uint32_t instanceSize; 59 | uint32_t ivarLayout; 60 | uint32_t name; 61 | uint32_t baseMethods; 62 | // ... 63 | }; 64 | 65 | struct class_ro_64_t { 66 | uint32_t flags; 67 | uint32_t instanceStart; 68 | uint32_t instanceSize; 69 | uint32_t reserved; 70 | uint64_t ivarLayout; 71 | uint64_t name; 72 | uint64_t baseMethods; 73 | // ... 74 | }; 75 | 76 | struct method_t { 77 | uint32_t name; 78 | uint32_t types; 79 | uint32_t imp; 80 | }; 81 | 82 | struct method_64_t { 83 | uint64_t name; 84 | uint64_t types; 85 | uint64_t imp; 86 | }; 87 | 88 | // NOTE: The first three fields of segment_command and segment_command_64, the 89 | // only fields that we need for this function, are the same name and size. 90 | // Therefore, it is not necessary to create separate 32/64 bit versions. 91 | static segment_command *segmentNamed(load_command *cmds, uint32_t ncmds, const char *name) { 92 | segment_command *segment = NULL; 93 | 94 | load_command *cmd = cmds; 95 | for (uint32_t i = 0; i < ncmds; ++i) { 96 | if ((cmd->cmd == LC_SEGMENT) || (cmd->cmd == LC_SEGMENT_64)) { 97 | segment_command *seg = reinterpret_cast(cmd); 98 | if (strcmp(seg->segname, name) == 0) { 99 | segment = seg; 100 | break; 101 | } 102 | } 103 | 104 | // Prepare next command. 105 | cmd = reinterpret_cast(reinterpret_cast(cmd) + cmd->cmdsize); 106 | } 107 | 108 | return segment; 109 | } 110 | 111 | static section *sectionNamed32(segment_command *segment, const char *name) { 112 | section *section = NULL; 113 | 114 | struct section *sect = reinterpret_cast(reinterpret_cast(segment) + sizeof(segment_command)); 115 | for (uint32_t i = 0; i < segment->nsects; ++i, ++sect) { 116 | if (strcmp(sect->sectname, name) == 0) { 117 | section = sect; 118 | break; 119 | } 120 | } 121 | 122 | return section; 123 | } 124 | 125 | static section_64 *sectionNamed64(segment_command_64 *segment, const char *name) { 126 | section_64 *section = NULL; 127 | 128 | struct section_64 *sect = reinterpret_cast(reinterpret_cast(segment) + sizeof(segment_command_64)); 129 | for (uint32_t i = 0; i < segment->nsects; ++i, ++sect) { 130 | if (strcmp(sect->sectname, name) == 0) { 131 | section = sect; 132 | break; 133 | } 134 | } 135 | 136 | return section; 137 | } 138 | 139 | static NSArray *methodsForMappedMemory32(uint8_t *memory) { 140 | NSMutableArray *methods = [NSMutableArray array]; 141 | 142 | if (memory != NULL) { 143 | mach_header *header = reinterpret_cast(memory); 144 | uint32_t ncmds = header->ncmds; 145 | 146 | // XXX: For normal binaries (not from shared cache), will there 147 | // ever be a time where the segments are not contiguous in virtual 148 | // memory space? 149 | load_command *cmds = reinterpret_cast(memory + sizeof(mach_header)); 150 | 151 | segment_command *textSeg = segmentNamed(cmds, ncmds, "__TEXT"); 152 | if (textSeg == NULL) { 153 | fprintf(stderr, "ERROR: Segment \"__TEXT\" not found.\n"); 154 | return nil; 155 | } 156 | int32_t vmdiff_text = textSeg->vmaddr - textSeg->fileoff; 157 | 158 | segment_command *dataSeg = segmentNamed(cmds, ncmds, "__DATA"); 159 | if (dataSeg == NULL) { 160 | fprintf(stderr, "ERROR: Segment \"__DATA\" not found.\n"); 161 | return nil; 162 | } 163 | int32_t vmdiff_data = dataSeg->vmaddr - dataSeg->fileoff; 164 | 165 | section *objcClassListSect = sectionNamed32(dataSeg, "__objc_classlist__DATA"); 166 | if (objcClassListSect == NULL) { 167 | // NOTE: File may not contain any Objective-C classes. 168 | fprintf(stderr, "INFO: Section \"__objc_classlist__DATA\" not found.\n"); 169 | return nil; 170 | } 171 | 172 | section *objcDataSect = sectionNamed32(dataSeg, "__objc_data"); 173 | if (objcDataSect == NULL) { 174 | // NOTE: File may not contain any Objective-C classes. 175 | fprintf(stderr, "INFO: Section \"__objc_data\" not found.\n"); 176 | return nil; 177 | } 178 | 179 | uint32_t *classList = reinterpret_cast(memory + objcClassListSect->offset); 180 | const uint32_t numClasses = objcClassListSect->size / sizeof(uint32_t); 181 | for (uint32_t i = 0; i < numClasses; ++i) { 182 | objc_class *klass = reinterpret_cast(memory + classList[i] - vmdiff_data); 183 | 184 | process_class: 185 | class_ro_t *klass_ro = reinterpret_cast(memory + klass->data() - vmdiff_data); 186 | 187 | // Confirm struct is actually class_ro_t (and not class_rw_t). 188 | // NOTE: A "realized" or "future" class will be class_rw_t. 189 | // XXX: It is assumed that these flags will only be true for 190 | // dynamically created classes. 191 | const uint32_t flags = klass_ro->flags; 192 | if (!(flags & RW_REALIZED) && !(flags & RW_FUTURE)) { 193 | const char methodType = (flags & 1) ? '+' : '-'; 194 | const char *className = reinterpret_cast(memory + klass_ro->name - vmdiff_text); 195 | 196 | if (klass_ro->baseMethods != 0) { 197 | uint32_t *baseMethods = reinterpret_cast(memory + klass_ro->baseMethods - vmdiff_data); 198 | BOOL isPreoptimized = (baseMethods[0] & 3); 199 | //const uint32_t entsize = baseMethods[0] & ~(uint32_t)3; 200 | const uint32_t count = baseMethods[1]; 201 | 202 | method_t *methodEntries = reinterpret_cast(&baseMethods[2]); 203 | for (uint32_t j = 0; j < count; ++j) { 204 | const char *methodName = NULL; 205 | if (isPreoptimized) { 206 | methodName = reinterpret_cast(methodEntries[j].name); 207 | } else { 208 | methodName = reinterpret_cast(memory + methodEntries[j].name - vmdiff_text); 209 | } 210 | NSString *name = [[NSString alloc] initWithFormat:@"%c[%s %s]", methodType, className, methodName]; 211 | 212 | SCMethodInfo *mi = [SCMethodInfo new]; 213 | [mi setName:name]; 214 | [mi setAddress:methodEntries[j].imp]; 215 | [methods addObject:mi]; 216 | [mi release]; 217 | 218 | [name release]; 219 | } 220 | } 221 | } 222 | 223 | if (!(flags & RO_META)) { 224 | // Process meta class. 225 | // NOTE: This is needed for retrieving class (non-instance) methods. 226 | klass = reinterpret_cast(memory + klass->isa - vmdiff_data); 227 | goto process_class; 228 | } 229 | } 230 | } 231 | 232 | return methods; 233 | } 234 | 235 | static NSArray *methodsForMappedMemory64(uint8_t *memory) { 236 | NSMutableArray *methods = [NSMutableArray array]; 237 | 238 | if (memory != NULL) { 239 | mach_header_64 *header = reinterpret_cast(memory); 240 | uint32_t ncmds = header->ncmds; 241 | 242 | // XXX: For normal binaries (not from shared cache), will there 243 | // ever be a time where the segments are not contiguous in virtual 244 | // memory space? 245 | load_command *cmds = reinterpret_cast(memory + sizeof(mach_header_64)); 246 | 247 | segment_command_64 *textSeg = reinterpret_cast(segmentNamed(cmds, ncmds, "__TEXT")); 248 | if (textSeg == NULL) { 249 | fprintf(stderr, "ERROR: Segment \"__TEXT\" not found.\n"); 250 | return nil; 251 | } 252 | int64_t vmdiff_text = textSeg->vmaddr - textSeg->fileoff; 253 | 254 | segment_command_64 *dataSeg = reinterpret_cast(segmentNamed(cmds, ncmds, "__DATA")); 255 | if (dataSeg == NULL) { 256 | fprintf(stderr, "ERROR: Segment \"__DATA\" not found.\n"); 257 | return nil; 258 | } 259 | int64_t vmdiff_data = dataSeg->vmaddr - dataSeg->fileoff; 260 | 261 | section_64 *objcClassListSect = sectionNamed64(dataSeg, "__objc_classlist__DATA"); 262 | if (objcClassListSect == NULL) { 263 | // NOTE: File may not contain any Objective-C classes. 264 | fprintf(stderr, "INFO: Section \"__objc_classlist__DATA\" not found.\n"); 265 | return nil; 266 | } 267 | 268 | section_64 *objcDataSect = sectionNamed64(dataSeg, "__objc_data"); 269 | if (objcDataSect == NULL) { 270 | // NOTE: File may not contain any Objective-C classes. 271 | fprintf(stderr, "INFO: Section \"__objc_data\" not found.\n"); 272 | return nil; 273 | } 274 | 275 | uint64_t *classList = reinterpret_cast(memory + objcClassListSect->offset); 276 | const uint64_t numClasses = objcClassListSect->size / sizeof(uint64_t); 277 | for (uint64_t i = 0; i < numClasses; ++i) { 278 | objc_class_64 *klass = reinterpret_cast(memory + classList[i] - vmdiff_data); 279 | 280 | process_class: 281 | class_ro_64_t *klass_ro = reinterpret_cast(memory + klass->data() - vmdiff_data); 282 | 283 | // Confirm struct is actually class_ro_t (and not class_rw_t). 284 | // NOTE: A "realized" or "future" class will be class_rw_t. 285 | // XXX: It is assumed that these flags will only be true for 286 | // dynamically created classes. 287 | const uint32_t flags = klass_ro->flags; 288 | if (!(flags & RW_REALIZED) && !(flags & RW_FUTURE)) { 289 | const char methodType = (flags & 1) ? '+' : '-'; 290 | const char *className = reinterpret_cast(memory + klass_ro->name - vmdiff_text); 291 | 292 | if (klass_ro->baseMethods != 0) { 293 | uint32_t *baseMethods = reinterpret_cast(memory + klass_ro->baseMethods - vmdiff_data); 294 | BOOL isPreoptimized = (baseMethods[0] & 3); 295 | //const uint32_t entsize = baseMethods[0] & ~(uint32_t)3; 296 | const uint32_t count = baseMethods[1]; 297 | 298 | method_64_t *methodEntries = reinterpret_cast(&baseMethods[2]); 299 | for (uint32_t j = 0; j < count; ++j) { 300 | const char *methodName = NULL; 301 | if (isPreoptimized) { 302 | methodName = reinterpret_cast(methodEntries[j].name); 303 | } else { 304 | methodName = reinterpret_cast(memory + methodEntries[j].name - vmdiff_text); 305 | } 306 | NSString *name = [[NSString alloc] initWithFormat:@"%c[%s %s]", methodType, className, methodName]; 307 | 308 | SCMethodInfo *mi = [SCMethodInfo new]; 309 | [mi setName:name]; 310 | [mi setAddress:methodEntries[j].imp]; 311 | [methods addObject:mi]; 312 | [mi release]; 313 | 314 | [name release]; 315 | } 316 | } 317 | } 318 | 319 | if (!(flags & RO_META)) { 320 | // Process meta class. 321 | // NOTE: This is needed for retrieving class (non-instance) methods. 322 | klass = reinterpret_cast(memory + klass->isa - vmdiff_data); 323 | goto process_class; 324 | } 325 | } 326 | } 327 | 328 | return methods; 329 | } 330 | 331 | NSArray *methodsForBinaryFile(const char *filepath, cpu_type_t cputype, cpu_subtype_t cpusubtype) { 332 | NSArray *methods = nil; 333 | 334 | // Determine offset and size of the requested architecture in the file. 335 | // NOTE: File may contain multiple architectures, or incorrect architecture. 336 | off_t offset; 337 | size_t size; 338 | if (!offsetAndSizeOfBinaryInFile(filepath, cputype, cpusubtype, &offset, &size)) { 339 | fprintf(stderr, "ERROR: Failed to determine offset and size of requested architecture in file: %s\n", filepath); 340 | return nil; 341 | } 342 | 343 | // Open the file. 344 | int fd = open(filepath, O_RDONLY); 345 | if (fd < 0) { 346 | fprintf(stderr, "ERROR: Failed to open file: %s\n", filepath); 347 | return nil; 348 | } 349 | 350 | // Map the binary. 351 | void *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset); 352 | close(fd); 353 | 354 | // Extract the methods. 355 | if (data != MAP_FAILED) { 356 | // Determine if requested architecture is 32-bit or 64-bit. 357 | uint8_t *memory = reinterpret_cast(data); 358 | BOOL is32Bit = !(cputype & CPU_ARCH_ABI64); 359 | if (is32Bit) { 360 | methods = methodsForMappedMemory32(memory); 361 | } else { 362 | methods = methodsForMappedMemory64(memory); 363 | } 364 | 365 | if ([methods count] == 0) { 366 | fprintf(stderr, "WARNING: Unable to extract methods or no methods exist in file: %s\n", filepath); 367 | } 368 | 369 | munmap(data, size); 370 | } else { 371 | fprintf(stderr, "ERROR: Failed to mmap file: %s\n", filepath); 372 | } 373 | 374 | return [methods sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))reversedCompareMethodInfos context:NULL]; 375 | } 376 | 377 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 378 | -------------------------------------------------------------------------------- /lib/sharedCache.mm: -------------------------------------------------------------------------------- 1 | /** 2 | * Name: libsymbolicate 3 | * Type: iOS/OS X shared library 4 | * Desc: Library for symbolicating memory addresses. 5 | * 6 | * Author: Lance Fetters (aka. ashikase) 7 | * License: LGPL v3 (See LICENSE file for details) 8 | */ 9 | 10 | #include "sharedCache.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | uint64_t offsetOfDylibInSharedCache(const char *sharedCachePath, const char *filepath) { 19 | uint64_t offset = 0; 20 | 21 | int fd = open(sharedCachePath, O_RDONLY); 22 | if (fd < 0) { 23 | fprintf(stderr, "ERROR: Failed to open shared cache file: %s\n", sharedCachePath); 24 | return 0; 25 | } 26 | 27 | size_t headerSize = sizeof(dyld_cache_header); 28 | dyld_cache_header *header = reinterpret_cast(malloc(headerSize)); 29 | if (read(fd, header, headerSize) < 0) { 30 | fprintf(stderr, "ERROR: Failed to read header for shared cache file: %s\n", sharedCachePath); 31 | free(header); 32 | close(fd); 33 | return 0; 34 | } 35 | const uint32_t imagesOffset = header->imagesOffset; 36 | const uint32_t imagesCount = header->imagesCount; 37 | const uint64_t dyldBaseAddress = header->dyldBaseAddress; 38 | free(header); 39 | 40 | // Adjust for page size. 41 | // NOTE: mmap() may fail if offset is not page-aligned. 42 | const int pagesize = getpagesize(); 43 | const uint32_t imagesPage = imagesOffset / pagesize; 44 | const off_t imagesPageOffset = imagesPage * pagesize; 45 | const size_t imagesLen = (imagesOffset - imagesPageOffset) + (imagesCount * sizeof(dyld_cache_image_info)); 46 | 47 | void *data = mmap(NULL, imagesLen, PROT_READ, MAP_PRIVATE, fd, imagesPageOffset); 48 | if (data != MAP_FAILED) { 49 | uint8_t *memory = reinterpret_cast(data); 50 | 51 | dyld_cache_image_info *images = reinterpret_cast(memory + (imagesOffset - imagesPageOffset)); 52 | for (uint32_t i = 0; i < imagesCount; ++i) { 53 | // NOTE: The maximum allowed path length is 1024 bytes. 54 | // (According to /usr/include/sys/syslimits.h) 55 | const uint32_t pathFileOffset = images[i].pathFileOffset; 56 | const uint32_t pathFilePage = pathFileOffset / pagesize; 57 | const off_t pathFilePageOffset = pathFilePage * pagesize; 58 | const size_t pathFileLen = (pathFileOffset - pathFilePageOffset) + 1024; 59 | void *pathFile = mmap(NULL, pathFileLen, PROT_READ, MAP_PRIVATE, fd, pathFilePageOffset); 60 | if (pathFile != MAP_FAILED) { 61 | const char *path = reinterpret_cast(reinterpret_cast(pathFile) + (pathFileOffset - pathFilePageOffset)); 62 | if (strcmp(filepath, path) == 0) { 63 | offset = (images[i].address - dyldBaseAddress); 64 | munmap(pathFile, pathFileLen); 65 | break; 66 | } else { 67 | munmap(pathFile, pathFileLen); 68 | } 69 | } else { 70 | fprintf(stderr, "ERROR: Failed to mmap image path portion of shared cache file: %s\n", sharedCachePath); 71 | } 72 | } 73 | 74 | munmap(data, imagesLen); 75 | } else { 76 | fprintf(stderr, "ERROR: Failed to mmap image infos portion of shared cache file: %s\n", sharedCachePath); 77 | } 78 | 79 | close(fd); 80 | 81 | return offset; 82 | } 83 | 84 | // NOTE: This function uses static storage, meaning that it is not thread safe. 85 | // The alternative would be to return a dynamically-allocated string, but 86 | // that would require the caller to free it when done using it. 87 | const char *nameForLocalSymbol(const char *sharedCachePath, uint64_t dylibOffset, uint64_t symbolAddress) { 88 | // TODO: Determine max allowed length for symbol names (if such a limit exists). 89 | static char name[1025]; 90 | 91 | int fd = open(sharedCachePath, O_RDONLY); 92 | if (fd < 0) { 93 | fprintf(stderr, "ERROR: Failed to open shared cache file: %s\n", sharedCachePath); 94 | return NULL; 95 | } 96 | 97 | size_t headerSize = sizeof(dyld_cache_header); 98 | dyld_cache_header *header = reinterpret_cast(malloc(headerSize)); 99 | if (read(fd, header, headerSize) < 0) { 100 | fprintf(stderr, "ERROR: Failed to read header for shared cache file: %s\n", sharedCachePath); 101 | free(header); 102 | close(fd); 103 | return NULL; 104 | } 105 | // NOTE: Local symbol offset/size fields did not exist in earlier firmware. 106 | // TODO: At what point were they introduced? 107 | if (header->mappingOffset < sizeof(dyld_cache_header)) return nil; 108 | const BOOL is64Bit = (strstr(header->magic, "arm64") != NULL); 109 | const uint64_t localSymbolsOffset = header->localSymbolsOffset; 110 | const uint64_t localSymbolsSize = header->localSymbolsSize; 111 | free(header); 112 | 113 | // Adjust for page size. 114 | // NOTE: mmap() may fail if offset is not page-aligned. 115 | const int pagesize = getpagesize(); 116 | const uint32_t localSymbolsPage = localSymbolsOffset / pagesize; 117 | const off_t localSymbolsPageOffset = localSymbolsPage * pagesize; 118 | const size_t localSymbolsLen = (localSymbolsOffset - localSymbolsPageOffset) + localSymbolsSize; 119 | 120 | // Adjust dylib offset. 121 | // FIXME: As mentioned in SCSymbolicator, the passed value of dylib offset 122 | // is not quite correct. Must determine the reason for this. 123 | dylibOffset -= (is64Bit ? 0x60000000 : 0x200000); 124 | 125 | // Zero-out any previously retrieved name. 126 | memset(name, 0, sizeof(name)); 127 | 128 | void *data = mmap(NULL, localSymbolsLen, PROT_READ, MAP_PRIVATE, fd, localSymbolsPageOffset); 129 | close(fd); 130 | if (data != MAP_FAILED) { 131 | dyld_cache_local_symbols_info *localSymbols = reinterpret_cast(reinterpret_cast(data) + (localSymbolsOffset - localSymbolsPageOffset)); 132 | dyld_cache_local_symbols_entry *entries = reinterpret_cast(reinterpret_cast(localSymbols) + localSymbols->entriesOffset); 133 | for (uint32_t i = 0; i < localSymbols->entriesCount; ++i) { 134 | dyld_cache_local_symbols_entry *entry = &entries[i]; 135 | if (entry->dylibOffset == dylibOffset) { 136 | for (uint32_t j = 0; j < entry->nlistCount; ++j) { 137 | if (is64Bit) { 138 | const struct nlist_64 *nlists = reinterpret_cast((uint64_t)localSymbols + localSymbols->nlistOffset); 139 | const struct nlist_64 *n = &nlists[entry->nlistStartIndex + j]; 140 | if (n->n_value == symbolAddress) { 141 | if (n->n_un.n_strx != 0 && (n->n_type & N_STAB) == 0) { 142 | const char *strings = reinterpret_cast((uint64_t)localSymbols + localSymbols->stringsOffset); 143 | strncpy(name, (strings + n->n_un.n_strx), 1024); 144 | } 145 | break; 146 | } 147 | } else { 148 | const struct nlist *nlists = reinterpret_cast((uint64_t)localSymbols + localSymbols->nlistOffset); 149 | const struct nlist *n = &nlists[entry->nlistStartIndex + j]; 150 | if (n->n_value == symbolAddress) { 151 | if (n->n_un.n_strx != 0 && (n->n_type & N_STAB) == 0) { 152 | const char *strings = reinterpret_cast((uint64_t)localSymbols + localSymbols->stringsOffset); 153 | strncpy(name, (strings + n->n_un.n_strx), 1024); 154 | } 155 | break; 156 | } 157 | } 158 | } 159 | break; 160 | } 161 | } 162 | munmap(data, localSymbolsLen); 163 | } else { 164 | fprintf(stderr, "ERROR: Failed to mmap local symbols portion of shared cache file: %s\n", sharedCachePath); 165 | } 166 | 167 | return name; 168 | } 169 | 170 | /* vim: set ft=objcpp ff=unix sw=4 ts=4 tw=80 expandtab: */ 171 | --------------------------------------------------------------------------------