From 408a7d983903f85069f7b7b6b5819707c311d2fd Mon Sep 17 00:00:00 2001 From: Razvan Deaconescu Date: Sun, 24 Dec 2017 11:33:35 +0200 Subject: [PATCH] Build dyld shared cache extractor (dsc_extractor) from source code --- README.md | 7 + tools/dyld/.gitignore | 1 + tools/dyld/Architectures.hpp | 62 ++ tools/dyld/CacheFileAbstraction.hpp | 418 +++++++++++ tools/dyld/FileAbstraction.hpp | 163 +++++ tools/dyld/MachOFileAbstraction.hpp | 968 ++++++++++++++++++++++++++ tools/dyld/MachOTrie.hpp | 399 +++++++++++ tools/dyld/Makefile | 13 + tools/dyld/dsc_extractor | Bin 207060 -> 0 bytes tools/dyld/dsc_extractor.cpp | 693 ++++++++++++++++++ tools/dyld/dsc_extractor.h | 43 ++ tools/dyld/dsc_iterator.cpp | 249 +++++++ tools/dyld/dsc_iterator.h | 84 +++ tools/dyld/dyld_cache_format.h | 265 +++++++ tools/dyld/dyld_shared_cache_util.cpp | 940 +++++++++++++++++++++++++ 15 files changed, 4305 insertions(+) create mode 100644 tools/dyld/.gitignore create mode 100644 tools/dyld/Architectures.hpp create mode 100644 tools/dyld/CacheFileAbstraction.hpp create mode 100644 tools/dyld/FileAbstraction.hpp create mode 100644 tools/dyld/MachOFileAbstraction.hpp create mode 100644 tools/dyld/MachOTrie.hpp create mode 100644 tools/dyld/Makefile delete mode 100755 tools/dyld/dsc_extractor create mode 100644 tools/dyld/dsc_extractor.cpp create mode 100644 tools/dyld/dsc_extractor.h create mode 100644 tools/dyld/dsc_iterator.cpp create mode 100644 tools/dyld/dsc_iterator.h create mode 100644 tools/dyld/dyld_cache_format.h create mode 100644 tools/dyld/dyld_shared_cache_util.cpp diff --git a/README.md b/README.md index 5492082..3ad4a3e 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,13 @@ make ``` cd tools/lzssdec/ make +``` + + * `dsc_extractor` + +``` +cd tools/dyld/ +make ``` * `xpwn` diff --git a/tools/dyld/.gitignore b/tools/dyld/.gitignore new file mode 100644 index 0000000..6037e5b --- /dev/null +++ b/tools/dyld/.gitignore @@ -0,0 +1 @@ +/dsc_extractor diff --git a/tools/dyld/Architectures.hpp b/tools/dyld/Architectures.hpp new file mode 100644 index 0000000..fe3eea4 --- /dev/null +++ b/tools/dyld/Architectures.hpp @@ -0,0 +1,62 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ARCHITECTURES__ +#define __ARCHITECTURES__ + +#include "FileAbstraction.hpp" + + +// +// Architectures +// +struct x86 +{ + typedef Pointer32 P; + +}; + +struct x86_64 +{ + typedef Pointer64 P; +}; + +struct arm +{ + typedef Pointer32 P; + +}; + +struct arm64 +{ + typedef Pointer64 P; + +}; + + + + +#endif // __ARCHITECTURES__ + + diff --git a/tools/dyld/CacheFileAbstraction.hpp b/tools/dyld/CacheFileAbstraction.hpp new file mode 100644 index 0000000..b1ac76c --- /dev/null +++ b/tools/dyld/CacheFileAbstraction.hpp @@ -0,0 +1,418 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __DYLD_CACHE_ABSTRACTION__ +#define __DYLD_CACHE_ABSTRACTION__ + +#include "dyld_cache_format.h" + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + +template +class dyldCacheHeader { +public: + const char* magic() const INLINE { return fields.magic; } + void set_magic(const char* value) INLINE { memcpy(fields.magic, value, 16); } + + uint32_t mappingOffset() const INLINE { return E::get32(fields.mappingOffset); } + void set_mappingOffset(uint32_t value) INLINE { E::set32(fields.mappingOffset, value); } + + uint32_t mappingCount() const INLINE { return E::get32(fields.mappingCount); } + void set_mappingCount(uint32_t value) INLINE { E::set32(fields.mappingCount, value); } + + uint32_t imagesOffset() const INLINE { return E::get32(fields.imagesOffset); } + void set_imagesOffset(uint32_t value) INLINE { E::set32(fields.imagesOffset, value); } + + uint32_t imagesCount() const INLINE { return E::get32(fields.imagesCount); } + void set_imagesCount(uint32_t value) INLINE { E::set32(fields.imagesCount, value); } + + uint64_t dyldBaseAddress() const INLINE { return E::get64(fields.dyldBaseAddress); } + void set_dyldBaseAddress(uint64_t value) INLINE { E::set64(fields.dyldBaseAddress, value); } + + uint64_t codeSignatureOffset() const INLINE { return E::get64(fields.codeSignatureOffset); } + void set_codeSignatureOffset(uint64_t value) INLINE { E::set64(fields.codeSignatureOffset, value); } + + uint64_t codeSignatureSize() const INLINE { return E::get64(fields.codeSignatureSize); } + void set_codeSignatureSize(uint64_t value) INLINE { E::set64(fields.codeSignatureSize, value); } + + uint64_t slideInfoOffset() const INLINE { return E::get64(fields.slideInfoOffset); } + void set_slideInfoOffset(uint64_t value) INLINE { E::set64(fields.slideInfoOffset, value); } + + uint64_t slideInfoSize() const INLINE { return E::get64(fields.slideInfoSize); } + void set_slideInfoSize(uint64_t value) INLINE { E::set64(fields.slideInfoSize, value); } + + uint64_t localSymbolsOffset() const INLINE { return E::get64(fields.localSymbolsOffset); } + void set_localSymbolsOffset(uint64_t value) INLINE { E::set64(fields.localSymbolsOffset, value); } + + uint64_t localSymbolsSize() const INLINE { return E::get64(fields.localSymbolsSize); } + void set_localSymbolsSize(uint64_t value) INLINE { E::set64(fields.localSymbolsSize, value); } + + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } + + uint64_t cacheType() const INLINE { return E::get64(fields.cacheType); } + void set_cacheType(uint64_t value) INLINE { E::set64(fields.cacheType, value); } + + uint32_t branchPoolsOffset() const INLINE { return E::get32(fields.branchPoolsOffset); } + void set_branchPoolsOffset(uint32_t value) INLINE { E::set32(fields.branchPoolsOffset, value); } + + uint32_t branchPoolsCount() const INLINE { return E::get32(fields.branchPoolsCount); } + void set_branchPoolsCount(uint32_t value) INLINE { E::set32(fields.branchPoolsCount, value); } + + uint64_t accelerateInfoAddr() const INLINE { return E::get64(fields.accelerateInfoAddr); } + void set_accelerateInfoAddr(uint64_t value) INLINE { E::set64(fields.accelerateInfoAddr, value); } + + uint64_t accelerateInfoSize() const INLINE { return E::get64(fields.accelerateInfoSize); } + void set_accelerateInfoSize(uint64_t value) INLINE { E::set64(fields.accelerateInfoSize, value); } + + uint64_t imagesTextOffset() const INLINE { return E::get64(fields.imagesTextOffset); } + void set_imagesTextOffset(uint64_t value) INLINE { E::set64(fields.imagesTextOffset, value); } + + uint64_t imagesTextCount() const INLINE { return E::get64(fields.imagesTextCount); } + void set_imagesTextCount(uint64_t value) INLINE { E::set64(fields.imagesTextCount, value); } + +private: + dyld_cache_header fields; +}; + + + +template +class dyldCacheFileMapping { +public: + uint64_t address() const INLINE { return E::get64(fields.address); } + void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } + + uint64_t size() const INLINE { return E::get64(fields.size); } + void set_size(uint64_t value) INLINE { E::set64(fields.size, value); } + + uint64_t file_offset() const INLINE { return E::get64(fields.fileOffset); } + void set_file_offset(uint64_t value) INLINE { E::set64(fields.fileOffset, value); } + + uint32_t max_prot() const INLINE { return E::get32(fields.maxProt); } + void set_max_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.maxProt, value); } + + uint32_t init_prot() const INLINE { return E::get32(fields.initProt); } + void set_init_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.initProt, value); } + +private: + dyld_cache_mapping_info fields; +}; + + +template +class dyldCacheImageInfo { +public: + uint64_t address() const INLINE { return E::get64(fields.address); } + void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } + + uint64_t modTime() const INLINE { return E::get64(fields.modTime); } + void set_modTime(uint64_t value) INLINE { E::set64(fields.modTime, value); } + + uint64_t inode() const INLINE { return E::get64(fields.inode); } + void set_inode(uint64_t value) INLINE { E::set64(fields.inode, value); } + + uint32_t pathFileOffset() const INLINE { return E::get32(fields.pathFileOffset); } + void set_pathFileOffset(uint32_t value) INLINE { E::set32(fields.pathFileOffset, value); fields.pad=0; } + +private: + dyld_cache_image_info fields; +}; + +template +class dyldCacheImageTextInfo { +public: + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } + + uint64_t loadAddress() const INLINE { return E::get64(fields.loadAddress); } + void set_loadAddress(uint64_t value) INLINE { E::set64(fields.loadAddress, value); } + + uint32_t textSegmentSize() const INLINE { return E::get32(fields.textSegmentSize); } + void set_textSegmentSize(uint32_t value) INLINE { E::set32(fields.textSegmentSize, value); } + + uint32_t pathOffset() const INLINE { return E::get32(fields.pathOffset); } + void set_pathOffset(uint32_t value) INLINE { E::set32(fields.pathOffset, value); } + +private: + dyld_cache_image_text_info fields; +}; + + + +template +class dyldCacheImageInfoExtra { +public: + uint64_t exportsTrieAddr() const INLINE { return E::get64(fields.exportsTrieAddr); } + void set_exportsTrieAddr(uint64_t value) INLINE { E::set64(fields.exportsTrieAddr, value); } + + uint64_t weakBindingsAddr() const INLINE { return E::get64(fields.weakBindingsAddr); } + void set_weakBindingsAddr(uint64_t value) INLINE { E::set64(fields.weakBindingsAddr, value); } + + uint32_t exportsTrieSize() const INLINE { return E::get32(fields.exportsTrieSize); } + void set_exportsTrieSize(uint32_t value) INLINE { E::set32(fields.exportsTrieSize, value); } + + uint32_t weakBindingsSize() const INLINE { return E::get32(fields.weakBindingsSize); } + void set_weakBindingsSize(uint32_t value) INLINE { E::set32(fields.weakBindingsSize, value); } + + uint32_t dependentsStartArrayIndex() const INLINE { return E::get32(fields.dependentsStartArrayIndex); } + void set_dependentsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.dependentsStartArrayIndex, value); } + + uint32_t reExportsStartArrayIndex() const INLINE { return E::get32(fields.reExportsStartArrayIndex); } + void set_reExportsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.reExportsStartArrayIndex, value); } + +private: + dyld_cache_image_info_extra fields; +}; + + +template +class dyldCacheAcceleratorInfo { +public: + uint32_t version() const INLINE { return E::get32(fields.version); } + void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } + + uint32_t imageExtrasCount() const INLINE { return E::get32(fields.imageExtrasCount); } + void set_imageExtrasCount(uint32_t value) INLINE { E::set32(fields.imageExtrasCount, value); } + + uint32_t imagesExtrasOffset() const INLINE { return E::get32(fields.imagesExtrasOffset); } + void set_imagesExtrasOffset(uint32_t value) INLINE { E::set32(fields.imagesExtrasOffset, value); } + + uint32_t bottomUpListOffset() const INLINE { return E::get32(fields.bottomUpListOffset); } + void set_bottomUpListOffset(uint32_t value) INLINE { E::set32(fields.bottomUpListOffset, value); } + + uint32_t dylibTrieOffset() const INLINE { return E::get32(fields.dylibTrieOffset); } + void set_dylibTrieOffset(uint32_t value) INLINE { E::set32(fields.dylibTrieOffset, value); } + + uint32_t dylibTrieSize() const INLINE { return E::get32(fields.dylibTrieSize); } + void set_dylibTrieSize(uint32_t value) INLINE { E::set32(fields.dylibTrieSize, value); } + + uint32_t initializersOffset() const INLINE { return E::get32(fields.initializersOffset); } + void set_initializersOffset(uint32_t value) INLINE { E::set32(fields.initializersOffset, value); } + + uint32_t initializersCount() const INLINE { return E::get32(fields.initializersCount); } + void set_initializersCount(uint32_t value) INLINE { E::set32(fields.initializersCount, value); } + + uint32_t dofSectionsOffset() const INLINE { return E::get32(fields.dofSectionsOffset); } + void set_dofSectionsOffset(uint32_t value) INLINE { E::set32(fields.dofSectionsOffset, value); } + + uint32_t dofSectionsCount() const INLINE { return E::get32(fields.dofSectionsCount); } + void set_dofSectionsCount(uint32_t value) INLINE { E::set32(fields.dofSectionsCount, value); } + + uint32_t reExportListOffset() const INLINE { return E::get32(fields.reExportListOffset); } + void set_reExportListOffset(uint32_t value) INLINE { E::set32(fields.reExportListOffset, value); } + + uint32_t reExportCount() const INLINE { return E::get32(fields.reExportCount); } + void set_reExportCount(uint32_t value) INLINE { E::set32(fields.reExportCount, value); } + + uint32_t depListOffset() const INLINE { return E::get32(fields.depListOffset); } + void set_depListOffset(uint32_t value) INLINE { E::set32(fields.depListOffset, value); } + + uint32_t depListCount() const INLINE { return E::get32(fields.depListCount); } + void set_depListCount(uint32_t value) INLINE { E::set32(fields.depListCount, value); } + + uint32_t rangeTableOffset() const INLINE { return E::get32(fields.rangeTableOffset); } + void set_rangeTableOffset(uint32_t value) INLINE { E::set32(fields.rangeTableOffset, value); } + + uint32_t rangeTableCount() const INLINE { return E::get32(fields.rangeTableCount); } + void set_rangeTableCount(uint32_t value) INLINE { E::set32(fields.rangeTableCount, value); } + + uint64_t dyldSectionAddr() const INLINE { return E::get64(fields.dyldSectionAddr); } + void set_dyldSectionAddr(uint64_t value) INLINE { E::set64(fields.dyldSectionAddr, value); } + + +private: + dyld_cache_accelerator_info fields; +}; + + +template +class dyldCacheAcceleratorInitializer { +public: + uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); } + void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); } + + uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } + void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } + +private: + dyld_cache_accelerator_initializer fields; +}; + + +template +class dyldCacheAcceleratorRangeEntry { +public: + uint64_t startAddress() const INLINE { return E::get64(fields.startAddress); } + void set_startAddress(uint64_t value) INLINE { E::set64(fields.startAddress, value); } + + uint32_t size() const INLINE { return E::get32(fields.size); } + void set_size(uint32_t value) INLINE { E::set32(fields.size, value); } + + uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } + void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } + +private: + dyld_cache_range_entry fields; +}; + +template +class dyldCacheAcceleratorDOFEntry { +public: + uint64_t sectionAddress() const INLINE { return E::get64(fields.sectionAddress); } + void set_sectionAddress(uint64_t value) INLINE { E::set64(fields.sectionAddress, value); } + + uint32_t sectionSize() const INLINE { return E::get32(fields.sectionSize); } + void set_sectionSize(uint32_t value) INLINE { E::set32(fields.sectionSize, value); } + + uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } + void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } + +private: + dyld_cache_accelerator_dof fields; +}; + + +template +class dyldCacheSlideInfo { +public: + uint32_t version() const INLINE { return E::get32(fields.version); } + void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } + + uint32_t toc_offset() const INLINE { return E::get32(fields.toc_offset); } + void set_toc_offset(uint32_t value) INLINE { E::set32(fields.toc_offset, value); } + + uint32_t toc_count() const INLINE { return E::get32(fields.toc_count); } + void set_toc_count(uint32_t value) INLINE { E::set32(fields.toc_count, value); } + + uint32_t entries_offset() const INLINE { return E::get32(fields.entries_offset); } + void set_entries_offset(uint32_t value) INLINE { E::set32(fields.entries_offset, value); } + + uint32_t entries_count() const INLINE { return E::get32(fields.entries_count); } + void set_entries_count(uint32_t value) INLINE { E::set32(fields.entries_count, value); } + + uint32_t entries_size() const INLINE { return E::get32(fields.entries_size); } + void set_entries_size(uint32_t value) INLINE { E::set32(fields.entries_size, value); } + + uint16_t toc(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index]); } + void set_toc(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index], value); } + +private: + dyld_cache_slide_info fields; +}; + + +struct dyldCacheSlideInfoEntry { + uint8_t bits[4096/(8*4)]; // 128-byte bitmap +}; + + +template +class dyldCacheSlideInfo2 { +public: + uint32_t version() const INLINE { return E::get32(fields.version); } + void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } + + uint32_t page_starts_offset() const INLINE { return E::get32(fields.page_starts_offset); } + void set_page_starts_offset(uint32_t value) INLINE { E::set32(fields.page_starts_offset, value); } + + uint32_t page_starts_count() const INLINE { return E::get32(fields.page_starts_count); } + void set_page_starts_count(uint32_t value) INLINE { E::set32(fields.page_starts_count, value); } + + uint32_t page_extras_offset() const INLINE { return E::get32(fields.page_extras_offset); } + void set_page_extras_offset(uint32_t value) INLINE { E::set32(fields.page_extras_offset, value); } + + uint32_t page_extras_count() const INLINE { return E::get32(fields.page_extras_count); } + void set_page_extras_count(uint32_t value) INLINE { E::set32(fields.page_extras_count, value); } + + uint32_t page_size() const INLINE { return E::get32(fields.page_size); } + void set_page_size(uint32_t value) INLINE { E::set32(fields.page_size, value); } + + uint64_t delta_mask() const INLINE { return E::get64(fields.delta_mask); } + void set_delta_mask(uint64_t value) INLINE { E::set64(fields.delta_mask, value); } + + uint64_t value_add() const INLINE { return E::get64(fields.value_add); } + void set_value_add(uint64_t value) INLINE { E::set64(fields.value_add, value); } + + uint16_t page_starts(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index]); } + void set_page_starts(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index], value); } + + uint16_t page_extras(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index]); } + void set_page_extras(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index], value); } + + +private: + dyld_cache_slide_info2 fields; +}; + + + +template +class dyldCacheLocalSymbolsInfo { +public: + uint32_t nlistOffset() const INLINE { return E::get32(fields.nlistOffset); } + void set_nlistOffset(uint32_t value) INLINE { E::set32(fields.nlistOffset, value); } + + uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } + void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } + + uint32_t stringsOffset() const INLINE { return E::get32(fields.stringsOffset); } + void set_stringsOffset(uint32_t value) INLINE { E::set32(fields.stringsOffset, value); } + + uint32_t stringsSize() const INLINE { return E::get32(fields.stringsSize); } + void set_stringsSize(uint32_t value) INLINE { E::set32(fields.stringsSize, value); } + + uint32_t entriesOffset() const INLINE { return E::get32(fields.entriesOffset); } + void set_entriesOffset(uint32_t value) INLINE { E::set32(fields.entriesOffset, value); } + + uint32_t entriesCount() const INLINE { return E::get32(fields.entriesCount); } + void set_entriesCount(uint32_t value) INLINE { E::set32(fields.entriesCount, value); } + +private: + dyld_cache_local_symbols_info fields; +}; + + +template +class dyldCacheLocalSymbolEntry { +public: + uint32_t dylibOffset() const INLINE { return E::get32(fields.dylibOffset); } + void set_dylibOffset(uint32_t value) INLINE { E::set32(fields.dylibOffset, value); } + + uint32_t nlistStartIndex() const INLINE { return E::get32(fields.nlistStartIndex); } + void set_nlistStartIndex(uint32_t value) INLINE { E::set32(fields.nlistStartIndex, value); } + + uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } + void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } + +private: + dyld_cache_local_symbols_entry fields; +}; + + + + +#endif // __DYLD_CACHE_ABSTRACTION__ + + diff --git a/tools/dyld/FileAbstraction.hpp b/tools/dyld/FileAbstraction.hpp new file mode 100644 index 0000000..1d73d74 --- /dev/null +++ b/tools/dyld/FileAbstraction.hpp @@ -0,0 +1,163 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include +#include +#include + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static int32_t get32(const int32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(int32_t& into, int32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, (uint32_t)value); } + + // Round to a P-size boundary + template + static T round_up(T value) { return (value+3) & ~(T)3; } + template + static T round_down(T value) { return value & ~(T)3; } +}; + + +template +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } + + // Round to a P-size boundary + template + static T round_up(T value) { return (value+7) & ~(T)7; } + template + static T round_down(T value) { return value & ~(T)7; } +}; + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/tools/dyld/MachOFileAbstraction.hpp b/tools/dyld/MachOFileAbstraction.hpp new file mode 100644 index 0000000..ebd298d --- /dev/null +++ b/tools/dyld/MachOFileAbstraction.hpp @@ -0,0 +1,968 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ +*/ +#ifndef __MACH_O_FILE_ABSTRACTION__ +#define __MACH_O_FILE_ABSTRACTION__ + +#include +#include +#include +#include + +// suport older versions of mach-o/loader.h +#ifndef LC_UUID +#define LC_UUID 0x1b +struct uuid_command { + uint32_t cmd; /* LC_UUID */ + uint32_t cmdsize; /* sizeof(struct uuid_command) */ + uint8_t uuid[16]; /* the 128-bit uuid */ +}; +#endif + +#ifndef S_16BYTE_LITERALS + #define S_16BYTE_LITERALS 0xE +#endif + +#ifndef CPU_SUBTYPE_ARM_V5TEJ + #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif +#ifndef CPU_SUBTYPE_ARM_XSCALE + #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif +#ifndef CPU_SUBTYPE_ARM_V7 + #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif +#ifndef CPU_SUBTYPE_ARM_V7F + #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) +#endif +#ifndef CPU_SUBTYPE_ARM_V7K + #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) +#endif +#ifndef CPU_SUBTYPE_ARM_V7S + #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) +#endif +#ifndef CPU_SUBTYPE_ARM64_ALL + #define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) +#endif +#ifndef CPU_TYPE_ARM64 + #define CPU_TYPE_ARM64 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64)) +#endif + +#define ARM64_RELOC_UNSIGNED 0 // for pointers + + +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif +#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT + #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#endif + +#ifndef LC_FUNCTION_STARTS + #define LC_FUNCTION_STARTS 0x26 +#endif + +#ifndef LC_DATA_IN_CODE + #define LC_DATA_IN_CODE 0x29 +#endif + +#ifndef LC_DYLIB_CODE_SIGN_DRS + #define LC_DYLIB_CODE_SIGN_DRS 0x2B +#endif + +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif + + +#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F + +#define DYLD_CACHE_ADJ_V2_POINTER_32 0x01 +#define DYLD_CACHE_ADJ_V2_POINTER_64 0x02 +#define DYLD_CACHE_ADJ_V2_DELTA_32 0x03 +#define DYLD_CACHE_ADJ_V2_DELTA_64 0x04 +#define DYLD_CACHE_ADJ_V2_ARM64_ADRP 0x05 +#define DYLD_CACHE_ADJ_V2_ARM64_OFF12 0x06 +#define DYLD_CACHE_ADJ_V2_ARM64_BR26 0x07 +#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT 0x08 +#define DYLD_CACHE_ADJ_V2_ARM_BR24 0x09 +#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A +#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B +#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C + +#define MH_HAS_OBJC 0x40000000 + +#ifndef CPU_SUBTYPE_ARM64_E + #define CPU_SUBTYPE_ARM64_E 2 +#endif + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + +// utility to pair together a cpu-type and cpu-sub-type +struct ArchPair +{ + uint32_t arch; + uint32_t subtype; + + ArchPair(uint32_t cputype, uint32_t cpusubtype) : arch(cputype), subtype(cpusubtype) {} + + bool operator<(const ArchPair& other) const { + if ( this->arch != other.arch ) + return (this->arch < other.arch); + return (this->subtype < other.subtype); + } + + bool operator==(const ArchPair& other) const { + return this->arch == other.arch && this->subtype == other.subtype; + } +}; + + +// +// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness +// + +// +// mach-o load command +// +template +class macho_load_command { +public: + uint32_t cmd() const INLINE { return E::get32(command.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } + + typedef typename P::E E; +private: + load_command command; +}; + + +// +// mach-o segment load command +// +template struct macho_segment_content {}; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; + +template +class macho_segment_command { +public: + uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } + + const char* segname() const INLINE { return segment.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } + + uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } + void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } + + uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } + void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } + + uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } + void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } + + uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } + void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } + + uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } + void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } + + uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } + void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } + + uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } + void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } + + uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } + + enum { + CMD = macho_segment_content

