dyld_cache_format.h   [plain text]


/* -*- 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 <stdint.h>
#include <uuid/uuid.h>


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    slideInfoOffsetUnused;  // unused.  Used to be file offset of kernel slid info
    uint64_t    slideInfoSizeUnused;    // unused.  Used to be 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
    uint64_t    patchInfoAddr;          // (unslid) address of dyld_cache_patch_info
    uint64_t    patchInfoSize;          // Size of all of the patch information pointed to via the dyld_cache_patch_info
    uint64_t    otherImageGroupAddrUnused;    // unused
    uint64_t    otherImageGroupSizeUnused;    // unused
    uint64_t    progClosuresAddr;       // (unslid) address of list of program launch closures
    uint64_t    progClosuresSize;       // size of list of program launch closures
    uint64_t    progClosuresTrieAddr;   // (unslid) address of trie of indexes into program launch closures
    uint64_t    progClosuresTrieSize;   // size of trie of indexes into program launch closures
    uint32_t    platform;               // platform number (macOS=1, etc)
    uint32_t    formatVersion          : 8,  // dyld3::closure::kFormatVersion
                dylibsExpectedOnDisk   : 1,  // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid
                simulator              : 1,  // for simulator of specified platform
                locallyBuiltCache      : 1,  // 0 for B&I built cache, 1 for locally built cache
                builtFromChainedFixups : 1,  // some dylib in cache was built using chained fixups, so patch tables must be used for overrides
                padding                : 20; // TBD
    uint64_t    sharedRegionStart;      // base load address of cache if not slid
    uint64_t    sharedRegionSize;       // overall size of region cache can be mapped into
    uint64_t    maxSlide;               // runtime slide of cache can be between zero and this value
    uint64_t    dylibsImageArrayAddr;   // (unslid) address of ImageArray for dylibs in this cache
    uint64_t    dylibsImageArraySize;   // size of ImageArray for dylibs in this cache
    uint64_t    dylibsTrieAddr;         // (unslid) address of trie of indexes of all cached dylibs
    uint64_t    dylibsTrieSize;         // size of trie of cached dylib paths
    uint64_t    otherImageArrayAddr;    // (unslid) address of ImageArray for dylibs and bundles with dlopen closures
    uint64_t    otherImageArraySize;    // size of ImageArray for dylibs and bundles with dlopen closures
    uint64_t    otherTrieAddr;          // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures
    uint64_t    otherTrieSize;          // size of trie of dylibs and bundles with dlopen closures
    uint32_t    mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info
    uint32_t    mappingWithSlideCount;  // number of dyld_cache_mapping_and_slide_info entries
};

// Uncomment this and check the build errors for the current mapping offset to check against when adding new fields.
// template<size_t size> class A { int x[-size]; }; A<sizeof(dyld_cache_header)> a;


struct dyld_cache_mapping_info {
    uint64_t    address;
    uint64_t    size;
    uint64_t    fileOffset;
    uint32_t    maxProt;
    uint32_t    initProt;
};

// Contains the flags for the dyld_cache_mapping_and_slide_info flgs field
enum {
    DYLD_CACHE_MAPPING_AUTH_DATA            = 1 << 0U,
    DYLD_CACHE_MAPPING_DIRTY_DATA           = 1 << 1U,
    DYLD_CACHE_MAPPING_CONST_DATA           = 1 << 2U,
};

struct dyld_cache_mapping_and_slide_info {
    uint64_t    address;
    uint64_t    size;
    uint64_t    fileOffset;
    uint64_t    slideInfoFileOffset;
    uint64_t    slideInfoFileSize;
    uint64_t    flags;
    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];
};

struct dyld_cache_slide_info_entry {
    uint8_t  bits[4096/(8*4)]; // 128-byte bitmap
};


// 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



// The version 3 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
//
// There are two cases:
//
// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
//    The page contains no values that need rebasing.
//
// 2) otherwise...
//    All rebase locations are in one linked list. The offset of the first
//    rebase location in the page is pageStarts[pageIndex].
//
// A pointer is one of of the variants in dyld_cache_slide_pointer3
//
// The code for processing a linked list (chain) is:
//
//    uint32_t delta = pageStarts[pageIndex];
//    dyld_cache_slide_pointer3* loc = pageStart;
//    do {
//        loc += delta;
//        delta = loc->offsetToNextPointer;
//        if ( loc->auth.authenticated ) {
//            newValue = loc->offsetFromSharedCacheBase  + results->slide + auth_value_add;
//            newValue = sign_using_the_various_bits(newValue);
//        }
//        else {
//            uint64_t value51      = loc->pointerValue;
//            uint64_t top8Bits     = value51 & 0x0007F80000000000ULL;
//            uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFFULL;
//            uint64_t targetValue  = ( top8Bits << 13 ) | bottom43Bits;
//            newValue = targetValue + results->slide;
//        }
//        loc->raw = newValue;
//    } while (delta != 0);
//
//
struct dyld_cache_slide_info3
{
    uint32_t    version;            // currently 3
    uint32_t    page_size;          // currently 4096 (may also be 16384)
    uint32_t    page_starts_count;
    uint64_t    auth_value_add;
    uint16_t    page_starts[/* page_starts_count */];
};

#define DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE    0xFFFF    // page has no rebasing

union dyld_cache_slide_pointer3
{
    uint64_t  raw;
    struct {
        uint64_t    pointerValue        : 51,
                    offsetToNextPointer : 11,
                    unused              :  2;
    }         plain;

