Loading...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
/*
 * 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 MachOFile_h
#define MachOFile_h

#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <uuid/uuid.h>

#include <array>
#include <optional>
#include <string_view>

#include "Defines.h"
#include "UUID.h"
#include "Diagnostics.h"
#include "MachOLayout.h"
#include "SupportedArchs.h"
#include <mach-o/fixup-chains.h>
#include <mach-o/loader.h>

// needed until dyld builds with a newer SDK
#ifndef CPU_SUBTYPE_ARM64E
  #define CPU_SUBTYPE_ARM64E 2
#endif
#ifndef CPU_TYPE_ARM64_32
  #define CPU_TYPE_ARM64_32 0x0200000C
#endif
#ifndef CPU_SUBTYPE_ARM64_32_V8
  #define CPU_SUBTYPE_ARM64_32_V8 1
#endif
#ifndef BIND_OPCODE_THREADED
    #define BIND_OPCODE_THREADED    0xD0
#endif
#ifndef BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
    #define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB    0x00
#endif
#ifndef BIND_SUBOPCODE_THREADED_APPLY
    #define BIND_SUBOPCODE_THREADED_APPLY                               0x01
#endif
#ifndef BIND_SPECIAL_DYLIB_WEAK_LOOKUP
  #define BIND_SPECIAL_DYLIB_WEAK_LOOKUP                (-3)
#endif
#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
  #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE             0x02
#endif
#ifndef SG_READ_ONLY
  #define SG_READ_ONLY            0x10
#endif

#ifndef LC_DYLD_EXPORTS_TRIE
  #define LC_DYLD_EXPORTS_TRIE    0x80000033
#endif
#ifndef LC_DYLD_CHAINED_FIXUPS
  #define LC_DYLD_CHAINED_FIXUPS  0x80000034
#endif

#ifndef S_INIT_FUNC_OFFSETS
  #define S_INIT_FUNC_OFFSETS       0x16
#endif

#ifndef MH_FILESET
    #define MH_FILESET              0xc     /* set of mach-o's */
#endif

namespace dyld3 {

using lsl::UUID;

// replacements for posix that handle EINTR
int	stat(const char* path, struct stat* buf) VIS_HIDDEN;
int	open(const char* path, int flag, int other) VIS_HIDDEN;
int fstatat(int fd, const char *path, struct stat *buf, int flag) VIS_HIDDEN;


/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
template<typename T>
inline bool greaterThanAddOrOverflow(uint32_t addLHS, uint32_t addRHS, T b) {
    return (addLHS > b) || (addRHS > (b-addLHS));
}

/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
template<typename T>
inline bool greaterThanAddOrOverflow(uint64_t addLHS, uint64_t addRHS, T b) {
    return (addLHS > b) || (addRHS > (b-addLHS));
}


// Note, this should match PLATFORM_* values in <mach-o/loader.h>
enum class Platform {
    unknown             = 0,
    macOS               = 1,    // PLATFORM_MACOS
    iOS                 = 2,    // PLATFORM_IOS
    tvOS                = 3,    // PLATFORM_TVOS
    watchOS             = 4,    // PLATFORM_WATCHOS
    bridgeOS            = 5,    // PLATFORM_BRIDGEOS
    iOSMac              = 6,    // PLATFORM_MACCATALYST
    iOS_simulator       = 7,    // PLATFORM_IOSSIMULATOR
    tvOS_simulator      = 8,    // PLATFORM_TVOSSIMULATOR
    watchOS_simulator   = 9,    // PLATFORM_WATCHOSSIMULATOR
    driverKit           = 10,   // PLATFORM_DRIVERKIT
};

// A prioritized list of architectures
class VIS_HIDDEN GradedArchs {
public:
    struct CpuGrade { uint32_t type = 0; uint32_t subtype = 0; bool osBinary = false; uint16_t grade = 0; };
    // never construct new ones - just use existing static instances
    GradedArchs()                   = delete;
    GradedArchs(const GradedArchs&) = delete;
    constexpr GradedArchs(const CpuGrade& cg0, const CpuGrade& cg1 = {0,0,false,0} , const CpuGrade& cg2 = {0,0,false,0}) : _orderedCpuTypes({cg0, cg1, cg2, CpuGrade()}) {}

#if BUILDING_LIBDYLD || BUILDING_UNIT_TESTS
    static const GradedArchs&  launchCurrentOS(const char* simArches); // for emulating how the kernel chooses which slice to exec()
#endif
    static const GradedArchs&  forCurrentOS(bool keysOff, bool platformBinariesOnly);
    static const GradedArchs&  forName(const char* archName, bool keysOff = false);