::CMD + }; + + typedef typename P::E E; +private: + macho_segment_content

segment; +}; + + +// +// mach-o section +// +template struct macho_section_content {}; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; + +template +class macho_section { +public: + const char* sectname() const INLINE { return section.fields.sectname; } + void set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); } + + const char* segname() const INLINE { return section.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(section.fields.segname, value, 16); } + + uint64_t addr() const INLINE { return P::getP(section.fields.addr); } + void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); } + + uint64_t size() const INLINE { return P::getP(section.fields.size); } + void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); } + + uint32_t offset() const INLINE { return E::get32(section.fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); } + + uint32_t align() const INLINE { return E::get32(section.fields.align); } + void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); } + + uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); } + void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); } + + uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); } + void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); } + + uint32_t flags() const INLINE { return E::get32(section.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); } + + uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); } + void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); } + + uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); } + void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); } + + typedef typename P::E E; +private: + macho_section_content

section; +}; + + +// +// mach-o dylib load command +// +template +class macho_dylib_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); } + + uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); } + void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); } + + uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); } + void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); } + + uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); } + void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylib_command fields; +}; + + +// +// mach-o dylinker load command +// +template +class macho_dylinker_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylinker_command fields; +}; + + +// +// mach-o sub_framework load command +// +template +class macho_sub_framework_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); } + void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); } + + const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); } + void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_framework_command fields; +}; + + +// +// mach-o sub_client load command +// +template +class macho_sub_client_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); } + void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); } + + const char* client() const INLINE { return (const char*)&fields + client_offset(); } + void set_client_offset() INLINE { set_client_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_client_command fields; +}; + + +// +// mach-o sub_umbrella load command +// +template +class macho_sub_umbrella_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); } + void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); } + + const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); } + void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_umbrella_command fields; +}; + + +// +// mach-o sub_library load command +// +template +class macho_sub_library_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); } + void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); } + + const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); } + void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_library_command fields; +}; + + +// +// mach-o uuid load command +// +template +class macho_uuid_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(uint8_t value[16]) INLINE { memcpy(&fields.uuid, value, 16); } + + typedef typename P::E E; +private: + uuid_command fields; +}; + + +// +// mach-o routines load command +// +template struct macho_routines_content {}; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; + +template +class macho_routines_command { +public: + uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); } + + uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); } + void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); } + + uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); } + void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); } + + uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); } + void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); } + + uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); } + void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); } + + uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); } + void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); } + + uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); } + void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); } + + uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); } + void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); } + + uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); } + void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); } + + typedef typename P::E E; + enum { + CMD = macho_routines_content

