Loading...
mach_o/Misc.cpp dyld-1340 dyld-1162
--- dyld/dyld-1340/mach_o/Misc.cpp
+++ dyld/dyld-1162/mach_o/Misc.cpp
@@ -27,9 +27,25 @@
 #include <stdio.h>
 #include <TargetConditionals.h>
 
+#if !TARGET_OS_EXCLAVEKIT
+  #include <sys/errno.h>
+  #include <sys/fcntl.h>
+  #include <sys/mman.h>
+  #include <sys/stat.h>
+  #include <sys/types.h>
+  #include <unistd.h>
+#endif // !TARGET_OS_EXCLAVEKIT
+
+
 #include <AvailabilityMacros.h>
 #include <mach-o/dyld_introspection.h>
 #include <mach-o/dyld_priv.h>
+
+#if BUILDING_DYLD && !TARGET_OS_EXCLAVEKIT
+  #include <subsystem.h>
+#endif // BUILDING_DYLD && !TARGET_OS_EXCLAVEKIT
+
+#include "DyldSharedCache.h"
 
 #include "Misc.h"
 #include "SupportedArchs.h"
@@ -38,21 +54,56 @@
 #include "Header.h"
 
 namespace mach_o {
+#if !TARGET_OS_EXCLAVEKIT
+////////////////////////////  posix wrappers ////////////////////////////////////////
+
+// <rdar://problem/10111032> wrap calls to stat() with check for EAGAIN
+int resilient_stat(const char* path, struct stat* buf)
+{
+    int result;
+    do {
+#if BUILDING_DYLD
+        result = ::stat_with_subsystem(path, buf);
+#else
+        result = ::stat(path, buf);
+#endif
+    } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
+
+    return result;
+}
+
+// <rdar://problem/13805025> dyld should retry open() if it gets an EGAIN
+int resilient_open(const char* path, int flag, int other)
+{
+    int result;
+    do {
+#if BUILDING_DYLD
+        if (flag & O_CREAT)
+            result = ::open(path, flag, other);
+        else
+            result = ::open_with_subsystem(path, flag);
+#else
+        result = ::open(path, flag, other);
+#endif
+    } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
+
+    return result;
+}
+#endif // !TARGET_OS_EXCLAVEKIT
 
 ////////////////////////////  ULEB128 helpers  ////////////////////////////////////////
 
-uint64_t read_uleb128(std::span<const uint8_t>& buffer, bool& malformed)
+uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end, bool& malformed)
 {
     uint64_t result = 0;
     int         bit = 0;
     malformed = false;
-    while ( true ) {
-        if ( buffer.empty() ) {
+    do {
+        if ( p == end ) {
             malformed = true;
             break;
         }
-        uint8_t elt = buffer.front();
-        uint64_t slice = elt & 0x7f;
+        uint64_t slice = *p & 0x7f;
 
         if ( bit > 63 ) {
             malformed = true;
@@ -62,27 +113,8 @@
             result |= (slice << bit);
             bit += 7;
         }
-
-        // Keep iterating if the high bit is set, and advance to next element
-        buffer = buffer.subspan(1);
-        if ( (elt & 0x80) == 0 )
-            break;
     }
-    return result;
-}
-
-uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end, bool& malformed)
-{
-    // Use the std::span one
-    std::span<const uint8_t> buffer(p, end);
-    uint64_t result = read_uleb128(buffer, malformed);
-
-    // Adjust 'p' to the new start of the buffer as read_uleb128() would have advanced it
-    if ( buffer.empty() )
-        p = end;
-    else
-        p = &buffer.front();
-
+    while (*p++ & 0x80);
     return result;
 }
 
@@ -117,23 +149,127 @@
     return result;
 }
 