    int                     grade(uint32_t cputype, uint32_t cpusubtype, bool platformBinariesOnly) const;
    const char*             name() const;
    bool                    checksOSBinary() const;
    bool                    supports64() const;
    void                    forEachArch(bool platformBinariesOnly, void (^handler)(const char* name)) const;

    // pre-built lists for existing hardware
#ifdef i386
#undef i386
#endif
    static const GradedArchs i386;            // 32-bit Mac
    static const GradedArchs x86_64;          // older Mac
    static const GradedArchs x86_64h;         // haswell Mac
    static const GradedArchs arm64;           // A11 or earlier iPhone or iPad
#if SUPPORT_ARCH_arm64e
    static const GradedArchs arm64e;            // A12 or later iPhone or iPad
    static const GradedArchs arm64e_keysoff;    // A12 running with signing keys disabled
    static const GradedArchs arm64e_pb;         // macOS Apple Silicon running platform binary
    static const GradedArchs arm64e_keysoff_pb; // macOS Apple Silicon running with signing keys disabled
#endif
    static const GradedArchs armv7k;          // watch thru series 3
    static const GradedArchs armv7s;          // deprecated
    static const GradedArchs armv7;           // deprecated
    static const GradedArchs armv6m;          // firmware
    static const GradedArchs armv7m;          // firmware
    static const GradedArchs armv7em;         // firmware
    static const GradedArchs armv8m;          // firmware

#if SUPPORT_ARCH_arm64_32
    static const GradedArchs arm64_32;        // watch series 4 and later
#endif
#if BUILDING_LIBDYLD || BUILDING_UNIT_TESTS
    static const GradedArchs launch_AS;       // Apple Silicon macs
    static const GradedArchs launch_AS_Sim;   // iOS simulator for Apple Silicon macs
    static const GradedArchs launch_Intel_h;  // Intel macs with haswell cpu
    static const GradedArchs launch_Intel;    // Intel macs
    static const GradedArchs launch_Intel_Sim; // iOS simulator for Intel macs
#endif

// private:
// should be private, but compiler won't statically initialize static members above
    const std::array<CpuGrade, 4>     _orderedCpuTypes;  // zero terminated
};


// A file read/mapped into memory
struct VIS_HIDDEN FatFile : fat_header
{
    static const FatFile*  isFatFile(const void* fileContent);
    void                   forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop)) const;
    bool                   isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, bool osBinary, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const;
    const char*            archNames(char strBuf[256], uint64_t fileLen) const;
private:
    bool                   isValidSlice(Diagnostics& diag, uint64_t fileLen, uint32_t sliceIndex,
                                        uint32_t sliceCpuType, uint32_t sliceCpuSubType, uint64_t sliceOffset, uint64_t sliceLen) const;
    void                   forEachSlice(Diagnostics& diag, uint64_t fileLen, bool validate,
                                        void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop)) const;
};


// A mach-o file read/mapped into memory
// Only info from mach_header or load commands is accessible (no LINKEDIT info)
struct VIS_HIDDEN MachOFile : mach_header
{
    typedef mach_o::ChainedFixupPointerOnDisk ChainedFixupPointerOnDisk;

    static const char*      archName(uint32_t cputype, uint32_t cpusubtype);
    static const char*      platformName(Platform platform);
    static bool             cpuTypeFromArchName(const char* archName, cpu_type_t* cputype, cpu_subtype_t* cpusubtype);
    static void             packedVersionToString(uint32_t packedVersion, char versionString[32]);
    static const char*      currentArchName();
    static Platform         currentPlatform();
    static Platform         basePlatform(dyld3::Platform reqPlatform);
    static uint64_t         read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end);
    static int64_t          read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end);
    static bool             isSimulatorPlatform(Platform platform, Platform* basePlatform=nullptr);
    static bool             isSharedCacheEligiblePath(const char* path);
    static const MachOFile* compatibleSlice(Diagnostics& diag, const void* content, size_t size, const char* path, Platform, bool isOSBinary, const GradedArchs&, bool internalInstall=false);
    static const MachOFile* isMachO(const void* content);

    bool            hasMachOMagic() const;
    bool            isMachO(Diagnostics& diag, uint64_t fileSize) const;
    bool            isDyldManaged() const;
    bool            isDylib() const;
    bool            isBundle() const;
    bool            isMainExecutable() const;
    bool            isDynamicExecutable() const;
    bool            isStaticExecutable() const;
    bool            isKextBundle() const;
    bool            isFileSet() const;
    bool            isPreload() const;
    bool            isDyld() const;
    bool            isPIE() const;
    bool            isArch(const char* archName) const;
    const char*     archName() const;
    bool            is64() const;
    uint32_t        maskedCpuSubtype() const;
    size_t          machHeaderSize() const;
    uint32_t        pointerSize() const;
    bool            uses16KPages() const;
    bool            builtForPlatform(Platform, bool onlyOnePlatform=false) const;
    bool            loadableIntoProcess(Platform processPlatform, const char* path, bool internalInstall=false) const;
    bool            isZippered() const;
    bool            inDyldCache() const;
    bool            getUuid(uuid_t uuid) const;
    UUID            uuid() const;
    bool            hasWeakDefs() const;
    bool            usesWeakDefs() const;
    bool            isBuiltForSimulator() const;
    bool            hasThreadLocalVariables() const;
    bool            getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const;
    void            forEachSupportedPlatform(void (^callback)(Platform platform, uint32_t minOS, uint32_t sdk)) const;
    void            forEachSupportedBuildTool(void (^callback)(Platform platform, uint32_t tool, uint32_t version)) const;
    const char*     installName() const;  // returns nullptr is no install name
    void            forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const;
    void            forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const;