::CMD + }; +private: + macho_routines_content

routines; +}; + + +// +// mach-o symbol table load command +// +template +class macho_symtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t symoff() const INLINE { return E::get32(fields.symoff); } + void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); } + + uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); } + void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); } + + uint32_t stroff() const INLINE { return E::get32(fields.stroff); } + void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); } + + uint32_t strsize() const INLINE { return E::get32(fields.strsize); } + void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); } + + + typedef typename P::E E; +private: + symtab_command fields; +}; + + +// +// mach-o dynamic symbol table load command +// +template +class macho_dysymtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); } + void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); } + + uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); } + void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); } + + uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); } + void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); } + + uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); } + void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); } + + uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); } + void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); } + + uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); } + void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); } + + uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); } + void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); } + + uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); } + void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); } + + uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); } + void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); } + + uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); } + void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); } + + uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); } + void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); } + + uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); } + void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); } + + uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); } + void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); } + + uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); } + void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); } + + uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); } + void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); } + + uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); } + void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); } + + uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); } + void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); } + + uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); } + void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); } + + typedef typename P::E E; +private: + dysymtab_command fields; +}; + + +// +// mach-o two-level hints load command +// +template +class macho_twolevel_hints_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t offset() const INLINE { return E::get32(fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); } + + uint32_t nhints() const INLINE { return E::get32(fields.nhints); } + void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); } + + typedef typename P::E E; +private: + twolevel_hints_command fields; +}; + + +// +// mach-o threads load command +// +template +class macho_thread_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t flavor() const INLINE { return E::get32(fields_flavor); } + void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); } + + uint32_t count() const INLINE { return E::get32(fields_count); } + void set_count(uint32_t value) INLINE { E::set32(fields_count, value); } + + uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); } + void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); } + + typedef typename P::E E; + typedef typename P::uint_t pint_t; +private: + struct thread_command fields; + uint32_t fields_flavor; + uint32_t fields_count; + pint_t thread_registers[1]; +}; + + +// +// mach-o misc data +// +template +class macho_linkedit_data_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); } + void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); } + + uint32_t datasize() const INLINE { return E::get32(fields.datasize); } + void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); } + + + typedef typename P::E E; +private: + linkedit_data_command fields; +}; + + +// +// mach-o symbol table entry +// +template struct macho_nlist_content {}; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; + +template +class macho_nlist { +public: + uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); } + void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); } + + uint8_t n_type() const INLINE { return entry.fields.n_type; } + void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; } + + uint8_t n_sect() const INLINE { return entry.fields.n_sect; } + void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; } + + uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); } + void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); } + + uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); } + void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); } + + typedef typename P::E E; +private: + macho_nlist_content

entry; +}; + + + +// +// mach-o relocation info +// +template +class macho_relocation_info { +public: + uint32_t r_address() const INLINE { return E::get32(address); } + void set_r_address(uint32_t value) INLINE { E::set32(address, value); } + + uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } + void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } + + bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } + void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } + + uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } + void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } + + bool r_extern() const INLINE { return E::getBits(other, 27, 1); } + void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } + + uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } + void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t address; + uint32_t other; +}; + + +// +// mach-o scattered relocation info +// The bit fields are always in big-endian order (see mach-o/reloc.h) +// +template +class macho_scattered_relocation_info { +public: + bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } + void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } + + bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } + void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } + + uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } + void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } + + uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } + void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } + + uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } + void set_r_address(uint32_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } + + uint32_t r_value() const INLINE { return E::get32(value); } + void set_r_value(uint32_t x) INLINE { E::set32(value, x); } + + uint32_t r_other() const INLINE { return other; } + + typedef typename P::E E; +private: + uint32_t other; + uint32_t value; +}; + + +// +// mach-o file header +// +template struct macho_header_content {}; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; + +template +class macho_header { +public: + uint32_t magic() const INLINE { return E::get32(header.fields.magic); } + void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } + + uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } + void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } + + uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } + void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } + + uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } + void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } + + uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } + void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } + + uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } + void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } + + uint32_t flags() const INLINE { return E::get32(header.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } + + uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } + void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } + + const macho_segment_command

* getSegment(const char *segname) const + { + const macho_load_command

* cmds = (macho_load_command

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

)); + uint32_t cmd_count = this->ncmds(); + const macho_load_command

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

::CMD ) { + const macho_segment_command

* segcmd = (macho_segment_command

*)cmd; + if (0 == strncmp(segname, segcmd->segname(), 16)) { + return segcmd; + } + } + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return NULL; + } + + const macho_section

* getSection(const char *segname, const char *sectname) const + { + const macho_segment_command

* segcmd = getSegment(segname); + if (!segcmd) return NULL; + + const macho_section

* sectcmd = (macho_section

*)(segcmd+1); + uint32_t section_count = segcmd->nsects(); + for (uint32_t j = 0; j < section_count; ++j) { + if (0 == ::strncmp(sectcmd[j].sectname(), sectname, 16)) { + return sectcmd+j; + } + } + + if (strcmp(segname, "__DATA") == 0) + return getSection("__DATA_CONST", sectname); + return NULL; + } + + const macho_load_command

* getLoadCommand(int query) const + { + const macho_load_command

* cmds = (macho_load_command

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

)); + uint32_t cmd_count = this->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == query ) { + return cmd; + } + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return NULL; + } + + typedef typename P::E E; +private: + macho_header_content

header; +}; + + + +// +// compressed dyld info load command +// +template +class macho_dyld_info_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); } + void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); } + + uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); } + void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); } + + uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); } + void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); } + + uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); } + void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); } + + uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); } + void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); } + + uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); } + void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); } + + uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); } + void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); } + + uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); } + void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); } + + uint32_t export_off() const INLINE { return E::get32(fields.export_off); } + void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); } + + uint32_t export_size() const INLINE { return E::get32(fields.export_size); } + void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); } + + + typedef typename P::E E; +private: + dyld_info_command fields; +}; + +#ifndef NO_ULEB +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throw "malformed uleb128 extends beyond trie"; + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throw "uleb128 too big for 64-bits"; + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + +inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throw "malformed sleb128"; + byte = *p++; + result |= (((int64_t)(byte & 0x7f)) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + +#endif + + +#endif // __MACH_O_FILE_ABSTRACTION__ + + diff --git a/tools/dyld/MachOTrie.hpp b/tools/dyld/MachOTrie.hpp new file mode 100644 index 0000000..d2f137e --- /dev/null +++ b/tools/dyld/MachOTrie.hpp @@ -0,0 +1,399 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ +*/ +#ifndef __MACH_O_TRIE__ +#define __MACH_O_TRIE__ + +#include +#include + +#include "MachOFileAbstraction.hpp" + + +namespace mach_o { +namespace trie { + +struct Edge +{ + Edge(const char* s, struct Node* n) : fSubString(s), fChild(n) { } + ~Edge() { } + const char* fSubString; + struct Node* fChild; + +}; + +struct Node +{ + Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), + fOther(0), fImportedName(NULL), fOrdered(false), + fHaveExportInfo(false), fTrieOffset(0) {} + ~Node() { } + const char* fCummulativeString; + std::vector fChildren; + uint64_t fAddress; + uint64_t fFlags; + uint64_t fOther; + const char* fImportedName; + bool fOrdered; + bool fHaveExportInfo; + uint32_t fTrieOffset; + + void addSymbol(const char* fullStr, uint64_t address, uint64_t flags, uint64_t other, const char* importName) { + const char* partialStr = &fullStr[strlen(fCummulativeString)]; + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + long subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addSymbol(fullStr, address, flags, other, importName); + return; + } + else { + for (long i=subStringLen-1; i > 0; --i) { + if ( strncmp(e.fSubString, partialStr, i) == 0 ) { + // found a common substring, splice in new node + // was A -> C, now A -> B -> C + char* bNodeCummStr = strdup(e.fChild->fCummulativeString); + bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0'; + //node* aNode = this; + Node* bNode = new Node(bNodeCummStr); + Node* cNode = e.fChild; + char* abEdgeStr = strdup(e.fSubString); + abEdgeStr[i] = '\0'; + char* bcEdgeStr = strdup(&e.fSubString[i]); + Edge& abEdge = e; + abEdge.fSubString = abEdgeStr; + abEdge.fChild = bNode; + Edge bcEdge(bcEdgeStr, cNode); + bNode->fChildren.push_back(bcEdge); + bNode->addSymbol(fullStr, address, flags, other, importName); + return; + } + } + } + } + + // no commonality with any existing child, make a new edge that is this whole string + Node* newNode = new Node(strdup(fullStr)); + Edge newEdge(strdup(partialStr), newNode); + fChildren.push_back(newEdge); + newNode->fAddress = address; + newNode->fFlags = flags; + newNode->fOther = other; + if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && (importName != NULL) && (strcmp(fullStr,importName) != 0) ) + newNode->fImportedName = importName; + else + newNode->fImportedName = NULL; + newNode->fHaveExportInfo = true; + } + + void addOrderedNodes(const char* name, std::vector& orderedNodes) { + if ( !fOrdered ) { + orderedNodes.push_back(this); + //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString); + fOrdered = true; + } + const char* partialStr = &name[strlen(fCummulativeString)]; + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + long subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addOrderedNodes(name, orderedNodes); + return; + } + } + } + + // byte for terminal node size in bytes, or 0x00 if not terminal node + // teminal node (uleb128 flags, uleb128 addr [uleb128 other]) + // byte for child node count + // each child: zero terminated substring, uleb128 node offset + bool updateOffset(uint32_t& offset) { + uint32_t nodeSize = 1; // length of export info when no export info + if ( fHaveExportInfo ) { + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + nodeSize = uleb128_size(fFlags) + uleb128_size(fOther); // ordinal + if ( fImportedName != NULL ) + nodeSize += strlen(fImportedName); + ++nodeSize; // trailing zero in imported name + } + else { + nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); + if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + nodeSize += uleb128_size(fOther); + } + // do have export info, overall node size so far is uleb128 of export info + export info + nodeSize += uleb128_size(nodeSize); + } + // add children + ++nodeSize; // byte for count of chidren + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + nodeSize += strlen(e.fSubString) + 1 + uleb128_size(e.fChild->fTrieOffset); + } + bool result = (fTrieOffset != offset); + fTrieOffset = offset; + //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); + offset += nodeSize; + // return true if fTrieOffset was changed + return result; + } + + void appendToStream(std::vector& out) { + if ( fHaveExportInfo ) { + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( fImportedName != NULL ) { + // nodes with re-export info: size, flags, ordinal, string + uint32_t nodeSize = (uint32_t)(uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fOther, out); + append_string(fImportedName, out); + } + else { + // nodes with re-export info: size, flags, ordinal, empty-string + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + 1; + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fOther, out); + out.push_back(0); + } + } + else if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + // nodes with export info: size, flags, address, other + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress) + uleb128_size(fOther); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + append_uleb128(fOther, out); + } + else { + // nodes with export info: size, flags, address + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + } + } + else { + // no export info uleb128 of zero is one byte of zero + out.push_back(0); + } + // write number of children + out.push_back(fChildren.size()); + // write each child + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + append_string(e.fSubString, out); + append_uleb128(e.fChild->fTrieOffset, out); + } + } + +private: + static void append_uleb128(uint64_t value, std::vector& out) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + out.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); + } + + static void append_string(const char* str, std::vector& out) { + for (const char* s = str; *s != '\0'; ++s) + out.push_back(*s); + out.push_back('\0'); + } + + static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; + } + + +}; + +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) +#if __EXCEPTIONS + throw "malformed uleb128 extends beyond trie"; +#else + return result; +#endif + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) +#if __EXCEPTIONS + throw "uleb128 too big for 64-bits"; +#else + return result; +#endif + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + + +struct Entry +{ + const char* name; + uint64_t address; + uint64_t flags; + uint64_t other; + const char* importName; +}; + + + +inline void makeTrie(const std::vector& entries, std::vector& output) +{ + Node start(strdup("")); + + // make nodes for all exported symbols + for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { + start.addSymbol(it->name, it->address, it->flags, it->other, it->importName); + } + + // create vector of nodes + std::vector orderedNodes; + orderedNodes.reserve(entries.size()*2); + for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { + start.addOrderedNodes(it->name, orderedNodes); + } + + // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized + bool more; + do { + uint32_t offset = 0; + more = false; + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + if ( (*it)->updateOffset(offset) ) + more = true; + } + } while ( more ); + + // create trie stream + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + (*it)->appendToStream(output); + } +} + +struct EntryWithOffset +{ + uintptr_t nodeOffset; + Entry entry; + + bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); } +}; + + + +static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, + char* cummulativeString, int curStrOffset, + std::vector& output) +{ + if ( p >= end ) +#if __EXCEPTIONS + throw "malformed trie, node past end"; +#else + return; +#endif + const uint8_t terminalSize = read_uleb128(p, end); + const uint8_t* children = p + terminalSize; + if ( terminalSize != 0 ) { + EntryWithOffset e; + e.nodeOffset = p-start; + e.entry.name = strdup(cummulativeString); + e.entry.flags = read_uleb128(p, end); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + e.entry.address = 0; + e.entry.other = read_uleb128(p, end); // dylib ordinal + e.entry.importName = (char*)p; + } + else { + e.entry.address = read_uleb128(p, end); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + e.entry.other = read_uleb128(p, end); + else + e.entry.other = 0; + e.entry.importName = NULL; + } + output.push_back(e); + } + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + int edgeStrLen = 0; + while (*s != '\0') { + cummulativeString[curStrOffset+edgeStrLen] = *s++; + ++edgeStrLen; + } + cummulativeString[curStrOffset+edgeStrLen] = *s++; + uint32_t childNodeOffet = (uint32_t)read_uleb128(s, end); + processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output); + } +} + + +inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector& output) +{ + // empty trie has no entries + if ( start == end ) + return; + char cummulativeString[32000]; + std::vector entries; + processExportNode(start, start, end, cummulativeString, 0, entries); + // to preserve tie layout order, sort by node offset + std::sort(entries.begin(), entries.end()); + // copy to output + output.reserve(entries.size()); + for (std::vector::iterator it=entries.begin(); it != entries.end(); ++it) + output.push_back(it->entry); +} + + + + +}; // namespace trie +}; // namespace mach_o + + +#endif // __MACH_O_TRIE__ + + diff --git a/tools/dyld/Makefile b/tools/dyld/Makefile new file mode 100644 index 0000000..4a4742e --- /dev/null +++ b/tools/dyld/Makefile @@ -0,0 +1,13 @@ +CXXFLAGS = -Wall +CXX = clang++ + +.PHONY: all clean + +all: dsc_extractor + +dsc_extractor: dsc_extractor.o dsc_iterator.o + $(CXX) -o $@ $^ + +clean: + -rm -f dsc_extractor dsc_extractor.o dsc_iterator.o + -rm -f *~ diff --git a/tools/dyld/dsc_extractor b/tools/dyld/dsc_extractor deleted file mode 100755 index 432fcc8c9f991818fffee5029dd7d70ddfc57af7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 207060 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NO_ULEB +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" + +#include "dsc_iterator.h" +#include "dsc_extractor.h" +#include "MachOTrie.hpp" + +#include +#include +#include +#include +#include +#include + +struct seg_info +{ + seg_info(const char* n, uint64_t o, uint64_t s) + : segName(n), offset(o), sizem(s) { } + const char* segName; + uint64_t offset; + uint64_t sizem; +}; + +class CStringHash { +public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; +}; +class CStringEquals { +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; +typedef std::unordered_map, CStringHash, CStringEquals> NameToSegments; + +// Filter to find individual symbol re-exports in trie +class NotReExportSymbol { +public: + NotReExportSymbol(const std::set &rd) :_reexportDeps(rd) {} + bool operator()(const mach_o::trie::Entry &entry) const { + bool result = isSymbolReExport(entry); + if (result) { + // Xcode 6 leaks in dyld_shared_cache_extract_dylibs + ::free((void*)entry.name); + const_cast(&entry)->name = NULL; + } + return result; + } +private: + bool isSymbolReExport(const mach_o::trie::Entry &entry) const { + if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) + return true; + if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 ) + return true; + // If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export + if ( _reexportDeps.count((int)entry.other) != 0 ) + return true; + return false; + } + const std::set &_reexportDeps; +}; + + +template +int optimize_linkedit(macho_header* mh, uint64_t textOffsetInCache, const void* mapped_cache, uint64_t* newSize) +{ + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + // update header flags + mh->set_flags(mh->flags() & 0x7FFFFFFF); // remove in-cache bit + + // update load commands + uint64_t cumulativeFileSize = 0; + const unsigned origLoadCommandsSize = mh->sizeofcmds(); + unsigned bytesRemaining = origLoadCommandsSize; + unsigned removedCount = 0; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmdCount = mh->ncmds(); + const macho_load_command

