Loading...
mach_o/Universal.cpp dyld-1340 dyld-1162
--- dyld/dyld-1340/mach_o/Universal.cpp
+++ dyld/dyld-1162/mach_o/Universal.cpp
@@ -54,7 +54,7 @@
 
     const Universal* fileStartAsFat = (Universal*)fileContent.data();
     uint32_t headerFirstFourBytes;
-    memcpy(&headerFirstFourBytes, (uint32_t*)fileStartAsFat, 4); // use memcpy to avoid UB if content (such as in static lib) is not aligned
+    memcpy(&headerFirstFourBytes, fileStartAsFat, 4); // use memcpy to avoid UB if content (such as in static lib) is not aligned
     if ( (headerFirstFourBytes == OSSwapBigToHostInt32(FAT_MAGIC)) || (headerFirstFourBytes == OSSwapBigToHostInt32(FAT_MAGIC_64)) )
         return fileStartAsFat;
     else
@@ -243,6 +243,118 @@
     return false;
 }
 
+//
+// MARK: --- methods when creating a fat file ---
+//
+
+#if BUILDING_MACHO_WRITER
+
+// FIXME: fill out align field of fat header
+// FIXME: compute slice alignment based on mach_header type and cpu type
+// FIXME: sort slices by alignment
+const Universal* Universal::make(std::span<const Header*> mhs, bool forceFat64, bool arm64offEnd)
+{
+    Slice slices[mhs.size()];
+    for ( size_t i = 0; i < mhs.size(); ++i ) {
+        const Header* header = mhs[i];
+        Slice& slice = slices[i];
+        slice.arch = header->arch();
+        slice.buffer = std::span((const uint8_t*)header, header->fileSize());
+    }
+
+    return make(std::span(slices, mhs.size()), forceFat64, arm64offEnd);
+}
+
+const Universal* Universal::make(std::span<const Universal::Slice> slices, bool forceFat64, bool arm64offEnd)
+{
+    // compute number of slices and total size
+    uint64_t totalSize = 0x4000;
+    int32_t  count     = 0;
+    for (const Universal::Slice& slice : slices) {
+        ++count;
+        totalSize += slice.buffer.size();
+        pageAlign16K(totalSize);
+    }
+
+    // allocate buffer
+    vm_address_t newAllocationAddr;
+    if ( ::vm_allocate(mach_task_self(), &newAllocationAddr, (size_t)totalSize, VM_FLAGS_ANYWHERE) != KERN_SUCCESS )
+        return nullptr;
+
+    // make fat header
+    Universal* result = (Universal*)newAllocationAddr;
+    bool fat64 = forceFat64 || (totalSize > 0x100000000ULL);
+    if ( fat64 ) {
+        result->fh.magic     = OSSwapHostToBigInt32(FAT_MAGIC_64);
+        result->fh.nfat_arch = OSSwapHostToBigInt32(count);
+    }
+    else {
+        result->fh.magic     = OSSwapHostToBigInt32(FAT_MAGIC);
+        if ( arm64offEnd && (slices[count-1].arch == Architecture::arm64) )
+            result->fh.nfat_arch = OSSwapHostToBigInt32(count-1); // hide arm64 slice off end of array
+        else
+            result->fh.nfat_arch = OSSwapHostToBigInt32(count);
+    }
+
+    // add entry and copy each slice into buffer
+    fat_arch*    entry32 = (fat_arch*)   ((uint8_t*)result + sizeof(fat_header));
+    fat_arch_64* entry64 = (fat_arch_64*)((uint8_t*)result + sizeof(fat_header));
+    uint64_t currentOffset = 0x4000;
+    for (const Universal::Slice& slice : slices) {
+        uint64_t sliceSize = slice.buffer.size();
+        if ( fat64 ) {
+            slice.arch.set(*entry64);
+            entry64->offset    = OSSwapHostToBigInt64(currentOffset);
+            entry64->size      = OSSwapHostToBigInt64(sliceSize);
+            entry64->align     = OSSwapHostToBigInt32(0x4000);
+            entry64->reserved  = 0;
+            ++entry64;
+        }
+        else {
+            slice.arch.set(*entry32);
+            entry32->offset = OSSwapHostToBigInt32((uint32_t)currentOffset);
+            entry32->size   = OSSwapHostToBigInt32((uint32_t)sliceSize);
+            entry32->align  = OSSwapHostToBigInt32(0x4000);
+            ++entry32;
+        }
+        memcpy((uint8_t*)newAllocationAddr+currentOffset, slice.buffer.data(), slice.buffer.size());
+        currentOffset += sliceSize;
+        pageAlign16K(currentOffset);
+    }
+    return result;
+}
+
+uint64_t Universal::size() const
+{
+    int currSliceCount = OSSwapBigToHostInt32(fh.nfat_arch);
+    if ( currSliceCount == 0 )
+        return 0x4000;
+
+    __block uint64_t endOffset = 0;
+    this->forEachSlice(^(Architecture arch, uint64_t sliceOffset, uint64_t sliceSize, bool& stop) {
+        endOffset = sliceOffset + sliceSize;
+    });
+    pageAlign16K(endOffset);
+    return endOffset;
+}
+
+void Universal::save(char savedPath[PATH_MAX]) const
+{
+    ::strcpy(savedPath, "/tmp/universal-XXXXXX");
+    int fd = ::mkstemp(savedPath);
+    if ( fd != -1 ) {
+        ::pwrite(fd, this, (size_t)size(), 0);
+        ::close(fd);
+    }
+}
+
+void Universal::free() const
+{
+    ::vm_deallocate(mach_task_self(), (vm_address_t)this, (vm_size_t)size());
+}
+
+#endif // BUILDING_MACHO_WRITER
+
 
 } // namespace mach_o