    struct {
        uint64_t    offsetFromSharedCacheBase : 32,
                    diversityData             : 16,
                    hasAddressDiversity       :  1,
                    key                       :  2,
                    offsetToNextPointer       : 11,
                    unused                    :  1,
                    authenticated             :  1; // = 1;
    }         auth;
};



// The version 4 of the slide info is optimized for 32-bit caches up to 1GB.
// Since only interior pointers (pointers that point within the cache) are rebased
// (slid), we know the possible range of the pointers takes 30 bits.  That
// gives us two bits to use to chain to the next rebase.
//
// 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_SLIDE4_PAGE_NO_REBASE
//    The page contains no values that need rebasing.
//
// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_USE_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_SLIDE4_PAGE_USE_EXTRA
//    Multiple chains 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 chain in the page. The first is at:
//       extrasStartIndex = (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_INDEX)
//    The next is at extrasStartIndex+1.  The last is denoted by
//    having the high bit (DYLD_CACHE_SLIDE4_PAGE_EXTRA_END) of the pageExtras[].
//
// For 32-bit architectures, there are only two bits free (the two 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 12 bytes.
// To reduce the number or chains needed, an optimization was added.  Turns
// most of the non-rebased data are small values and can be co-opt'ed into
// being used in the chain. The can be done because nothing
// in the shared cache should point to the first 64KB which are in the shared
// cache header information. So if the resulting pointer points to the
// start of the cache +/-32KB, then it is actually a small number that should
// not be rebased, but just reconstituted.
//
// The code for processing a linked list (chain) is:
//
//    uint32_t delta = 1;
//    while ( delta != 0 ) {
//        uint8_t* loc = pageStart + pageOffset;
//        uint32_t rawValue = *((uint32_t*)loc);
//        delta = ((rawValue & deltaMask) >> deltaShift);
//        uintptr_t newValue = (rawValue & valueMask);
//        if ( (newValue & 0xFFFF8000) == 0 ) {
//           // small positive non-pointer, use as-is
//        }
//        else if ( (newValue & 0x3FFF8000) == 0x3FFF8000 ) {
//           // small negative non-pointer
//           newValue |= 0xC0000000;
//        }
//        else  {
//            // pointer that needs rebasing
//            newValue += valueAdd;
//            newValue += slideAmount;
//        }
//        *((uint32_t*)loc) = newValue;
//        pageOffset += delta;
//    }
//
//
struct dyld_cache_slide_info4
{
    uint32_t    version;            // currently 4
    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 (0xC0000000)
    uint64_t    value_add;          // base address of cache
    //uint16_t    page_starts[page_starts_count];
    //uint16_t    page_extras[page_extras_count];
};
#define DYLD_CACHE_SLIDE4_PAGE_NO_REBASE           0xFFFF  // page has no rebasing
#define DYLD_CACHE_SLIDE4_PAGE_INDEX               0x7FFF  // mask of page_starts[] values
#define DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA           0x8000  // index is into extras array (not a chain start offset)
#define DYLD_CACHE_SLIDE4_PAGE_EXTRA_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
};

struct dyld_cache_patch_info
{
    uint64_t    patchTableArrayAddr;    // (unslid) address of array for dyld_cache_image_patches for each image
    uint64_t    patchTableArrayCount;   // count of patch table entries
    uint64_t    patchExportArrayAddr;   // (unslid) address of array for patch exports for each image
    uint64_t    patchExportArrayCount;  // count of patch exports entries
    uint64_t    patchLocationArrayAddr; // (unslid) address of array for patch locations for each patch
    uint64_t    patchLocationArrayCount;// count of patch location entries
    uint64_t    patchExportNamesAddr;   // blob of strings of export names for patches
    uint64_t    patchExportNamesSize;   // size of string blob of export names for patches
};

struct dyld_cache_image_patches
{
    uint32_t    patchExportsStartIndex;
    uint32_t    patchExportsCount;
};

struct dyld_cache_patchable_export
{
    uint32_t    cacheOffsetOfImpl;
    uint32_t    patchLocationsStartIndex;
    uint32_t    patchLocationsCount;
    uint32_t    exportNameOffset;
};

struct dyld_cache_patchable_location
{
    uint64_t    cacheOffset             : 32,
                high7                   : 7,
                addend                  : 5,    // 0..31
                authenticated           : 1,
                usesAddressDiversity    : 1,
                key                     : 2,
                discriminator           : 16;
};


// This is the  location of the macOS shared cache on macOS 11.0 and later
#define MACOSX_MRM_DYLD_SHARED_CACHE_DIR   "/System/Library/dyld/"

// This is old define for the old location of the dyld cache
#define MACOSX_DYLD_SHARED_CACHE_DIR       MACOSX_MRM_DYLD_SHARED_CACHE_DIR

#define IPHONE_DYLD_SHARED_CACHE_DIR       "/System/Library/Caches/com.apple.dyld/"
#if !TARGET_OS_SIMULATOR
  #define DYLD_SHARED_CACHE_BASE_NAME        "dyld_shared_cache_"
#else
  #define DYLD_SHARED_CACHE_BASE_NAME        "dyld_sim_shared_cache_"
#endif
#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT  ".development"

static const uint64_t kDyldSharedCacheTypeDevelopment = 0;
static const uint64_t kDyldSharedCacheTypeProduction = 1;




#endif // __DYLD_CACHE_FORMAT__