* cmd = cmds; + macho_segment_command

* linkEditSegCmd = NULL; + macho_symtab_command

* symtab = NULL; + macho_dysymtab_command

* dynamicSymTab = NULL; + macho_linkedit_data_command

* functionStarts = NULL; + macho_linkedit_data_command

* dataInCode = NULL; + uint32_t exportsTrieOffset = 0; + uint32_t exportsTrieSize = 0; + std::set reexportDeps; + int depIndex = 0; + for (uint32_t i = 0; i < cmdCount; ++i) { + bool remove = false; + switch ( cmd->cmd() ) { + case macho_segment_command

::CMD: + { + // update segment/section file offsets + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + segCmd->set_fileoff(cumulativeFileSize); + macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->offset() != 0 ) + sect->set_offset((uint32_t)(cumulativeFileSize+sect->addr()-segCmd->vmaddr())); + } + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + linkEditSegCmd = segCmd; + } + cumulativeFileSize += segCmd->filesize(); + } + break; + case LC_DYLD_INFO_ONLY: + { + // zero out all dyld info + macho_dyld_info_command

* dyldInfo = (macho_dyld_info_command

*)cmd; + exportsTrieOffset = dyldInfo->export_off(); + exportsTrieSize = dyldInfo->export_size(); + dyldInfo->set_rebase_off(0); + dyldInfo->set_rebase_size(0); + dyldInfo->set_bind_off(0); + dyldInfo->set_bind_size(0); + dyldInfo->set_weak_bind_off(0); + dyldInfo->set_weak_bind_size(0); + dyldInfo->set_lazy_bind_off(0); + dyldInfo->set_lazy_bind_size(0); + dyldInfo->set_export_off(0); + dyldInfo->set_export_size(0); + } + break; + case LC_SYMTAB: + symtab = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + dynamicSymTab = (macho_dysymtab_command

*)cmd; + break; + case LC_FUNCTION_STARTS: + functionStarts = (macho_linkedit_data_command

*)cmd; + break; + case LC_DATA_IN_CODE: + dataInCode = (macho_linkedit_data_command

*)cmd; + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + ++depIndex; + if ( cmd->cmd() == LC_REEXPORT_DYLIB ) { + reexportDeps.insert(depIndex); + } + break; + case LC_SEGMENT_SPLIT_INFO: + // dylibs iOS 9 dyld caches have bogus LC_SEGMENT_SPLIT_INFO + remove = true; + break; + } + uint32_t cmdSize = cmd->cmdsize(); + macho_load_command

* nextCmd = (macho_load_command

*)(((uint8_t*)cmd)+cmdSize); + if ( remove ) { + ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining); + ++removedCount; + } + else { + bytesRemaining -= cmdSize; + cmd = nextCmd; + } + } + // zero out stuff removed + ::bzero((void*)cmd, bytesRemaining); + // update header + mh->set_ncmds(cmdCount - removedCount); + mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining); + + // rebuild symbol table + if ( linkEditSegCmd == NULL ) { + fprintf(stderr, "__LINKEDIT not found\n"); + return -1; + } + if ( symtab == NULL ) { + fprintf(stderr, "LC_SYMTAB not found\n"); + return -1; + } + if ( dynamicSymTab == NULL ) { + fprintf(stderr, "LC_DYSYMTAB not found\n"); + return -1; + } + + const uint64_t newFunctionStartsOffset = linkEditSegCmd->fileoff(); + uint32_t functionStartsSize = 0; + if ( functionStarts != NULL ) { + // copy function starts from original cache file to new mapped dylib file + functionStartsSize = functionStarts->datasize(); + memcpy((char*)mh + newFunctionStartsOffset, (char*)mapped_cache + functionStarts->dataoff(), functionStartsSize); + } + const uint64_t newDataInCodeOffset = (newFunctionStartsOffset + functionStartsSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align + uint32_t dataInCodeSize = 0; + if ( dataInCode != NULL ) { + // copy data-in-code info from original cache file to new mapped dylib file + dataInCodeSize = dataInCode->datasize(); + memcpy((char*)mh + newDataInCodeOffset, (char*)mapped_cache + dataInCode->dataoff(), dataInCodeSize); + } + + std::vector exports; + if ( exportsTrieSize != 0 ) { + const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset; + const uint8_t* exportsEnd = &exportsStart[exportsTrieSize]; + mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); + exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end()); + } + + // look for local symbol info in unmapped part of shared cache + dyldCacheHeader* header = (dyldCacheHeader*)mapped_cache; + macho_nlist

* localNlists = NULL; + uint32_t localNlistCount = 0; + const char* localStrings = NULL; + const char* localStringsEnd = NULL; + if ( header->mappingOffset() > offsetof(dyld_cache_header,localSymbolsSize) ) { + dyldCacheLocalSymbolsInfo* localInfo = (dyldCacheLocalSymbolsInfo*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset()); + dyldCacheLocalSymbolEntry* entries = (dyldCacheLocalSymbolEntry*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset() + localInfo->entriesOffset()); + macho_nlist

* allLocalNlists = (macho_nlist

*)(((uint8_t*)localInfo) + localInfo->nlistOffset()); + const uint32_t entriesCount = localInfo->entriesCount(); + for (uint32_t i=0; i < entriesCount; ++i) { + if ( entries[i].dylibOffset() == textOffsetInCache ) { + uint32_t localNlistStart = entries[i].nlistStartIndex(); + localNlistCount = entries[i].nlistCount(); + localNlists = &allLocalNlists[localNlistStart]; + localStrings = ((char*)localInfo) + localInfo->stringsOffset(); + localStringsEnd = &localStrings[localInfo->stringsSize()]; + break; + } + } + } + // compute number of symbols in new symbol table + const macho_nlist

* const mergedSymTabStart = (macho_nlist

*)(((uint8_t*)mapped_cache) + symtab->symoff()); + const macho_nlist

* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()]; + uint32_t newSymCount = symtab->nsyms(); + if ( localNlists != NULL ) { + newSymCount = localNlistCount; + for (const macho_nlist

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { + // skip any locals in cache + if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT ) + continue; + ++newSymCount; + } + } + + // add room for N_INDR symbols for re-exported symbols + newSymCount += exports.size(); + + // copy symbol entries and strings from original cache file to new mapped dylib file + const uint64_t newSymTabOffset = (newDataInCodeOffset + dataInCodeSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align + const uint64_t newIndSymTabOffset = newSymTabOffset + newSymCount*sizeof(macho_nlist

); + const uint64_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t); + macho_nlist

* const newSymTabStart = (macho_nlist

*)(((uint8_t*)mh) + newSymTabOffset); + char* const newStringPoolStart = (char*)mh + newStringPoolOffset; + const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff()); + const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff(); + const char* mergedStringPoolEnd = &mergedStringPoolStart[symtab->strsize()]; + macho_nlist

