Loading...
libdyld/utils.cpp dyld-1162 dyld-1284.13
--- dyld/dyld-1162/libdyld/utils.cpp
+++ dyld/dyld-1284.13/libdyld/utils.cpp
@@ -37,11 +37,28 @@
 #include "FileUtils.h"
 #include "DyldDelegates.h"
 
+// mach_o
+#include "Architecture.h"
+#include "Header.h"
+#include "Image.h"
+#include "Error.h"
+#include "Version32.h"
+#include "Fixup.h"
+#include "Symbol.h"
+
 using dyld3::MachOFile;
 using dyld3::FatFile;
 using dyld3::GradedArchs;
-using dyld3::Platform;
-
+
+using mach_o::Header;
+using mach_o::Image;
+using mach_o::Error;
+using mach_o::Version32;
+using mach_o::Version64;
+using mach_o::LinkedDylibAttributes;
+using mach_o::Fixup;
+using mach_o::Symbol;
+using mach_o::Platform;
 
 // used by unit tests
 __attribute__((visibility("hidden")))
@@ -55,14 +72,23 @@
 
 bool macho_cpu_type_for_arch_name(const char* archName, cpu_type_t* type, cpu_subtype_t* subtype)
 {
-    return MachOFile::cpuTypeFromArchName(archName, type, subtype);
+    mach_o::Architecture arch = mach_o::Architecture::byName(archName);
+    if ( arch == mach_o::Architecture::invalid )
+        return false;
+    
+    *type = arch.cpuType();
+    *subtype = arch.cpuSubtype();
+    return true;
 }
 
 const char* macho_arch_name_for_cpu_type(cpu_type_t type, cpu_subtype_t subtype)
 {
-    const char* result = MachOFile::archName(type, subtype);
+    const char* result = mach_o::Architecture(type, subtype).name();
     if ( strcmp(result, "unknown") == 0 )
         return nullptr;
+    // Strip any suffix that further specifies the exact arm64e type (.old, .kernel, etc).
+    if ( std::string_view(result).starts_with("arm64e") )
+        return "arm64e";
     return result;
 }
 
@@ -138,13 +164,13 @@
     return result;
 }
 
-static bool launchableOnCurrentPlatform(const MachOFile* mf)
+static bool launchableOnCurrentPlatform(const Header* mh)
 {
 #if TARGET_OS_OSX
     // macOS is special and can launch macOS, catalyst, and iOS apps
-    return ( mf->builtForPlatform(Platform::macOS) || mf->builtForPlatform(Platform::iOSMac) || mf->builtForPlatform(Platform::iOS) );
+    return ( mh->builtForPlatform(Platform::macOS) || mh->builtForPlatform(Platform::macCatalyst) || mh->builtForPlatform(Platform::iOS) );
 #else
-    return mf->builtForPlatform(MachOFile::currentPlatform());
+    return mh->builtForPlatform(Platform::current());
 #endif
 }
 