#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
    bool            canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const;
    bool            canHavePrebuiltExecutableLoader(dyld3::Platform platform, const std::string_view& path,
                                                    void (^failureReason)(const char*)) const;
#endif
#if BUILDING_APP_CACHE_UTIL
    bool            canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const;
    bool            usesClassicRelocationsInKernelCollection() const;
#endif
    bool            canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const;
    bool            canBeFairPlayEncrypted() const;
    bool            isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const;
    bool            allowsAlternatePlatform() const;
    bool            hasChainedFixups() const;
    bool            hasChainedFixupsLoadCommand() const;
    bool            hasOpcodeFixups() const;
    void            forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const;
    bool            enforceCompatVersion() const;
    bool            hasInterposingTuples() const;
    bool            isRestricted() const;
    void            removeLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& remove, bool& stop));
    bool            hasSection(const char* segName, const char* sectName) const;
    void            forEachRPath(void (^callback)(const char* rPath, bool& stop)) const;
    bool            inCodeSection(uint32_t runtimeOffset) const;
    uint32_t        dependentDylibCount(bool* allDepsAreNormal = nullptr) const;
    bool            hasPlusLoadMethod(Diagnostics& diag) const;
    uint32_t        getFixupsLoadCommandFileOffset() const;
    bool            hasInitializer(Diagnostics& diag) const;
    void            forEachInitializerPointerSection(Diagnostics& diag, void (^callback)(uint32_t sectionOffset, uint32_t sectionSize, bool& stop)) const;
    bool            hasCodeSignature() const;
    bool            hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const;
    bool            hasObjC() const;
    uint64_t        mappedSize() const;
    uint32_t        segmentCount() const;
    void            forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const;
    bool            hasObjCMessageReferences() const;
    uint32_t        loadCommandsFreeSpace() const;

    // Looks for the given section in the segments in order: __DATA, __DATA_CONST, __DATA_DIRTY.
    // Returns true if it finds a section, and sets the out parameters to the first section it found.
    // Returns false if the given section doesn't exist in any of the given segments.
    bool            findObjCDataSection(const char* sectionName, uint64_t& sectionRuntimeOffset, uint64_t& sectionSize) const;

    enum class Malformed { linkeditOrder, linkeditAlignment, linkeditPermissions, dyldInfoAndlocalRelocs, segmentOrder,
                            textPermissions, executableData, writableData, codeSigAlignment, sectionsAddrRangeWithinSegment,
                            noLinkedDylibs, loaderPathsAreReal, mainExecInDyldCache, noUUID, zerofillSwiftMetadata, sdkOnOrAfter2021, sdkOnOrAfter2022 };
    bool            enforceFormat(Malformed) const;

    bool            validSegments(Diagnostics& diag, const char* path, size_t fileLen) const;

    // used at runtime to validate loaded image matches closure
    void            forEachCDHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen,
                                                 void (^callback)(const uint8_t cdHash[20])) const;

    static void     forEachTreatAsWeakDef(void (^handler)(const char* symbolName));

    // used by closure builder to find the offset and size of the trie.
    bool            hasExportTrie(uint32_t& runtimeOffset, uint32_t& size) const;

    const thread_command*           unixThreadLoadCommand() const;
    const linkedit_data_command*    chainedFixupsCmd() const;
    uint32_t                        entryAddrRegisterIndexForThreadCmd() const;
    bool                            use64BitEntryRegs() const;
    uint64_t                        entryAddrFromThreadCmd(const thread_command* cmd) const;
    uint64_t                        preferredLoadAddress() const;
    bool                            getEntry(uint64_t& offset, bool& usesCRT) const;

    // used by DyldSharedCache to find closure
    static const uint8_t*   trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol);