* t = newSymTabStart; + int poolOffset = 0; + uint32_t symbolsCopied = 0; + newStringPoolStart[poolOffset++] = '\0'; // first pool entry is always empty string + for (const macho_nlist

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { + // if we have better local symbol info, skip any locals here + if ( (localNlists != NULL) && ((s->n_type() & (N_TYPE|N_EXT)) == N_SECT) ) + continue; + *t = *s; + t->set_n_strx(poolOffset); + const char* symName = &mergedStringPoolStart[s->n_strx()]; + if ( symName > mergedStringPoolEnd ) + symName = ""; + strcpy(&newStringPoolStart[poolOffset], symName); + poolOffset += (strlen(symName) + 1); + ++t; + ++symbolsCopied; + } + // recreate N_INDR symbols in extracted dylibs for debugger + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + strcpy(&newStringPoolStart[poolOffset], it->name); + t->set_n_strx(poolOffset); + poolOffset += (strlen(it->name) + 1); + t->set_n_type(N_INDR | N_EXT); + t->set_n_sect(0); + t->set_n_desc(0); + const char* importName = it->importName; + if ( *importName == '\0' ) + importName = it->name; + strcpy(&newStringPoolStart[poolOffset], importName); + t->set_n_value(poolOffset); + poolOffset += (strlen(importName) + 1); + ++t; + ++symbolsCopied; + } + if ( localNlists != NULL ) { + // update load command to reflect new count of locals + dynamicSymTab->set_ilocalsym(symbolsCopied); + dynamicSymTab->set_nlocalsym(localNlistCount); + // copy local symbols + for (uint32_t i=0; i < localNlistCount; ++i) { + const char* localName = &localStrings[localNlists[i].n_strx()]; + if ( localName > localStringsEnd ) + localName = ""; + *t = localNlists[i]; + t->set_n_strx(poolOffset); + strcpy(&newStringPoolStart[poolOffset], localName); + poolOffset += (strlen(localName) + 1); + ++t; + ++symbolsCopied; + } + } + + if ( newSymCount != symbolsCopied ) { + fprintf(stderr, "symbol count miscalculation\n"); + return -1; + } + + // pointer align string pool size + while ( (poolOffset % sizeof(pint_t)) != 0 ) + ++poolOffset; + // copy indirect symbol table + uint32_t* newIndSymTab = (uint32_t*)((char*)mh + newIndSymTabOffset); + memcpy(newIndSymTab, mergedIndSymTab, dynamicSymTab->nindirectsyms()*sizeof(uint32_t)); + + // update load commands + if ( functionStarts != NULL ) { + functionStarts->set_dataoff((uint32_t)newFunctionStartsOffset); + functionStarts->set_datasize(functionStartsSize); + } + if ( dataInCode != NULL ) { + dataInCode->set_dataoff((uint32_t)newDataInCodeOffset); + dataInCode->set_datasize(dataInCodeSize); + } + symtab->set_nsyms(symbolsCopied); + symtab->set_symoff((uint32_t)newSymTabOffset); + symtab->set_stroff((uint32_t)newStringPoolOffset); + symtab->set_strsize(poolOffset); + dynamicSymTab->set_extreloff(0); + dynamicSymTab->set_nextrel(0); + dynamicSymTab->set_locreloff(0); + dynamicSymTab->set_nlocrel(0); + dynamicSymTab->set_indirectsymoff((uint32_t)newIndSymTabOffset); + linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff()); + linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) ); + + // return new size + *newSize = (symtab->stroff()+symtab->strsize()+4095) & (-4096); + + // Xcode 6 leaks in dyld_shared_cache_extract_dylibs + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + ::free((void*)(it->name)); + } + + + return 0; +} + + + +static void make_dirs(const char* file_path) +{ + //printf("make_dirs(%s)\n", file_path); + char dirs[strlen(file_path)+1]; + strcpy(dirs, file_path); + char* lastSlash = strrchr(dirs, '/'); + if ( lastSlash == NULL ) + return; + lastSlash[1] = '\0'; + struct stat stat_buf; + if ( stat(dirs, &stat_buf) != 0 ) { + char* afterSlash = &dirs[1]; + char* slash; + while ( (slash = strchr(afterSlash, '/')) != NULL ) { + *slash = '\0'; + ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + //printf("mkdir(%s)\n", dirs); + *slash = '/'; + afterSlash = slash+1; + } + } +} + + + +template +size_t dylib_maker(const void* mapped_cache, std::vector &dylib_data, const std::vector& segments) { + typedef typename A::P P; + + size_t additionalSize = 0; + for(std::vector::const_iterator it=segments.begin(); it != segments.end(); ++it) { + additionalSize += it->sizem; + } + + dylib_data.reserve(dylib_data.size() + additionalSize); + + uint32_t nfat_archs = 0; + uint32_t offsetInFatFile = 4096; + uint8_t *base_ptr = &dylib_data.front(); + +#define FH reinterpret_cast(base_ptr) +#define FA reinterpret_cast(base_ptr + (8 + (nfat_archs - 1) * sizeof(fat_arch))) + + if(dylib_data.size() >= 4096 && OSSwapBigToHostInt32(FH->magic) == FAT_MAGIC) { + // have fat header, append new arch to end + nfat_archs = OSSwapBigToHostInt32(FH->nfat_arch); + offsetInFatFile = OSSwapBigToHostInt32(FA->offset) + OSSwapBigToHostInt32(FA->size); + } + + dylib_data.resize(offsetInFatFile); + base_ptr = &dylib_data.front(); + + FH->magic = OSSwapHostToBigInt32(FAT_MAGIC); + FH->nfat_arch = OSSwapHostToBigInt32(++nfat_archs); + + FA->cputype = 0; // filled in later + FA->cpusubtype = 0; // filled in later + FA->offset = OSSwapHostToBigInt32(offsetInFatFile); + FA->size = 0; // filled in later + FA->align = OSSwapHostToBigInt32(12); + + // Write regular segments into the buffer + uint64_t totalSize = 0; + uint64_t textOffsetInCache = 0; + for( std::vector::const_iterator it=segments.begin(); it != segments.end(); ++it) { + + if(strcmp(it->segName, "__TEXT") == 0 ) { + textOffsetInCache = it->offset; + const macho_header

*textMH = reinterpret_cast*>((uint8_t*)mapped_cache+textOffsetInCache); + FA->cputype = OSSwapHostToBigInt32(textMH->cputype()); + FA->cpusubtype = OSSwapHostToBigInt32(textMH->cpusubtype()); + + // if this cputype/subtype already exist in fat header, then return immediately + for(uint32_t i=0; i < nfat_archs-1; ++i) { + fat_arch *afa = reinterpret_cast(base_ptr+8)+i; + + if( afa->cputype == FA->cputype + && afa->cpusubtype == FA->cpusubtype) { + //fprintf(stderr, "arch already exists in fat dylib\n"); + dylib_data.resize(offsetInFatFile); + return offsetInFatFile; + } + } + } + + //printf("segName=%s, offset=0x%llX, size=0x%0llX\n", it->segName, it->offset, it->sizem); + std::copy(((uint8_t*)mapped_cache)+it->offset, ((uint8_t*)mapped_cache)+it->offset+it->sizem, std::back_inserter(dylib_data)); + base_ptr = &dylib_data.front(); + totalSize += it->sizem; + } + + FA->size = OSSwapHostToBigInt32(totalSize); + + // optimize linkedit + uint64_t newSize = dylib_data.size(); + optimize_linkedit(((macho_header

*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize); + + // update fat header with new file size + dylib_data.resize((size_t)(offsetInFatFile+newSize)); + base_ptr = &dylib_data.front(); + FA->size = OSSwapHostToBigInt32(newSize); +#undef FH +#undef FA + return offsetInFatFile; +} + + +int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)) +{ + struct stat statbuf; + if (stat(shared_cache_file_path, &statbuf)) { + fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_file_path); + return -1; + } + + int cache_fd = open(shared_cache_file_path, O_RDONLY); + if (cache_fd < 0) { + fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_file_path); + return -1; + } + + void* mapped_cache = mmap(NULL, (size_t)statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); + if (mapped_cache == MAP_FAILED) { + fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_file_path, errno); + return -1; + } + + close(cache_fd); + + // instantiate arch specific dylib maker + size_t (*dylib_create_func)(const void*, std::vector&, const std::vector&) = NULL; + if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64h") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 ) + dylib_create_func = dylib_maker; + else if ( strncmp((char*)mapped_cache, "dyld_v1 armv7", 14) == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64e") == 0 ) + dylib_create_func = dylib_maker; + else { + fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); + munmap(mapped_cache, (size_t)statbuf.st_size); + return -1; + } + + // iterate through all images in cache and build map of dylibs and segments + __block NameToSegments map; + __block int result = dyld_shared_cache_iterate(mapped_cache, (uint32_t)statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { + map[dylibInfo->path].push_back(seg_info(segInfo->name, segInfo->fileOffset, segInfo->fileSize)); + }); + + if(result != 0) { + fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n"); + munmap(mapped_cache, (size_t)statbuf.st_size); + return result; + } + + // for each dylib instantiate a dylib file + dispatch_group_t group = dispatch_group_create(); + dispatch_semaphore_t sema = dispatch_semaphore_create(2); + dispatch_queue_t process_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); + dispatch_queue_t writer_queue = dispatch_queue_create("dyld writer queue", 0); + + __block unsigned count = 0; + + for ( NameToSegments::iterator it = map.begin(); it != map.end(); ++it) { + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + dispatch_group_async(group, process_queue, ^{ + + char dylib_path[PATH_MAX]; + strcpy(dylib_path, extraction_root_path); + strcat(dylib_path, "/"); + strcat(dylib_path, it->first); + + //printf("%s with %lu segments\n", dylib_path, it->second.size()); + // make sure all directories in this path exist + make_dirs(dylib_path); + + // open file, create if does not already exist + int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644); + if ( fd == -1 ) { + fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno); + result = -1; + return; + } + + struct stat statbuf; + if (fstat(fd, &statbuf)) { + fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno); + close(fd); + result = -1; + return; + } + + std::vector *vec = new std::vector((size_t)statbuf.st_size); + if(pread(fd, &vec->front(), vec->size(), 0) != (long)vec->size()) { + fprintf(stderr, "can't read dylib file %s, errnor=%d\n", dylib_path, errno); + close(fd); + result = -1; + return; + } + + const size_t offset = dylib_create_func(mapped_cache, *vec, it->second); + + dispatch_group_async(group, writer_queue, ^{ + progress(count++, (unsigned)map.size()); + + if(offset != vec->size()) { + //Write out the first page, and everything after offset + if( pwrite(fd, &vec->front(), 4096, 0) == -1 + || pwrite(fd, &vec->front() + offset, vec->size() - offset, offset) == -1) { + fprintf(stderr, "error writing, errnor=%d\n", errno); + result = -1; + } + } + + delete vec; + close(fd); + dispatch_semaphore_signal(sema); + }); + }); + } + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + dispatch_release(group); + dispatch_release(writer_queue); + + munmap(mapped_cache, (size_t)statbuf.st_size); + return result; +} + + + +int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path) +{ + return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path, + ^(unsigned , unsigned) {} ); +} + + +//#if 0 +// test program +#include +#include +#include + + +typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)); + +int main(int argc, const char* argv[]) +{ + if ( argc != 3 ) { + fprintf(stderr, "usage: dsc_extractor \n"); + return 1; + } + + //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY); + void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY); + if ( handle == NULL ) { + fprintf(stderr, "dsc_extractor.bundle could not be loaded\n"); + return 1; + } + + extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress"); + if ( proc == NULL ) { + fprintf(stderr, "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n"); + return 1; + } + + int result = (*proc)(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } ); + fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result); + return 0; +} + + +//#endif + + + + diff --git a/tools/dyld/dsc_extractor.h b/tools/dyld/dsc_extractor.h new file mode 100644 index 0000000..a8620d0 --- /dev/null +++ b/tools/dyld/dsc_extractor.h @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _DYLD_SHARED_CACHE_EXTRACTOR_ +#define _DYLD_SHARED_CACHE_EXTRACTOR_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path); +extern int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)); + +#ifdef __cplusplus +} +#endif + +#endif // _DYLD_SHARED_CACHE_EXTRACTOR_ + diff --git a/tools/dyld/dsc_iterator.cpp b/tools/dyld/dsc_iterator.cpp new file mode 100644 index 0000000..7aa84ca --- /dev/null +++ b/tools/dyld/dsc_iterator.cpp @@ -0,0 +1,249 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + + +#include "dsc_iterator.h" +#include "dyld_cache_format.h" +#define NO_ULEB +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" + + +namespace dyld { + + + // convert an address in the shared region where the cache would normally be mapped, into an address where the cache is currently mapped + template + const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr) + { + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (mappings[i].address() <= addr) && (addr < (mappings[i].address() + mappings[i].size())) ) { + uint64_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address(); + const uint8_t* result = &cache[cacheOffset]; + if ( result < cacheEnd ) + return result; + else + return NULL; + } + } + return NULL; + } + + // call the callback block on each segment in this image + template + int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, uint64_t inode,uint64_t modTime, const uint8_t* machHeader, uint64_t cache_unslid_base_address, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) + { + typedef typename A::P P; + typedef typename A::P::E E; + dyld_shared_cache_dylib_info dylibInfo; + dyld_shared_cache_segment_info segInfo; + dylibInfo.version = 2; + dylibInfo.isAlias = (dylibPath < (char*)firstSeg); // paths for aliases are store between cache header and first segment + dylibInfo.machHeader = machHeader; + dylibInfo.path = dylibPath; + dylibInfo.inode = inode; + dylibInfo.modTime = modTime; + const macho_header

* mh = (const macho_header

*)machHeader; + const macho_load_command

* const cmds = (macho_load_command

*)(machHeader + sizeof(macho_header

)); + if ( (machHeader+ mh->sizeofcmds()) > cacheEnd ) + return -1; + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = cmds; + // scan for LC_UUID + dylibInfo.uuid = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == LC_UUID ) { + const uuid_command* uc = (const uuid_command*)cmd; + dylibInfo.uuid = &uc->uuid; + break; + } + cmd = (const macho_load_command

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