@@ -168,18 +194,18 @@
         __block uint64_t sliceOffset = 0;
         __block uint64_t sliceLen    = 0;
         ff->forEachSlice(diag, statbuf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
-            if ( const MachOFile* mf = MachOFile::isMachO(sliceStart) ) {
-                if ( mf->filetype == MH_EXECUTE ) {
-                    int sliceGrade = launchArchs.grade(mf->cputype, mf->cpusubtype, isOSBinary);
-                    if ( (sliceGrade > bestGrade) && launchableOnCurrentPlatform(mf) ) {
+            if ( const Header* mh = Header::isMachO({(const uint8_t*)sliceStart, (size_t)sliceSize}) ) {
+                if ( mh->isMainExecutable() ) {
+                    int sliceGrade = launchArchs.grade(mh->arch().cpuType(), mh->arch().cpuSubtype(), isOSBinary);
+                    if ( (sliceGrade > bestGrade) && launchableOnCurrentPlatform(mh) ) {
                         sliceOffset = (char*)sliceStart - (char*)mappedFile;
                         sliceLen    = sliceSize;
                         bestGrade   = sliceGrade;
                     }
                 }
                 else {
-                    int sliceGrade = dylibArchs.grade(mf->cputype, mf->cpusubtype, isOSBinary);
-                    if ( (sliceGrade > bestGrade) && mf->loadableIntoProcess(platform, "") ) {
+                    int sliceGrade = dylibArchs.grade(mh->arch().cpuType(), mh->arch().cpuSubtype(), isOSBinary);
+                    if ( (sliceGrade > bestGrade) && mh->loadableIntoProcess(platform, "") ) {
                         sliceOffset = (char*)sliceStart - (char*)mappedFile;
                         sliceLen    = sliceSize;
                         bestGrade   = sliceGrade;
@@ -197,16 +223,16 @@
         else
             result = EBADARCH;
     }
-    else if ( const MachOFile* mf = MachOFile::isMachO(mappedFile) ) {
-        if ( (mf->filetype == MH_EXECUTE) && (launchArchs.grade(mf->cputype, mf->cpusubtype, isOSBinary) != 0) && launchableOnCurrentPlatform(mf) )  {
+    else if ( const Header* mh = Header::isMachO({(const uint8_t*)mappedFile, (size_t)statbuf.st_size}) ) {
+        if ( mh->isMainExecutable() && (launchArchs.grade(mh->arch().cpuType(), mh->arch().cpuSubtype(), isOSBinary) != 0) && launchableOnCurrentPlatform(mh) )  {
             // the "best" of a main executable must pass grading and be a launchable
             if ( bestSlice )
-                bestSlice(mf, 0, (size_t)statbuf.st_size);
-        }
-        else if ( (dylibArchs.grade(mf->cputype, mf->cpusubtype, isOSBinary) != 0) && mf->loadableIntoProcess(platform, "") ) {
+                bestSlice((const mach_header*)mh, 0, (size_t)statbuf.st_size);
+        }
+        else if ( (dylibArchs.grade(mh->arch().cpuType(), mh->arch().cpuSubtype(), isOSBinary) != 0) && mh->loadableIntoProcess(platform, "") ) {
             // the "best" of a dylib/bundle must pass grading and match the platform of the current process
             if ( bestSlice )
-                bestSlice(mf, 0, (size_t)statbuf.st_size);
+                bestSlice((const mach_header*)mh, 0, (size_t)statbuf.st_size);
         }
         else {
             result = EBADARCH;
@@ -221,12 +247,29 @@
     return result;
 }
 
+#if __arm64e__
+static const void* stripPointer(const void* ptr)
+{
+#if __has_feature(ptrauth_calls)
+    return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+    return ptr;
+#endif
+}
+#endif
 
 int macho_best_slice_in_fd(int fd, void (^bestSlice)(const struct mach_header* slice, uint64_t sliceFileOffset, size_t sliceSize)__MACHO_NOESCAPE)
 {
-    const Platform     platform    = MachOFile::currentPlatform();
-    const GradedArchs* launchArchs = &GradedArchs::forCurrentOS(false, false);
-    const GradedArchs* dylibArchs  = &GradedArchs::forCurrentOS(false, false);
+    bool keysOff = true;
+#if __arm64e__
+    // Test if PAC is enabled
+    const void* p = (const void*)&macho_best_slice;
+    if ( stripPointer(p) != p )
+       keysOff = false;
+#endif
+    const Platform     platform    = Platform::current();
+    const GradedArchs* launchArchs = &GradedArchs::launchCurrentOS();
+    const GradedArchs* dylibArchs  = &GradedArchs::forCurrentOS(keysOff, false);
 #if TARGET_OS_SIMULATOR
     const char* simArchNames = getenv("SIMULATOR_ARCHS");
     if ( simArchNames == nullptr )
@@ -243,10 +286,176 @@
 ///
 const char* _Nullable macho_dylib_install_name(const struct mach_header* _Nonnull mh) DYLD_EXCLAVEKIT_UNAVAILABLE
 {
-    if ( const MachOFile* mf = MachOFile::isMachO(mh) )
-        return mf->installName();
+    const Header* hdr = (const Header*)mh;
+    if ( hdr->hasMachOMagic() )
+        return hdr->installName();
 
     return nullptr;
 }
 
+static void iterateDependencies(const Image& image, void (^_Nonnull callback)(const char* _Nonnull loadPath, const char* _Nonnull attributes, bool* _Nonnull stop) )
+{
+    image.header()->forEachLinkedDylib(^(const char* loadPath, LinkedDylibAttributes kind, Version32 compatVersion, Version32 curVersion, 
+                                         bool synthesizedLink, bool& stop) {
+        char attrBuf[64];
+        kind.toString(attrBuf);
+        callback(loadPath, attrBuf, &stop);
+    });
+}
+
+int macho_for_each_dependent_dylib(const struct mach_header* _Nonnull mh, size_t mappedSize,
+                                   void (^ _Nonnull callback)(const char* _Nonnull loadPath, const char* _Nonnull attributes, bool* _Nonnull stop))
+{
+    if ( mappedSize == 0 ) {
+        // Image loaded by dyld
+        Image image(mh);
+        iterateDependencies(image, callback);
+    }
+    else {
+        // raw mach-o file/slice in memory
+        Image image(mh, mappedSize, Image::MappingKind::wholeSliceMapped);
+        if ( !image.header()->hasMachOMagic() )
+            return EFTYPE;
+        if ( Error err = image.validate() )
+            return EBADMACHO;
+        iterateDependencies(image, callback);
+    }
+    return 0;
+}
+
+static void iterateImportedSymbols(const Image& image, void (^_Nonnull callback)(const char* _Nonnull symbolName, const char* _Nonnull libraryPath, bool weakImport, bool* _Nonnull stop) )
+{
+    if ( image.hasChainedFixups() ) {
+        image.chainedFixups().forEachBindTarget(^(const Fixup::BindTarget& bindTarget, bool& stop) {
+            callback(bindTarget.symbolName.c_str(), image.header()->libOrdinalName(bindTarget.libOrdinal).c_str(), bindTarget.weakImport, &stop);
+        });
+    }
+    else {
+        // old opcode based fixups
+        if ( image.hasBindOpcodes() ) {
+            image.bindOpcodes().forEachBindTarget(^(const Fixup::BindTarget& bindTarget, bool& stop) {
+                callback(bindTarget.symbolName.c_str(), image.header()->libOrdinalName(bindTarget.libOrdinal).c_str(), bindTarget.weakImport, &stop);
+            }, ^(const char* symbolName) {
+            });
+        }
+        if ( image.hasLazyBindOpcodes() ) {
+            image.lazyBindOpcodes().forEachBindTarget(^(const Fixup::BindTarget& bindTarget, bool& stop) {
+                callback(bindTarget.symbolName.c_str(), image.header()->libOrdinalName(bindTarget.libOrdinal).c_str(), bindTarget.weakImport, &stop);
+            }, ^(const char *symbolName) {
+            });
+        }
+    }
+}
+
+int macho_for_each_imported_symbol(const struct mach_header* _Nonnull mh, size_t mappedSize,
+                                   void (^ _Nonnull callback)(const char* _Nonnull symbolName, const char* _Nonnull libraryPath, bool weakImport, bool* _Nonnull stop))
+{
+    if ( mappedSize == 0 ) {
+        // Image loaded by dyld, but sanity check
+        if ( !((Header*)mh)->hasMachOMagic() )
+            return EFTYPE;
+        Image image(mh);
+        iterateImportedSymbols(image, callback);
+    }
+    else {
+        // raw mach-o file/slice in memory
+        Image image(mh, mappedSize, Image::MappingKind::wholeSliceMapped);
+        if ( !image.header()->hasMachOMagic() )
+            return EFTYPE;
+        if ( Error err = image.validate() )
+            return EBADMACHO;
+        iterateImportedSymbols(image, callback);
+    }
+    return 0;
+}
+
+static const char* exportSymbolAttrString(const Symbol& symbol)
+{
+    uint64_t other;
+    if ( symbol.isWeakDef() )
+        return "weak-def";
+    else if ( symbol.isThreadLocal() )
+        return "thread-local";
+    else if ( symbol.isDynamicResolver(other) )
+        return "dynamic-resolver";
+    else if ( symbol.isAbsolute(other) )
+        return "absolute";
+    return "";
+}
+
+static void iterateExportedSymbols(const Image& image, void (^_Nonnull callback)(const char* _Nonnull symbolName, const char* _Nonnull attributes, bool* _Nonnull stop) )
+{
+    if ( image.hasExportsTrie() ) {
+        image.exportsTrie().forEachExportedSymbol(^(const Symbol& symbol, bool& stop) {
+            callback(symbol.name().c_str(), exportSymbolAttrString(symbol), &stop);
+        });
+    }
+    else if ( image.hasSymbolTable() ) {
+        image.symbolTable().forEachExportedSymbol(^(const Symbol& symbol, uint32_t symbolIndex, bool& stop) {
+            callback(symbol.name().c_str(), exportSymbolAttrString(symbol), &stop);
+        });
+    }
+}
+
+int macho_for_each_exported_symbol(const struct mach_header* _Nonnull mh, size_t mappedSize,
+                                   void (^ _Nonnull callback)(const char* _Nonnull symbolName, const char* _Nonnull attributes, bool* _Nonnull stop))
+{
+    if ( mappedSize == 0 ) {
+        // Image loaded by dyld, but sanity check
+        if ( !((Header*)mh)->hasMachOMagic() )
+            return EFTYPE;
+        Image image(mh);
+        iterateExportedSymbols(image, callback);
+    }
+    else {
+        // raw mach-o file/slice in memory
+        Image image(mh, mappedSize, Image::MappingKind::wholeSliceMapped);
+        if ( !image.header()->hasMachOMagic() )
+            return EFTYPE;
+        if ( Error err = image.validate() )
+            return EBADMACHO;
+        iterateExportedSymbols(image, callback);
+    }
+    return 0;
+}
+
+
+int macho_for_each_defined_rpath(const struct mach_header* _Nonnull mh, size_t mappedSize,
+                                 void (^ _Nonnull callback)(const char* _Nonnull rpath, bool* _Nonnull stop))
+{
+    if ( mappedSize == 0 ) {
+        // Image loaded by dyld
+        Image image(mh);
+        image.header()->forEachRPath(^(const char* _Nonnull rpath, bool& stop) {
+            callback(rpath, &stop);
+        });
+    }
+    else {
+        // raw mach-o file/slice in memory
+        Image image(mh, mappedSize, Image::MappingKind::wholeSliceMapped);
+        if ( !image.header()->hasMachOMagic() )
+            return EFTYPE;
+        if ( Error err = image.validate() )
+            return EBADMACHO;
+        image.header()->forEachRPath(^(const char* _Nonnull rpath, bool& stop) {
+            callback(rpath, &stop);
+        });
+    }
+    return 0;
+}
+
+bool macho_source_version(const struct mach_header* _Nonnull mh, uint64_t* _Nonnull version)
+{
+    Header* header = (Header*)mh;
+    if ( !header->hasMachOMagic() )
+        return false;
+    
+    Version64 v;
+    if ( !header->sourceVersion(v) )
+        return false;
+    
+    *version = v.value();
+    return true;
+}
+
 #endif // !TARGET_OS_EXCLAVEKIT