-Error forEachHeader(std::span<const uint8_t> buffer, std::string_view path,
-                    void (^callback)(const Header* sliceHeader, size_t sliceLength, bool& stop)) {
-    if ( const mach_o::Universal* universal = mach_o::Universal::isUniversal(buffer) ) {
-        if ( mach_o::Error err = universal->valid(buffer.size()) )
-            return Error("error in file '%s': %s", path.data(), err.message());
-        universal->forEachSlice(^(mach_o::Universal::Slice slice, bool &stop) {
-            if ( const mach_o::Header* mh = mach_o::Header::isMachO(slice.buffer) ) {
-                callback(mh, slice.buffer.size(), stop);
-            }
+#if BUILDING_NM || BUILDING_DYLDINFO
+
+bool withReadOnlyMappedFile(const char* path, void (^handler)(std::span<const uint8_t>))
+{
+    struct stat statbuf;
+    if ( ::stat(path, &statbuf) == -1 )
+        return false;
+    int fd = ::open(path, O_RDONLY, 0);
+    if ( fd == -1 )
+        return false;
+    const void* mapping = ::mmap(nullptr, (size_t)statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    ::close(fd);
+    if ( mapping == MAP_FAILED )
+        return false;
+
+    handler(std::span((uint8_t*)mapping, (size_t)statbuf.st_size));
+
+    ::munmap((void*)mapping, (size_t)statbuf.st_size);
+    return true;
+}
+
+
+static bool inStringVector(const std::span<const char*>& vect, const char* target)
+{
+    for (const char* str : vect) {
+        if ( strcmp(str, target) == 0 )
+            return true;
+    }
+    return false;
+}
+
+void forSelectedSliceInPaths(std::span<const char*> paths, std::span<const char*> archFilter,
+                             void (^handler)(const char* path, const Header* slice, size_t len))
+{
+    const auto handleArchive = [handler](const char* path, const Archive& ar) {
+        Error err1 = ar.forEachMachO(^(const Archive::Member& m, const mach_o::Header * header, bool &stop) {
+            char objPath[PATH_MAX];
+            snprintf(objPath, sizeof(objPath), "%s(%s)", path, m.name.data());
+            handler(objPath, header, m.contents.size());
         });
-    } else if ( const mach_o::Header* mh = mach_o::Header::isMachO(buffer) ) {
-        bool stop = false;
-        callback(mh, buffer.size(), stop);
+        if ( err1.hasError() )
+            fprintf(stderr, "malformed archive '%s': %s\n", path, err1.message());
+    };
+
+    for (const char* path : paths) {
+        bool found = withReadOnlyMappedFile(path, ^(std::span<const uint8_t> buffer) {
+            if ( const Universal* uni = Universal::isUniversal(buffer) ) {
+               uni->forEachSlice(^(Universal::Slice slice, bool& stopSlice) {
+                    const char* sliceArchName = slice.arch.name();
+                    if ( archFilter.empty() || inStringVector(archFilter, sliceArchName) ) {
+                        if ( std::optional<Archive> ar = Archive::isArchive(slice.buffer) ) {
+                            handleArchive(path, *ar);
+                        }
+                        else if ( const Header* mh = Header::isMachO(slice.buffer) ) {
+                            handler(path, (Header*)slice.buffer.data(), slice.buffer.size());
+                        }
+                        else {
+                            fprintf(stderr, "%s slice in %s is not a mach-o\n", sliceArchName, path);
+                        }
+                    }
+                });
+            }
+            else if ( const Header* mh = Header::isMachO(buffer) ) {
+                handler(path, (Header*)buffer.data(), buffer.size());
+            }
+            else if ( std::optional<Archive> ar = Archive::isArchive(buffer) ) {
+                handleArchive(path, *ar);
+            }
+        });
+// dyld_for_each_installed_shared_cache() only available in macOS 12 aligned platforms
+// and we only build this code for earlier versions on macOS
+#if !TARGET_OS_OSX || (MAC_OS_X_VERSION_MIN_REQUIRED >= 120000)
+        if ( !found ) {
+            size_t                           cacheLen;
+            __block const DyldSharedCache*  dyldCache   = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLen);
+            __block const DyldSharedCache*  dyldCacheDK = nullptr;
+            __block const char*             currentArch = dyldCache->archName();
+            if ( strncmp(path, "/System/DriverKit/", 18) == 0 ) {
+                if ( dyldCacheDK == nullptr ) {
+                    dyld_for_each_installed_shared_cache(^(dyld_shared_cache_t cacheRef) {
+                        //__block bool firstCacheFile = false;
+                        dyld_shared_cache_for_each_file(cacheRef, ^(const char* aCacheFilePath) {
+                            // skip non-driverkit caches
+                            if ( strncmp(aCacheFilePath, "/System/DriverKit/", 18) != 0 )
+                                return;
+
+                            // skip cache files for all but matching arch with no extension
+                            const char* founddk = strstr(aCacheFilePath, currentArch);
+                            if ( founddk == nullptr || strlen(founddk) != strlen(currentArch) )
+                                return;
+
+                            std::vector<const DyldSharedCache*> dyldCaches = DyldSharedCache::mapCacheFiles(aCacheFilePath);
+                            if ( dyldCaches.empty() )
+                                return;
+                            dyldCacheDK = dyldCaches.front();
+                        });
+                    });
+                }
+                if ( dyldCacheDK != nullptr ) {
+                    uint32_t imageIndex;
+                    if ( dyldCacheDK->hasImagePath(path, imageIndex) ) {
+                        const mach_header* mh = dyldCacheDK->getIndexedImageEntry(imageIndex);
+                        handler(path, (Header*)mh, (size_t)(-1));
+                    }
+                }
+            }
+            else if ( dyldCache != nullptr ) {
+                // see if path is in current dyld shared cache
+                uint32_t imageIndex;
+                if ( dyldCache->hasImagePath(path, imageIndex) ) {
+                    const mach_header* mh = dyldCache->getIndexedImageEntry(imageIndex);
+                    handler(path, (Header*)mh, (size_t)(-1));
+                }
+            }
+         }
+#else
+        (void)found;
+#endif
     }
-
-    return Error::none();
-}
+}
+#endif
 
 
 } // namespace mach_o