::CMD ) { + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + uint64_t fileOffset = segCmd->fileoff(); + // work around until is fixed + if ( fileOffset == 0 ) { + fileOffset = (machHeader - cache); + } + uint64_t sizem = segCmd->vmsize(); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + // clip LINKEDIT size if bigger than cache file + if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) ) + sizem = (cacheEnd-cache)-fileOffset; + } + segInfo.version = 2; + segInfo.name = segCmd->segname(); + segInfo.fileOffset = fileOffset; + segInfo.fileSize = sizem; + if ( segCmd->filesize() > segCmd->vmsize() ) + return -1; + segInfo.address = segCmd->vmaddr(); + segInfo.addressOffset = segInfo.address - cache_unslid_base_address; + callback(&dylibInfo, &segInfo); + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return 0; + } + + + // call walkSegments on each image in the cache + template + 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)) + { + // Sanity check there is at least a header + if ( (size > 0) && (size < 0x7000) ) + return -1; + typedef typename A::P::E E; + typedef typename A::P P; + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + const dyldCacheImageInfo* dylibs = (dyldCacheImageInfo*)&cache[header->imagesOffset()]; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + uint64_t unslid_base_address = mappings[0].address(); + uint64_t greatestMappingOffset = 0; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (size != 0) && (mappings[i].file_offset() > size) ) + return -1; + uint64_t endOffset = mappings[i].file_offset()+mappings[i].size(); + if ( (size != 0) && (endOffset > size) ) + return -1; + if ( endOffset > greatestMappingOffset ) + greatestMappingOffset = endOffset; + } + const uint8_t* cacheEnd = &cache[size]; + if ( size == 0 ) { + // Zero size means old API is being used, assume all mapped + cacheEnd = &cache[greatestMappingOffset]; + } + else { + // verifiy mappings are not bigger than size + if ( size < greatestMappingOffset ) + return -1; + } + // verify all image infos are mapped + if ( (const uint8_t*)&dylibs[header->imagesCount()] > cacheEnd ) + return -1; + const uint8_t* firstSeg = NULL; + for (uint32_t i=0; i < header->imagesCount(); ++i) { + const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); + uint64_t inode = dylibs[i].inode(); + uint64_t modTime = dylibs[i].modTime(); + if ( (const uint8_t*)dylibPath > cacheEnd ) + return -1; + const uint8_t* machHeader = mappedAddress(cache, cacheEnd, dylibs[i].address()); + if ( machHeader == NULL ) + return -1; + if ( machHeader > cacheEnd ) + return -1; + if ( firstSeg == NULL ) + firstSeg = machHeader; + int result = walkSegments(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, unslid_base_address, callback); + if ( result != 0 ) + return result; + } + return 0; + } + +} + + +// Given a pointer to an in-memory copy of a dyld shared cache file, +// this routine will call the callback block once for each segment +// in each dylib in the shared cache file. +// Returns -1 if there was an error, otherwise 0. +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { + const uint8_t* cache = (uint8_t*)shared_cache_file; + if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 x86_64h") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 arm64") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 arm64e") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else + return -1; +} + + +// implement old version by calling new version +int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) +{ + return dyld_shared_cache_iterate(shared_cache_file, 0, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { + callback(dylibInfo->path, segInfo->name, segInfo->fileOffset, segInfo->fileSize, segInfo->address, 0); + }); +} + +// implement non-block version by calling block version +int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData) +{ + return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, + uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { + (*func)(dylibName, segName, offset, size, mappedddress, slide, userData); + }); +} + + +// implement non-slide version by wrapping slide version in block +int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) +{ + dyld_shared_cache_iterator_slide_t wrapper_cb = ^(const char* dylibName, const char* segName, uint64_t offset, + uint64_t size, uint64_t mappedddress, uint64_t slide) { + callback(dylibName, segName, offset, size, mappedddress); + }; + return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, wrapper_cb); +} + +// implement non-slide,non-block version by wrapping slide version in block +int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t func, void* userData) +{ + return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, + uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { + (*func)(dylibName, segName, offset, size, mappedddress, userData); + }); +} + diff --git a/tools/dyld/dsc_iterator.h b/tools/dyld/dsc_iterator.h new file mode 100644 index 0000000..d0ea94d --- /dev/null +++ b/tools/dyld/dsc_iterator.h @@ -0,0 +1,84 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include + +struct dyld_shared_cache_dylib_info { + uint32_t version; // current version 2 + // following fields all exist in version 1 + uint32_t isAlias; // this is alternate path (symlink) + const void* machHeader; // of dylib in mapped cached file + const char* path; // of dylib + const uuid_t* uuid; // of dylib, or NULL is missing + // following fields all exist in version 2 + uint64_t inode; // of dylib file or path hash + uint64_t modTime; // of dylib file +}; +typedef struct dyld_shared_cache_dylib_info dyld_shared_cache_dylib_info; + +struct dyld_shared_cache_segment_info { + uint64_t version; // initial version 1 + // following fields exist in version 1 + const char* name; // of segment + uint64_t fileOffset; // of segment in cache file + uint64_t fileSize; // of segment + uint64_t address; // of segment when cache mapped with ASLR (sliding) off + // following fields exist in version 2 + uint64_t addressOffset; // of segment from base of mapped cache +}; +typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info; + + +#ifdef __cplusplus +extern "C" { +#endif + + +// Given a pointer and size of an in-memory copy of a dyld shared cache file, +// this routine will call the callback block once for each segment in each dylib +// in the shared cache file. +// Returns -1 if there was an error, otherwise 0. +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)); + + + +// +// The following iterator functions are deprecated: +// +typedef void (^dyld_shared_cache_iterator_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress); +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); +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); +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); + +extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData) __attribute__((deprecated)); +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)); + + +#ifdef __cplusplus +} +#endif diff --git a/tools/dyld/dyld_cache_format.h b/tools/dyld/dyld_cache_format.h new file mode 100644 index 0000000..57b5d07 --- /dev/null +++ b/tools/dyld/dyld_cache_format.h @@ -0,0 +1,265 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __DYLD_CACHE_FORMAT__ +#define __DYLD_CACHE_FORMAT__ + +#include +#include +#include +#include + + +struct dyld_cache_header +{ + char magic[16]; // e.g. "dyld_v0 i386" + uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info + uint32_t mappingCount; // number of dyld_cache_mapping_info entries + uint32_t imagesOffset; // file offset to first dyld_cache_image_info + uint32_t imagesCount; // number of dyld_cache_image_info entries + uint64_t dyldBaseAddress; // base address of dyld when cache was built + uint64_t codeSignatureOffset; // file offset of code signature blob + uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) + uint64_t slideInfoOffset; // file offset of kernel slid info + uint64_t slideInfoSize; // size of kernel slid info + uint64_t localSymbolsOffset; // file offset of where local symbols are stored + uint64_t localSymbolsSize; // size of local symbols information + uint8_t uuid[16]; // unique value for each shared cache file + uint64_t cacheType; // 0 for development, 1 for production + uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses + uint32_t branchPoolsCount; // number of uint64_t entries + uint64_t accelerateInfoAddr; // (unslid) address of optimization info + uint64_t accelerateInfoSize; // size of optimization info + uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info + uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries +}; + + +struct dyld_cache_mapping_info { + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint32_t maxProt; + uint32_t initProt; +}; + +struct dyld_cache_image_info +{ + uint64_t address; + uint64_t modTime; + uint64_t inode; + uint32_t pathFileOffset; + uint32_t pad; +}; + +struct dyld_cache_image_info_extra +{ + uint64_t exportsTrieAddr; // address of trie in unslid cache + uint64_t weakBindingsAddr; + uint32_t exportsTrieSize; + uint32_t weakBindingsSize; + uint32_t dependentsStartArrayIndex; + uint32_t reExportsStartArrayIndex; +}; + + +struct dyld_cache_accelerator_info +{ + uint32_t version; // currently 1 + uint32_t imageExtrasCount; // does not include aliases + uint32_t imagesExtrasOffset; // offset into this chunk of first dyld_cache_image_info_extra + uint32_t bottomUpListOffset; // offset into this chunk to start of 16-bit array of sorted image indexes + uint32_t dylibTrieOffset; // offset into this chunk to start of trie containing all dylib paths + uint32_t dylibTrieSize; // size of trie containing all dylib paths + uint32_t initializersOffset; // offset into this chunk to start of initializers list + uint32_t initializersCount; // size of initializers list + uint32_t dofSectionsOffset; // offset into this chunk to start of DOF sections list + uint32_t dofSectionsCount; // size of initializers list + uint32_t reExportListOffset; // offset into this chunk to start of 16-bit array of re-exports + uint32_t reExportCount; // size of re-exports + uint32_t depListOffset; // offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward) + uint32_t depListCount; // size of dependencies + uint32_t rangeTableOffset; // offset into this chunk to start of ss + uint32_t rangeTableCount; // size of dependencies + uint64_t dyldSectionAddr; // address of libdyld's __dyld section in unslid cache +}; + +struct dyld_cache_accelerator_initializer +{ + uint32_t functionOffset; // address offset from start of cache mapping + uint32_t imageIndex; +}; + +struct dyld_cache_range_entry +{ + uint64_t startAddress; // unslid address of start of region + uint32_t size; + uint32_t imageIndex; +}; + +struct dyld_cache_accelerator_dof +{ + uint64_t sectionAddress; // unslid address of start of region + uint32_t sectionSize; + uint32_t imageIndex; +}; + +struct dyld_cache_image_text_info +{ + uuid_t uuid; + uint64_t loadAddress; // unslid address of start of __TEXT + uint32_t textSegmentSize; + uint32_t pathOffset; // offset from start of cache file +}; + +// The rebasing info is to allow the kernel to lazily rebase DATA pages of the +// dyld shared cache. Rebasing is adding the slide to interior pointers. +struct dyld_cache_slide_info +{ + uint32_t version; // currently 1 + uint32_t toc_offset; + uint32_t toc_count; + uint32_t entries_offset; + uint32_t entries_count; + uint32_t entries_size; // currently 128 + // uint16_t toc[toc_count]; + // entrybitmap entries[entries_count]; +}; + + +// The version 2 of the slide info uses a different compression scheme. Since +// only interior pointers (pointers that point within the cache) are rebased +// (slid), we know the possible range of the pointers and thus know there are +// unused bits in each pointer. We use those bits to form a linked list of +// locations needing rebasing in each page. +// +// Definitions: +// +// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size +// pageStarts[] = info + info->page_starts_offset +// pageExtras[] = info + info->page_extras_offset +// valueMask = ~(info->delta_mask) +// deltaShift = __builtin_ctzll(info->delta_mask) - 2 +// +// There are three cases: +// +// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE +// The page contains no values that need rebasing. +// +// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 +// All rebase locations are in one linked list. The offset of the first +// rebase location in the page is pageStarts[pageIndex] * 4. +// +// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA +// Multiple linked lists are needed for all rebase locations in a page. +// The pagesExtras array contains 2 or more entries each of which is the +// start of a new linked list in the page. The first is at: +// extrasStartIndex = (pageStarts[pageIndex] & 0x3FFF) +// The next is at extrasStartIndex+1. The last is denoted by +// having the high bit (DYLD_CACHE_SLIDE_PAGE_ATTR_END) of the pageExtras[] +// set. +// +// For 64-bit architectures, there is always enough free bits to encode all +// possible deltas. The info->delta_mask field shows where the delta is located +// in the pointer. That value must be masked off (valueMask) before the slide +// is added to the pointer. +// +// For 32-bit architectures, there are only three bits free (the three most +// significant bits). To extract the delta, you must first subtract value_add +// from the pointer value, then AND with delta_mask, then shift by deltaShift. +// That still leaves a maximum delta to the next rebase location of 28 bytes. +// To reduce the number or chains needed, an optimization was added. Turns +// out zero is common in the DATA region. A zero can be turned into a +// non-rebasing entry in the linked list. The can be done because nothing +// in the shared cache should point out of its dylib to the start of the shared +// cache. +// +// The code for processing a linked list (chain) is: +// +// uint32_t delta = 1; +// while ( delta != 0 ) { +// uint8_t* loc = pageStart + pageOffset; +// uintptr_t rawValue = *((uintptr_t*)loc); +// delta = ((rawValue & deltaMask) >> deltaShift); +// uintptr_t newValue = (rawValue & valueMask); +// if ( newValue != 0 ) { +// newValue += valueAdd; +// newValue += slideAmount; +// } +// *((uintptr_t*)loc) = newValue; +// pageOffset += delta; +// } +// +// +struct dyld_cache_slide_info2 +{ + uint32_t version; // currently 2 + uint32_t page_size; // currently 4096 (may also be 16384) + uint32_t page_starts_offset; + uint32_t page_starts_count; + uint32_t page_extras_offset; + uint32_t page_extras_count; + uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location + uint64_t value_add; + //uint16_t page_starts[page_starts_count]; + //uint16_t page_extras[page_extras_count]; +}; +#define DYLD_CACHE_SLIDE_PAGE_ATTRS 0xC000 // high bits of uint16_t are flags +#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 0x8000 // index is into extras array (not starts array) +#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 0x4000 // page has no rebasing +#define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page + + +struct dyld_cache_local_symbols_info +{ + uint32_t nlistOffset; // offset into this chunk of nlist entries + uint32_t nlistCount; // count of nlist entries + uint32_t stringsOffset; // offset into this chunk of string pool + uint32_t stringsSize; // byte count of string pool + uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry + uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array +}; + +struct dyld_cache_local_symbols_entry +{ + uint32_t dylibOffset; // offset in cache file of start of dylib + uint32_t nlistStartIndex; // start index of locals for this dylib + uint32_t nlistCount; // number of local symbols for this dylib +}; + + + +#define MACOSX_DYLD_SHARED_CACHE_DIR "/private/var/db/dyld/" +#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" +#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" +#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT ".development" + +static const uint64_t kDyldSharedCacheTypeDevelopment = 0; +static const uint64_t kDyldSharedCacheTypeProduction = 1; + + + + +#endif // __DYLD_CACHE_FORMAT__ + + diff --git a/tools/dyld/dyld_shared_cache_util.cpp b/tools/dyld/dyld_shared_cache_util.cpp new file mode 100644 index 0000000..4c56c35 --- /dev/null +++ b/tools/dyld/dyld_shared_cache_util.cpp @@ -0,0 +1,940 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dsc_iterator.h" +#include "dsc_extractor.h" +#include "dyld_cache_format.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" +#include "Trie.hpp" + +enum Mode { + modeNone, + modeList, + modeMap, + modeDependencies, + modeSlideInfo, + modeAcceleratorInfo, + modeTextInfo, + modeLinkEdit, + modeLocalSymbols, + modeInfo, + modeSize, + modeExtract +}; + +struct Options { + Mode mode; + const char* dependentsOfPath; + const void* mappedCache; + const char* extractionDir; + bool printUUIDs; + bool printVMAddrs; + bool printDylibVersions; + bool printInodes; +}; + +struct TextInfo { + uint64_t textSize; + const char* path; +}; + +struct TextInfoSorter { + bool operator()(const TextInfo& left, const TextInfo& right) { + return (left.textSize > right.textSize); + } +}; + +struct Results { + std::map pageToContent; + uint64_t linkeditBase; + bool dependentTargetFound; + std::vector textSegments; +}; + + + +void usage() { + fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents [ -versions ] | -linkedit | -map | -slide_info | -info | -extract [ shared-cache-file ] \n"); +} + +#if __x86_64__ +static bool isHaswell() +{ + // check system is capable of running x86_64h code + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + mach_port_t hostPort = mach_host_self(); + kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); + mach_port_deallocate(mach_task_self(), hostPort); + if ( result != KERN_SUCCESS ) + return false; + return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H ); +} +#endif + +/* + * Get the path to the native shared cache for this host + */ +static const char* default_shared_cache_path() { +#if __i386__ + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "i386"; +#elif __x86_64__ + if ( isHaswell() ) + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64h"; + else + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64"; +#elif __ARM_ARCH_5TEJ__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv5"; +#elif __ARM_ARCH_6K__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv6"; +#elif __ARM_ARCH_7K__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7k"; +#elif __ARM_ARCH_7A__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7"; +#elif __ARM_ARCH_7F__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7f"; +#elif __ARM_ARCH_7S__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7s"; +#elif __arm64e__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64e"; +#elif __arm64__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64"; +#else + #error unsupported architecture +#endif +} + +typedef void (*segment_callback_t)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results); + + + +/* + * List dependencies from the mach-o header at headerAddr + * in the same format as 'otool -L' + */ +template +void print_dependencies(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) { + typedef typename A::P P; + typedef typename A::P::E E; + + if ( strcmp(options.dependentsOfPath, dylibInfo->path) != 0 ) + return; + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + + const macho_dylib_command

* dylib_cmd; + const macho_header

* mh = (const macho_header

*)dylibInfo->machHeader; + const macho_load_command

* const cmds = (macho_load_command

*)((uintptr_t)dylibInfo->machHeader + sizeof(macho_header

)); + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_LOAD_DYLIB: + case LC_ID_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + dylib_cmd = (macho_dylib_command

*)cmd; + if ( options.printDylibVersions ) { + uint32_t compat_vers = dylib_cmd->compatibility_version(); + uint32_t current_vers = dylib_cmd->current_version(); + printf("\t%s", dylib_cmd->name()); + if ( compat_vers != 0xFFFFFFFF ) { + printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n", + (compat_vers >> 16), + (compat_vers >> 8) & 0xff, + (compat_vers) & 0xff, + (current_vers >> 16), + (current_vers >> 8) & 0xff, + (current_vers) & 0xff); + } + else { + printf("\n"); + } + } + else { + printf("\t%s\n", dylib_cmd->name()); + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + results.dependentTargetFound = true; +} + +/* + * Print out a dylib from the shared cache, optionally including the UUID or unslid load address + */ +template +void print_list(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) +{ + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + + if ( options.printVMAddrs ) + printf("0x%08llX ", segInfo->address); + if ( options.printInodes ) + printf("0x%08llX 0x%08llX ", dylibInfo->inode, dylibInfo->modTime); + if ( options.printUUIDs ) { + if ( dylibInfo->uuid != NULL ) { + const uint8_t* uuid = (uint8_t*)dylibInfo->uuid;; + printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } + else + printf("< no uuid in dylib > "); + } + if ( dylibInfo->isAlias ) + printf("[alias] %s\n", dylibInfo->path); + else + printf("%s\n", dylibInfo->path); +} + + +template +void collect_size(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) +{ + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + if ( dylibInfo->isAlias ) + return; + + TextInfo info; + info.textSize = segInfo->fileSize; + info.path = dylibInfo->path; + results.textSegments.push_back(info); +} + + + + +static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, const char* message, Results& results) +{ + for (uint32_t p = pageStart; p <= pageEnd; p += 4096) { + std::map::iterator pos = results.pageToContent.find(p); + if ( pos == results.pageToContent.end() ) { + results.pageToContent[p] = strdup(message); + } + else { + const char* oldMessage = pos->second; + char* newMesssage; + asprintf(&newMesssage, "%s, %s", oldMessage, message); + results.pageToContent[p] = newMesssage; + ::free((void*)oldMessage); + } + } +} + + +/* + * get LINKEDIT info for dylib + */ +template +void process_linkedit(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) { + typedef typename A::P P; + typedef typename A::P::E E; + // filter out symlinks + if ( dylibInfo->isAlias ) + return; + const macho_header

* mh = (const macho_header

*)dylibInfo->machHeader; + uint32_t ncmds = mh->ncmds(); + const macho_load_command

* const cmds = (macho_load_command

*)((long)mh + sizeof(macho_header

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < ncmds; i++) { + if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) { + macho_dyld_info_command

* dyldInfo = (macho_dyld_info_command

*)cmd; + char message[1000]; + const char* shortName = strrchr(dylibInfo->path, '/') + 1; + // add export trie info + if ( dyldInfo->export_size() != 0 ) { + //printf("export_off=0x%X\n", dyldInfo->export_off()); + uint32_t exportPageOffsetStart = dyldInfo->export_off() & (-4096); + uint32_t exportPageOffsetEnd = (dyldInfo->export_off() + dyldInfo->export_size()) & (-4096); + sprintf(message, "exports from %s", shortName); + add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message, results); + } + // add binding info + if ( dyldInfo->bind_size() != 0 ) { + uint32_t bindPageOffsetStart = dyldInfo->bind_off() & (-4096); + uint32_t bindPageOffsetEnd = (dyldInfo->bind_off() + dyldInfo->bind_size()) & (-4096); + sprintf(message, "bindings from %s", shortName); + add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message, results); + } + // add lazy binding info + if ( dyldInfo->lazy_bind_size() != 0 ) { + uint32_t lazybindPageOffsetStart = dyldInfo->lazy_bind_off() & (-4096); + uint32_t lazybindPageOffsetEnd = (dyldInfo->lazy_bind_off() + dyldInfo->lazy_bind_size()) & (-4096); + sprintf(message, "lazy bindings from %s", shortName); + add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message, results); + } + // add weak binding info + if ( dyldInfo->weak_bind_size() != 0 ) { + uint32_t weakbindPageOffsetStart = dyldInfo->weak_bind_off() & (-4096); + uint32_t weakbindPageOffsetEnd = (dyldInfo->weak_bind_off() + dyldInfo->weak_bind_size()) & (-4096); + sprintf(message, "weak bindings from %s", shortName); + add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message, results); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +/* + * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built + */ +template +void print_map(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, const Options& options, Results& results) { + if ( !dylibInfo->isAlias ) + printf("0x%08llX - 0x%08llX %s %s\n", segInfo->address, segInfo->address + segInfo->fileSize, segInfo->name, dylibInfo->path); +} + + +static void checkMode(Mode mode) { + if ( mode != modeNone ) { + fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, -extract, or -size\n"); + usage(); + exit(1); + } +} + +int main (int argc, const char* argv[]) { + + const char* sharedCachePath = default_shared_cache_path(); + + Options options; + options.mode = modeNone; + options.printUUIDs = false; + options.printVMAddrs = false; + options.printDylibVersions = false; + options.printInodes = false; + options.dependentsOfPath = NULL; + options.extractionDir = NULL; + + for (uint32_t i = 1; i < argc; i++) { + const char* opt = argv[i]; + if (opt[0] == '-') { + if (strcmp(opt, "-list") == 0) { + checkMode(options.mode); + options.mode = modeList; + } + else if (strcmp(opt, "-dependents") == 0) { + checkMode(options.mode); + options.mode = modeDependencies; + options.dependentsOfPath = argv[++i]; + if ( i >= argc ) { + fprintf(stderr, "Error: option -depdendents requires an argument\n"); + usage(); + exit(1); + } + } + else if (strcmp(opt, "-linkedit") == 0) { + checkMode(options.mode); + options.mode = modeLinkEdit; + } + else if (strcmp(opt, "-info") == 0) { + checkMode(options.mode); + options.mode = modeInfo; + } + else if (strcmp(opt, "-slide_info") == 0) { + checkMode(options.mode); + options.mode = modeSlideInfo; + } + else if (strcmp(opt, "-accelerator_info") == 0) { + checkMode(options.mode); + options.mode = modeAcceleratorInfo; + } + else if (strcmp(opt, "-text_info") == 0) { + checkMode(options.mode); + options.mode = modeTextInfo; + } + else if (strcmp(opt, "-local_symbols") == 0) { + checkMode(options.mode); + options.mode = modeLocalSymbols; + } + else if (strcmp(opt, "-map") == 0) { + checkMode(options.mode); + options.mode = modeMap; + } + else if (strcmp(opt, "-size") == 0) { + checkMode(options.mode); + options.mode = modeSize; + } + else if (strcmp(opt, "-extract") == 0) { + checkMode(options.mode); + options.mode = modeExtract; + options.extractionDir = argv[++i]; + if ( i >= argc ) { + fprintf(stderr, "Error: option -extract requires a directory argument\n"); + usage(); + exit(1); + } + } + else if (strcmp(opt, "-uuid") == 0) { + options.printUUIDs = true; + } + else if (strcmp(opt, "-inode") == 0) { + options.printInodes = true; + } + else if (strcmp(opt, "-versions") == 0) { + options.printDylibVersions = true; + } + else if (strcmp(opt, "-vmaddr") == 0) { + options.printVMAddrs = true; + } + else { + fprintf(stderr, "Error: unrecognized option %s\n", opt); + usage(); + exit(1); + } + } + else { + sharedCachePath = opt; + } + } + + if ( options.mode == modeNone ) { + fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n"); + usage(); + exit(1); + } + + if ( options.mode != modeSlideInfo ) { + if ( options.printUUIDs && (options.mode != modeList) ) + fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n"); + + if ( options.printVMAddrs && (options.mode != modeList) ) + fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n"); + + if ( options.printDylibVersions && (options.mode != modeDependencies) ) + fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n"); + + if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) { + fprintf(stderr, "Error: -dependents given, but no dylib path specified\n"); + usage(); + exit(1); + } + } + + struct stat statbuf; + if ( ::stat(sharedCachePath, &statbuf) == -1 ) { + fprintf(stderr, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath, errno); + exit(1); + } + + int cache_fd = ::open(sharedCachePath, O_RDONLY); + if ( cache_fd < 0 ) { + fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno); + exit(1); + } + options.mappedCache = ::mmap(NULL, (size_t)statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); + if (options.mappedCache == MAP_FAILED) { + fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", sharedCachePath, errno); + exit(1); + } + + if ( options.mode == modeSlideInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( header->slideInfoOffset() == 0 ) { + fprintf(stderr, "Error: dyld shared cache does not contain slide info\n"); + exit(1); + } + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + const dyldCacheFileMapping* dataMapping = &mappings[1]; + uint64_t dataStartAddress = dataMapping->address(); + uint64_t dataSize = dataMapping->size(); + const dyldCacheSlideInfo* slideInfoHeader = (dyldCacheSlideInfo*)((char*)options.mappedCache+header->slideInfoOffset()); + printf("slide info version=%d\n", slideInfoHeader->version()); + if ( slideInfoHeader->version() == 1 ) { + printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096); + const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset()); + for(int i=0; i < slideInfoHeader->toc_count(); ++i) { + printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, slideInfoHeader->toc(i)); + const dyldCacheSlideInfoEntry* entry = &entries[slideInfoHeader->toc(i)]; + for(int j=0; j < slideInfoHeader->entries_size(); ++j) + printf("%02X", entry->bits[j]); + printf("\n"); + } + } + else if ( slideInfoHeader->version() == 2 ) { + const dyldCacheSlideInfo2* slideInfo = (dyldCacheSlideInfo2*)(slideInfoHeader); + printf("page_size=%d\n", slideInfo->page_size()); + printf("delta_mask=0x%016llX\n", slideInfo->delta_mask()); + printf("value_add=0x%016llX\n", slideInfo->value_add()); + printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count(), slideInfo->page_extras_count()); + const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset()); + const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset()); + for (int i=0; i < slideInfo->page_starts_count(); ++i) { + const uint16_t start = starts[i]; + if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) { + printf("page[% 5d]: no rebasing\n", i); + } + else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { + printf("page[% 5d]: ", i); + int j=(start & 0x3FFF); + bool done = false; + do { + uint16_t aStart = extras[j]; + printf("start=0x%04X ", aStart & 0x3FFF); + done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END); + ++j; + } while ( !done ); + printf("\n"); + } + else { + printf("page[% 5d]: start=0x%04X\n", i, starts[i]); + } + } + } + } + else if ( options.mode == modeInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + printf("uuid: "); + if ( header->mappingOffset() >= 0x68 ) { + const uint8_t* uuid = header->uuid(); + printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } + else { + printf("n/a\n"); + } + if ( header->mappingOffset() >= 0xE0 ) { + // HACK until this uses new header + uint32_t platform = *((uint32_t*)(((char*)header) + 0xD8)); + uint32_t simulator = *((uint32_t*)(((char*)header) + 0xDC)); + switch (platform) { + case 1: + printf("platform: macOS\n"); + break; + case 2: + if ( simulator & 0x400 ) + printf("platform: iOS simulator\n"); + else + printf("platform: iOS\n"); + break; + case 3: + if ( simulator & 0x400 ) + printf("platform: tvOS simulator\n"); + else + printf("platform: tvOS\n"); + break; + case 4: + if ( simulator & 0x400 ) + printf("platform: watchOS simulator\n"); + else + printf("platform: watchOS\n"); + break; + case 5: + printf("platform: bridgeOS\n"); + break; + default: + printf("platform: 0x%08X 0x%08X\n", platform, simulator); + } + } + printf("image count: %u\n", header->imagesCount()); + if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) { + printf("branch pool count: %u\n", header->branchPoolsCount()); + } + printf("mappings:\n"); + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( mappings[i].init_prot() & VM_PROT_EXECUTE ) + printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(), + mappings[i].address(), mappings[i].address() + mappings[i].size()); + else if ( mappings[i]. init_prot() & VM_PROT_WRITE ) + printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(), + mappings[i].address(), mappings[i].address() + mappings[i].size()); + else if ( mappings[i].init_prot() & VM_PROT_READ ) + printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(), + mappings[i].address(), mappings[i].address() + mappings[i].size()); + } + if ( header->codeSignatureOffset() != 0 ) { + uint64_t size = statbuf.st_size - header->codeSignatureOffset(); + uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size(); + if ( size != 0 ) + printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + size/(1024*1024), header->codeSignatureOffset(), header->codeSignatureOffset() + size, csAddr, csAddr + size); + } + printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n", + header->slideInfoSize()/1024, header->slideInfoOffset(), header->slideInfoOffset() + header->slideInfoSize()); + if ( header->localSymbolsOffset() != 0 ) + printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n", + header->localSymbolsSize()/(1024*1024), header->localSymbolsOffset(), header->localSymbolsOffset() + header->localSymbolsSize()); + if ( (header->mappingOffset() >= 0x78) && (header->accelerateInfoSize() != 0) ) + printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n", + header->accelerateInfoSize()/1024, header->accelerateInfoAddr(), header->accelerateInfoAddr() + header->accelerateInfoSize()); + } + else if ( options.mode == modeAcceleratorInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( (header->mappingOffset() < sizeof(dyldCacheHeader)) || (header->accelerateInfoSize() == 0) ) { + printf("no accelerator info\n"); + } + else { + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + uint64_t aiAddr = header->accelerateInfoAddr(); + dyldCacheAcceleratorInfo* accelInfo = NULL; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (mappings[i].address() <= aiAddr) && (aiAddr < mappings[i].address()+mappings[i].size()) ) { + uint64_t offset = aiAddr - mappings[i].address() + mappings[i].file_offset(); + accelInfo = (dyldCacheAcceleratorInfo*)((uint8_t*)options.mappedCache + offset); + } + } + if ( accelInfo == NULL ) { + printf("accelerator info not in any mapped range\n"); + } + else { + const dyldCacheImageInfo* images = (dyldCacheImageInfo*)((char*)options.mappedCache + header->imagesOffset()); + const dyldCacheImageInfoExtra* imagesExtra = (dyldCacheImageInfoExtra*)((char*)accelInfo + accelInfo->imagesExtrasOffset()); + const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset()); + const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset()); + printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount()); + for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) { + printf(" image[%3u] %s:\n", i, (char*)options.mappedCache +images[i].pathFileOffset()); + printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr(), imagesExtra[i].exportsTrieSize()); + if ( imagesExtra[i].weakBindingsSize() ) + printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr(), imagesExtra[i].weakBindingsSize()); + printf(" dependents: "); + for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex(); dependencyArray[d] != 0xFFFF; ++d) { + uint16_t depIndex = dependencyArray[d]; + if ( depIndex & 0x8000 ) + printf(" up(%d) ", depIndex & 0x7FFF); + else + printf(" %d ", depIndex); + } + printf("\n"); + printf(" re-exports: "); + for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex(); reExportArray[r] != 0xFFFF; ++r) + printf(" %d ", reExportArray[r]); + printf("\n"); + } + printf("libdyld.dylib:\n"); + printf(" __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr()); + printf("initializers (count=%u):\n", accelInfo->initializersCount()); + const dyldCacheAcceleratorInitializer* initializers = (dyldCacheAcceleratorInitializer*)((char*)accelInfo + accelInfo->initializersOffset()); + for (uint32_t i=0; i < accelInfo->initializersCount(); ++i) { + printf(" image[%3u] 0x%llX\n", initializers[i].imageIndex(), mappings[0].address() + initializers[i].functionOffset()); + } + printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount()); + const dyldCacheAcceleratorDOFEntry* dofs = (dyldCacheAcceleratorDOFEntry*)((char*)accelInfo + accelInfo->dofSectionsOffset()); + for (uint32_t i=0; i < accelInfo->dofSectionsCount(); ++i) { + printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex(), dofs[i].sectionAddress(), dofs[i].sectionAddress()+dofs[i].sectionSize()); + } + printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount()); + const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset()); + for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) { + unsigned imageIndex = bottomUpArray[i]; + if ( imageIndex < accelInfo->imageExtrasCount() ) + printf(" image[%3u] %s\n", imageIndex, (char*)options.mappedCache + images[imageIndex].pathFileOffset()); + else + printf(" image[%3u] BAD INDEX\n", imageIndex); + } + printf("range table (count=%u):\n", accelInfo->rangeTableCount()); + const dyldCacheAcceleratorRangeEntry* rangeTable = (dyldCacheAcceleratorRangeEntry*)((char*)accelInfo + accelInfo->rangeTableOffset()); + for (uint32_t i=0; i < accelInfo->rangeTableCount(); ++i) { + const dyldCacheAcceleratorRangeEntry& entry = rangeTable[i]; + printf(" 0x%llX -> 0x%llX %s\n", entry.startAddress(), entry.startAddress() + entry.size(), (char*)options.mappedCache + images[entry.imageIndex()].pathFileOffset()); + } + printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize()); + const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset(); + const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize(); + std::vector dylibEntries; + if ( !Trie::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) ) + printf(" malformed dylibs trie\n"); + for (const DylibIndexTrie::Entry& x : dylibEntries) { + printf(" image[%3u] %s\n", x.info.index, x.name.c_str()); + } + } + } + } + else if ( options.mode == modeTextInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( (header->mappingOffset() < sizeof(dyldCacheHeader)) || (header->imagesTextCount() == 0) ) { + printf("no text info\n"); + } + else { + const dyldCacheImageTextInfo* imagesText = (dyldCacheImageTextInfo*)((char*)options.mappedCache + header->imagesTextOffset()); + const dyldCacheImageTextInfo* imagesTextEnd = &imagesText[header->imagesTextCount()]; + printf("dylib text infos (count=%llu):\n", header->imagesTextCount()); + for (const dyldCacheImageTextInfo* p=imagesText; p < imagesTextEnd; ++p) { + printf(" 0x%09llX -> 0x%09llX <", p->loadAddress(), p->loadAddress() + p->textSegmentSize()); + for (int i=0; i<16; ++i) { + switch (i) { + case 4: + case 6: + case 8: + case 10: + printf("-"); + break; + } + printf("%02X", p->uuid()[i]); + } + printf("> %s\n", (char*)options.mappedCache + p->pathOffset()); + } + } + } + else if ( options.mode == modeLocalSymbols ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( header->localSymbolsOffset() == 0 ) { + fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n"); + exit(1); + } + const bool is64 = (strstr((char*)options.mappedCache, "64") != NULL); + const dyldCacheImageInfo* imageInfos = (dyldCacheImageInfo*)((char*)options.mappedCache + header->imagesOffset()); + const dyldCacheLocalSymbolsInfo* localsInfo = (dyldCacheLocalSymbolsInfo*)((char*)options.mappedCache + header->localSymbolsOffset()); + const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->nlistOffset()); + const uint32_t nlistCount = localsInfo->nlistCount(); + const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12; + const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->stringsOffset()); + const uint32_t stringsSize = localsInfo->stringsSize(); + const uint32_t entriesCount = localsInfo->entriesCount(); + const dyldCacheLocalSymbolEntry* entries = (dyldCacheLocalSymbolEntry*)((char*)localsInfo + localsInfo->entriesOffset()); + printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize); + printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize); + printf("local symbols by dylib (count=%d):\n", entriesCount); + //const char* stringPool = (char*)options.mappedCache + stringsFileOffset; + for (int i=0; i < entriesCount; ++i) { + const char* imageName = (char*)options.mappedCache + imageInfos[i].pathFileOffset(); + printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex(), entries[i].nlistCount(), imageName); + #if 0 + if ( is64 ) { + const nlist_64* symTab = (nlist_64*)((char*)options.mappedCache + nlistFileOffset); + for (int e=0; e < entries[i].nlistCount(); ++e) { + const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e]; + printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]); + printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value); + } + } + #endif + } + } + else if ( options.mode == modeExtract ) { + char pathBuffer[PATH_MAX]; + uint32_t bufferSize = PATH_MAX; + if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) { + fprintf(stderr, "Error: could not get path of program\n"); + return 1; + } + char* last = strrchr(pathBuffer, '/'); + strcpy(last+1, "../../lib/dsc_extractor.bundle"); + void* handle = dlopen(pathBuffer, RTLD_LAZY); + if ( handle == NULL ) { + fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer); + return 1; + } + + typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)); + + extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress"); + if ( proc == NULL ) { + fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n"); + return 1; + } + + int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } ); + return result; + } + else { + segment_callback_t callback = nullptr; + if ( strcmp((char*)options.mappedCache, "dyld_v1 i386") == 0 ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else if ( (strcmp((char*)options.mappedCache, "dyld_v1 x86_64") == 0) + || (strcmp((char*)options.mappedCache, "dyld_v1 x86_64h") == 0) ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else if ( (strncmp((char*)options.mappedCache, "dyld_v1 armv", 14) == 0) + || (strncmp((char*)options.mappedCache, "dyld_v1 armv", 13) == 0) ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else if ( (strcmp((char*)options.mappedCache, "dyld_v1 arm64") == 0) + || (strcmp((char*)options.mappedCache, "dyld_v1 arm64e") == 0) ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else { + fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); + exit(1); + } + + __block Results results; + results.dependentTargetFound = false; + int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)statbuf.st_size, + ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) { + (callback)(dylibInfo, segInfo, options, results); + }); + if ( iterateResult != 0 ) { + fprintf(stderr, "Error: malformed shared cache file\n"); + exit(1); + } + + if ( options.mode == modeLinkEdit ) { + // dump -linkedit information + for (std::map::iterator it = results.pageToContent.begin(); it != results.pageToContent.end(); ++it) { + printf("0x%08X %s\n", it->first, it->second); + } + } + else if ( options.mode == modeSize ) { + std::sort(results.textSegments.begin(), results.textSegments.end(), TextInfoSorter()); + for (std::vector::iterator it = results.textSegments.begin(); it != results.textSegments.end(); ++it) { + printf(" 0x%08llX %s\n", it->textSize, it->path); + } + } + + if ( (options.mode == modeDependencies) && options.dependentsOfPath && !results.dependentTargetFound) { + fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath); + exit(1); + } + } + return 0; +}