LaunchCacheFormat.h   [plain text]


/*
 * Copyright (c) 2017 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 LaunchCacheFormat_h
#define LaunchCacheFormat_h


#include <stdint.h>
#include <assert.h>
#include <uuid/uuid.h>
#include <mach/mach.h>

#include "LaunchCache.h"


namespace dyld3 {
namespace launch_cache {
namespace binary_format {


// bump this number each time binary format changes
enum  { kFormatVersion = 8 };

union VIS_HIDDEN ImageRef {
    ImageRef() : val(0xFFFFFFFF) { }
                ImageRef(uint8_t kind, uint32_t groupNum, uint32_t indexInGroup) : _linkKind(kind), _groupNum(groupNum), _indexInGroup(indexInGroup) {
                    assert(groupNum < (1 << 18));
                    assert(indexInGroup < (1 << 12));
                }
    uint8_t     kind()  const { return _linkKind; }
    uint32_t    groupNum() const { return _groupNum; }
    uint32_t    indexInGroup() const { return _indexInGroup; }
    uint16_t    value() const { return val; }
    void        clearKind()  { _linkKind = 0; }
    
    bool operator==(const ImageRef& rhs) const {
        return (val == rhs.val);
    }
    bool operator!=(const ImageRef& rhs) const {
        return (val != rhs.val);
    }
    static ImageRef weakImportMissing();
    static ImageRef makeEmptyImageRef() { return ImageRef(); }

private:
    ImageRef(uint32_t v) : val(v) { }

    uint32_t     val;
    struct {
        uint32_t _linkKind       :   2,     // Image::LinkKind
                 _groupNum       :  18,     // 0 => cached dylib group, 1 => other dylib group, 2 => main closure, etc
                 _indexInGroup   :  12;     // max 64K images in group
    };
};




// In disk based images, all segments are multiples of page size
// This struct just tracks the size (disk and vm) of each segment.
// This is compact for most every image which have contiguous segments.
// If the image does not have contiguous segments (rare), an extra
// DiskSegment is inserted with the paddingNotSeg bit set.
struct DiskSegment
{
    uint64_t    filePageCount   : 30,
                vmPageCount     : 30,
                permissions     : 3,
                paddingNotSeg   : 1;
};


// In cache DATA_DIRTY is not page aligned or sized
// This struct allows segments with any alignment and up to 256MB in size
struct DyldCacheSegment
{
    uint64_t    cacheOffset : 32,
                size        : 28,
                permissions : 4;
};

// When an Image is built on the device, the mtime and inode are recorded.
// When built off device, the first 16 bytes of SHA1 of CodeDirectory is recorded.
union FileInfo
{
    struct {
        uint64_t mtime;
        uint64_t inode;
    } statInfo;
    struct {
        uint8_t  bytes[16];
    } cdHash16;
};

struct Image
{
    uint32_t            isDiskImage      : 1,       // images are DiskImage - not Image
                        isInvalid        : 1,       // an error occurred creating the info for this image
                        has16KBpages     : 1,
                        hasTextRelocs    : 1,
                        hasObjC          : 1,
                        mayHavePlusLoads : 1,
                        isEncrypted      : 1,       // image is DSMOS or FairPlay encrypted
                        hasWeakDefs      : 1,
                        neverUnload      : 1,
                        cwdSameAsThis    : 1,       // dylibs use file system relative paths, cwd must be main's dir
                        isPlatformBinary : 1,       // part of OS - can be loaded into LV process
                        isBundle         : 1,
                        overridableDylib : 1,       // only applicable to group 0
                        padding          : 7,
                        maxLoadCount     : 12;
    int32_t             groupOffset;                // back pointer to containing ImageGroup (from start of Image)
    uint32_t            pathPoolOffset;
    uint32_t            pathHash;
    FileInfo            fileInfo;
    uuid_t              uuid;
    uint16_t            dependentsArrayStartIndex;
    uint16_t            dependentsArrayCount;
    uint16_t            segmentsArrayStartIndex;
    uint16_t            segmentsArrayCount;
    uint16_t            initBeforeArrayStartIndex;
    uint16_t            initBeforeArrayCount;
    uint16_t            initOffsetsArrayStartIndex;
    uint16_t            initOffsetsArrayCount;
    uint16_t            dofOffsetsArrayStartIndex;
    uint16_t            dofOffsetsArrayCount;
};

// an image in the dyld shared cache
struct CachedImage : public Image
{
    uint32_t            patchStartIndex;
    uint32_t            patchCount;
};

// an image not in the dyld shared cache (loaded from disk at runtime)
struct DiskImage : public Image
{
    uint32_t            totalVmPages;
    uint32_t            sliceOffsetIn4K;
    uint32_t            codeSignFileOffset;
    uint32_t            codeSignFileSize;
    uint32_t            fixupsPoolOffset      : 28,    // offset in ImageGroup's pool for AllFixupsBySegment
                        fixupsPoolSegCount    : 4;     // count of segments in AllFixupsBySegment for this image
    uint32_t            fairPlayTextPageCount : 28,
                        fairPlayTextStartPage : 4;
    uint32_t            targetsArrayStartIndex;         // index in ImageGroup's pool of OrdinalEntry
    uint32_t            targetsArrayCount;
};


// if an Image has an alias (symlink to it), the Image does not record the alias, but the ImageGroup does
struct AliasEntry
{
    uint32_t    aliasHash;
    uint32_t    imageIndexInGroup;
    uint32_t    aliasOffsetInStringPool;
};

// each DiskImage points to an array of these, one per segment with fixups
struct AllFixupsBySegment
{
    uint32_t    segIndex    : 4,
                offset      : 28;    // from start of AllFixupsBySegment to this seg's SegmentFixupsByPage
};


// This struct is suitable for passing into kernel when kernel supports fixups on page-in.
struct SegmentFixupsByPage
{
    uint32_t    size;                // of this struct, including fixup opcodes
    uint32_t    pageSize;            // 0x1000 or 0x4000
    uint32_t    pageCount;
    uint32_t    pageInfoOffsets[1];  // array size is pageCount
    // each page info is a FixUpOpcode[]
};

enum class FixUpOpcode : uint8_t {
      done            = 0x00,
//    apply           = 0x10,
      rebase32        = 0x10,    // add32 slide at current pageOffset, increment pageOffset by 4
      rebase64        = 0x11,    // add64 slide at current pageOffset, increment pageOffset by 8
      bind32          = 0x12,    // set 32-bit ordinal value at current pageOffset, increment pageOffset by 4
      bind64          = 0x13,    // set 64-bit ordinal value at current pageOffset, increment pageOffset by 8
      rebaseText32    = 0x14,    // add32 slide at current text pageOffset, increment pageOffset by 4
      bindText32      = 0x15,    // set 32-bit ordinal value at current text pageOffset, increment pageOffset by 4
      bindTextRel32   = 0x16,    // set delta to 32-bit ordinal value at current text pageOffset, increment pageOffset by 4 (i386 CALL to dylib)
      bindImportJmp32 = 0x17,    // set delta to 32-bit ordinal value at current text pageOffset, increment pageOffset by 4 (i386 JMP to dylib)
//    fixupChain64    = 0x18,    // current page offset is start of a chain of locations to fix up
//    adjPageOffset   = 0x20,
      setPageOffset   = 0x20,    // low 4-bits is amount to increment (1 to 15).  If zero, then add next ULEB (note: can set offset for unaligned pointer)
      incPageOffset   = 0x30,    // low 4-bits *4 is amount to increment (4 to 60).  If zero, then add next ULEB * 4
//    adjOrdinal      = 0x40,
      setOrdinal      = 0x40,    // low 4-bits is ordinal (1-15).  If zero, then ordinal is next ULEB
      incOrdinal      = 0x50,    // low 4-bits is ordinal inc amount (1-15).  If zero, then ordinal is next ULEB
    repeat            = 0x60     // low 5-bits is how many next bytes to repeat.  next ULEB is repeat count
};

// If a closure uses DYLD_LIBRARY_PATH to override an OS dylib, there is an
// ImageRefOverride entry to redirect uses of the OS dylib.
struct ImageRefOverride
{
    ImageRef standardDylib;
    ImageRef overrideDylib;
};

// If a closure interposes on, or has a dylib that overrides, something in the dyld shared cache,
// then closure's ImageGroup contains an array of these
struct DyldCacheOverride
{
    uint64_t    patchTableIndex     : 24,       // index into PatchTable array of group 0
                imageIndex          : 8,        // index in this group (2) of what to replace with
                imageOffset         : 32;       // offset within image to override something in cache
};


// The ImageGroup for the dyld shared cache dylibs contains and array of these
// with one entry for each symbol in a cached dylib that is used by some other cached dylib.
struct PatchTable
{
    uint32_t    targetCacheOffset;      // delta from the base address of the cache to the address of the symbol to patch
    uint32_t    offsetsStartIndex;      // index into the PatchOffset array of first location to patch, last offset has low bit set
};

struct PatchOffset
{
    uint32_t    last             : 1,
                hasAddend        : 1,
                dataRegionOffset : 30;
};

struct ImageGroup
{
    uint32_t        imagesEntrySize         : 8,
                    dylibsExpectedOnDisk    : 1,
                    imageFileInfoIsCdHash   : 1,
                    padding                 : 14;
    uint32_t        groupNum;
    uint32_t        imagesPoolCount;
    uint32_t        imagesPoolOffset;           // offset to array of Image or DiskImage
    uint32_t        imageAliasCount;
    uint32_t        imageAliasOffset;           // offset to array of AliasEntry
    uint32_t        segmentsPoolCount;
    uint32_t        segmentsPoolOffset;         // offset to array of Segment or DyldCacheSegment
    uint32_t        dependentsPoolCount;
    uint32_t        dependentsPoolOffset;       // offset to array of ImageRef
    uint32_t        intializerOffsetPoolCount;
    uint32_t        intializerOffsetPoolOffset; // offset to array of uint32_t
    uint32_t        intializerListPoolCount;
    uint32_t        intializerListPoolOffset;   // offset to array of ImageRef
    uint32_t        targetsPoolCount;
    uint32_t        targetsOffset;              // offset to array of TargetSymbolValue
    uint32_t        fixupsPoolSize;
    uint32_t        fixupsOffset;               // offset to list of AllFixupsBySegment
    uint32_t        cachePatchTableCount;
    uint32_t        cachePatchTableOffset;      // offset to array of PatchTable (used only in group 0)
    uint32_t        cachePatchOffsetsCount;
    uint32_t        cachePatchOffsetsOffset;    // offset to array of PatchOffset cache offsets (used only in group 0)
    uint32_t        symbolOverrideTableCount;
    uint32_t        symbolOverrideTableOffset;  // offset to array of DyldCacheOverride (used only in group 2)
    uint32_t        imageOverrideTableCount;
    uint32_t        imageOverrideTableOffset;   // offset to array of ImageRefOverride (used only in group 2)
    uint32_t        dofOffsetPoolCount;
    uint32_t        dofOffsetPoolOffset;        // offset to array of uint32_t
    uint32_t        indirectGroupNumPoolCount;
    uint32_t        indirectGroupNumPoolOffset; // offset to array of uint32_t
    uint32_t        stringsPoolSize;
    uint32_t        stringsPoolOffset;
    // Image array
    // Alias array
    // Segment array
    // ImageRef array
    // Initializer offsets array
    // Initializer ImageRef array
    // TargetSymbolValue array
    // AllFixupsBySegment pool
    // PatchTable array
    // PatchOffset array
    // DyldCacheOverride array
    // ImageRefOverride array
    // string pool
    // DOF offsets array
};


struct Closure
{
    enum { magicV1 = 0x31646c6e };

    uint32_t        magic;
    uint32_t        usesCRT                  : 1,
                    isRestricted             : 1,
                    usesLibraryValidation    : 1,
                    padding                  : 29;
    uint32_t        missingFileComponentsOffset;    // offset to array of 16-bit string pool offset of path components
    uint32_t        dyldEnvVarsOffset;
    uint32_t        dyldEnvVarsCount;
    uint32_t        stringPoolOffset;
    uint32_t        stringPoolSize;
    ImageRef        libSystemRef;
    ImageRef        libDyldRef;
    uint32_t        libdyldVectorOffset;
    uint32_t        mainExecutableIndexInGroup;
    uint32_t        mainExecutableEntryOffset;
    uint32_t        initialImageCount;
    uuid_t          dyldCacheUUID;                // all zero if this closure is embedded in a dyld cache
    uint8_t         mainExecutableCdHash[20];     // or UUID if not code signed
    ImageGroup      group;
    // MissingFile array
    // env vars array
    // string pool
};



} // namespace binary_format

} // namespace launch_cache
} // namespace dyld


#endif // LaunchCacheFormat_h