#if !SUPPORT_VM_LAYOUT
    // Get the layout information for this MachOFile.  Requires that the file is in file layout, not VM layout.
    // If you have a MachOLoaded/MachOAnalyzer, do not use this method
    bool                    getLinkeditLayout(Diagnostics& diag, mach_o::LinkeditLayout& layout) const;
    void                    withFileLayout(Diagnostics &diag, void (^callback)(const mach_o::Layout& layout)) const;
#endif

    // Note all kinds have the low bit set, and all offsets to values don't
    enum class SingletonPatchKind : uint32_t
    {
        unknown = 0,

        // An ISA, followed by a uintptr_t of constant data
        cfObj2 = 1
    };

    void forEachSingletonPatch(Diagnostics& diag, void (^handler)(SingletonPatchKind kind,
                                                                  uint64_t runtimeOffset)) const;

    struct SegmentInfo
    {
        uint64_t    fileOffset;
        uint64_t    fileSize;
        uint64_t    vmAddr;
        uint64_t    vmSize;
        uint64_t    sizeOfSections;
        const char* segName;
        uint32_t    loadCommandOffset;
        uint32_t    protections;
        uint32_t    textRelocs    :  1,  // segment has text relocs (i386 only)
                    readOnlyData  :  1,
                    isProtected   :  1,  // segment is protected
                    hasZeroFill   :  1,  // fileSize < vmSize
                    segIndex      : 12,
                    p2align       : 16;
        bool        readable() const   { return protections & VM_PROT_READ; }
        bool        writable() const   { return protections & VM_PROT_WRITE; }
        bool        executable() const { return protections & VM_PROT_EXECUTE; }
     };

    struct SectionInfo
    {
        SegmentInfo segInfo;
        uint64_t    sectAddr;
        uint64_t    sectSize;
        const char* sectName;
        uint32_t    sectFileOffset;
        uint32_t    sectFlags;
        uint32_t    sectAlignP2;
        uint32_t    reserved1;
        uint32_t    reserved2;
    };

    void            forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const;
    void            forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const;
    void            forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const;

    struct PointerMetaData
    {
                    PointerMetaData();
                    PointerMetaData(const ChainedFixupPointerOnDisk* fixupLoc, uint16_t pointer_format);

        bool        operator==(const PointerMetaData& other) const;

        uint32_t    diversity         : 16,
                    high8             :  8,
                    authenticated     :  1,
                    key               :  2,
                    usesAddrDiversity :  1;
    };

    static uint16_t chainedPointerFormat(const dyld_chained_fixups_header* chainHeader);
    static void     withChainStarts(Diagnostics& diag, const dyld_chained_fixups_header* chainHeader, void (^callback)(const dyld_chained_starts_in_image*));

    static void     forEachFixupChainSegment(Diagnostics& diag, const dyld_chained_starts_in_image* starts,
                                             void (^handler)(const dyld_chained_starts_in_segment* segInfo, uint32_t segIndex, bool& stop));
    static void     forEachFixupInSegmentChains(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo,
                                                bool notifyNonPointers, uint8_t* segmentContent,
                                                void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop));
    static bool     walkChain(Diagnostics& diag, ChainedFixupPointerOnDisk* start, uint16_t pointer_format,
                              bool notifyNonPointers, uint32_t max_valid_pointer,
                              void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop));
    static void     forEachChainedFixupTarget(Diagnostics& diag, const dyld_chained_fixups_header* chainHeader, const linkedit_data_command* chainedFixups,
                                              void (^callback)(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop));

protected:
    bool            hasMachOBigEndianMagic() const;
    bool            hasLoadCommand(uint32_t) const;

    const encryption_info_command* findFairPlayEncryptionLoadCommand() const;

    struct ArchInfo
    {
        const char* name;
        uint32_t    cputype;
        uint32_t    cpusubtype;
    };
    static const ArchInfo       _s_archInfos[];

    struct PlatformInfo
    {
        const char* name;
        Platform    platform;
        uint32_t    loadCommand;
    };
    static const PlatformInfo   _s_platformInfos[];

    void    analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) const;

    // This calls the callback for all code directories required for a given platform/binary combination.
    // On watchOS main executables this is all cd hashes.
    // On watchOS dylibs this is only the single cd hash we need (by rank defined by dyld, not the kernel).
    // On all other platforms this always returns a single best cd hash (ranked to match the kernel).
    // Note the callback parameter is really a CS_CodeDirectory.
    void    forEachCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen, void (^callback)(const void* cd)) const;
};


} // namespace dyld3

#endif /* MachOFile_h */