Skip to content

Commit

Permalink
Merge pull request #143 from p-x9/feature/enhance-prebuilt-loader
Browse files Browse the repository at this point in the history
Enhance prebuilt loader
  • Loading branch information
p-x9 authored Nov 15, 2024
2 parents 2cd02cf + c6e1c79 commit 708af3d
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 2 deletions.
15 changes: 15 additions & 0 deletions Sources/MachOKit/Model/DyldCache/Loader/ObjCBinaryInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// ObjCBinaryInfo.swift
// MachOKit
//
// Created by p-x9 on 2024/11/16
//
//

import Foundation

public struct ObjCBinaryInfo: LayoutWrapper {
public typealias Layout = objc_binary_info

public var layout: Layout
}
57 changes: 57 additions & 0 deletions Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,25 @@ extension PrebuiltLoader {
layout.loader.isPrebuilt != 0
}

public var neverUnload: Bool {
layout.loader.neverUnload != 0
}

public var isPremapped: Bool {
layout.loader.isPremapped != 0
}

public var ref: LoaderRef {
.init(layout: layout.loader.ref)
}

public var uuid: UUID {
.init(uuid: layout.loader.uuid)
}

public var sectionLocations: SectionLocations {
.init(layout: layout.sectionLocations)
}
}

extension PrebuiltLoader {
Expand All @@ -34,6 +50,14 @@ extension PrebuiltLoader {
return cache.fileHandle.readString(offset: offset)
}

public func altPath(in cache: DyldCache) -> String? {
guard layout.altPathOffset != 0 else { return nil }
guard let offset = cache.fileOffset(
of: numericCast(address) + numericCast(layout.altPathOffset)
) else { return nil }
return cache.fileHandle.readString(offset: offset)
}

public func dependentLoaderRefs(in cache: DyldCache) -> DataSequence<LoaderRef>? {
guard layout.dependentLoaderRefsArrayOffset != 0,
let offset = cache.fileOffset(
Expand All @@ -46,6 +70,14 @@ extension PrebuiltLoader {
numberOfElements: numericCast(layout.depCount)
)
}

public func objcBinaryInfo(in cache: DyldCache) -> ObjCBinaryInfo? {
guard layout.objcBinaryInfoOffset != 0 else { return nil }
guard let offset = cache.fileOffset(
of: numericCast(address) + numericCast(layout.objcBinaryInfoOffset)
) else { return nil }
return cache.fileHandle.read(offset: offset)
}
}

extension PrebuiltLoader {
Expand All @@ -61,6 +93,19 @@ extension PrebuiltLoader {
)
}

public func altPath(in cache: DyldCacheLoaded) -> String? {
// swiftlint:disable:previous unused_parameter
guard layout.altPathOffset != 0 else { return nil }
guard let baseAddress = UnsafeRawPointer(bitPattern: address) else {
return nil
}
return String(
cString: baseAddress
.advanced(by: numericCast(layout.altPathOffset))
.assumingMemoryBound(to: CChar.self)
)
}

public func dependentLoaderRefs(in cache: DyldCacheLoaded) -> MemorySequence<LoaderRef>? {
// swiftlint:disable:previous unused_parameter
guard layout.dependentLoaderRefsArrayOffset != 0,
Expand All @@ -74,6 +119,18 @@ extension PrebuiltLoader {
numberOfElements: numericCast(layout.depCount)
)
}

public func objcBinaryInfo(in cache: DyldCacheLoaded) -> ObjCBinaryInfo? {
// swiftlint:disable:previous unused_parameter
guard layout.objcBinaryInfoOffset != 0,
let baseAddress = UnsafeRawPointer(bitPattern: address) else {
return nil
}
return baseAddress
.advanced(by: numericCast(layout.objcBinaryInfoOffset))
.assumingMemoryBound(to: ObjCBinaryInfo.self)
.pointee
}
}

extension PrebuiltLoader {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,47 @@ public protocol PrebuiltLoaderProtocol {

/// magic of loader starts
var magic: String? { get }
/// PrebuiltLoader vs JustInTimeLoader
/// PrebuiltLoader vs JustInTimeLoader
var isPrebuilt: Bool { get }
var neverUnload: Bool { get }
var isPremapped: Bool { get }

var ref: LoaderRef { get }

// Information for all pre-calculated sections that we know about
var sectionLocations: SectionLocations { get }

/// path for target mach-o image
/// - Parameter cache: DyldCache to which `self` belongs
/// - Returns: path name
func path(in cache: DyldCache) -> String?
/// alternative path for target mach-o image if install_name does not match real path
/// - Parameter cache: DyldCache to which `self` belongs
/// - Returns: path name
func altPath(in cache: DyldCache) -> String?
/// loader reference list of target 's dependencies
/// - Parameter cache: DyldCache to which `self` belongs
/// - Returns: sequence of loader reference
func dependentLoaderRefs(in cache: DyldCache) -> DataSequence<LoaderRef>?
/// Stores information about the layout of the objc sections in a binary
/// - Parameter cache: DyldCache to which `self` belongs
/// - Returns: binary info for objc
func objcBinaryInfo(in cache: DyldCache) -> ObjCBinaryInfo?

/// path for target mach-o image
/// - Parameter cache: DyldCacheLoaded to which `self` belongs
/// - Returns: path name
func path(in cache: DyldCacheLoaded) -> String?
/// alternative path for target mach-o image if install_name does not match real path
/// - Parameter cache: DyldCacheLoaded to which `self` belongs
/// - Returns: path name
func altPath(in cache: DyldCacheLoaded) -> String?
/// loader reference list of target 's dependencies
/// - Parameter cache: DyldCacheLoaded to which `self` belongs
/// - Returns: sequence of loader reference
func dependentLoaderRefs(in cache: DyldCacheLoaded) -> MemorySequence<LoaderRef>?
/// Stores information about the layout of the objc sections in a binary
/// - Parameter cache: DyldCacheLoaded to which `self` belongs
/// - Returns: binary info for objc
func objcBinaryInfo(in cache: DyldCacheLoaded) -> ObjCBinaryInfo?
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,21 @@ extension PrebuiltLoader_Pre1165_3 {
layout.loader.isPrebuilt != 0
}

public var neverUnload: Bool {
layout.loader.neverUnload != 0
}

public var isPremapped: Bool {
layout.loader.isPremapped != 0
}

public var ref: LoaderRef {
.init(layout: layout.loader.ref)
}

public var sectionLocations: SectionLocations {
.init(layout: layout.sectionLocations)
}
}

extension PrebuiltLoader_Pre1165_3 {
Expand All @@ -32,6 +44,14 @@ extension PrebuiltLoader_Pre1165_3 {
return cache.fileHandle.readString(offset: offset)
}

public func altPath(in cache: DyldCache) -> String? {
guard layout.altPathOffset != 0 else { return nil }
guard let offset = cache.fileOffset(
of: numericCast(address) + numericCast(layout.altPathOffset)
) else { return nil }
return cache.fileHandle.readString(offset: offset)
}

public func dependentLoaderRefs(in cache: DyldCache) -> DataSequence<LoaderRef>? {
guard layout.dependentLoaderRefsArrayOffset != 0,
let offset = cache.fileOffset(
Expand All @@ -44,6 +64,14 @@ extension PrebuiltLoader_Pre1165_3 {
numberOfElements: numericCast(layout.depCount)
)
}

public func objcBinaryInfo(in cache: DyldCache) -> ObjCBinaryInfo? {
guard layout.objcBinaryInfoOffset != 0 else { return nil }
guard let offset = cache.fileOffset(
of: numericCast(address) + numericCast(layout.objcBinaryInfoOffset)
) else { return nil }
return cache.fileHandle.read(offset: offset)
}
}

extension PrebuiltLoader_Pre1165_3 {
Expand All @@ -59,6 +87,19 @@ extension PrebuiltLoader_Pre1165_3 {
)
}

public func altPath(in cache: DyldCacheLoaded) -> String? {
// swiftlint:disable:previous unused_parameter
guard layout.altPathOffset != 0 else { return nil }
guard let baseAddress = UnsafeRawPointer(bitPattern: address) else {
return nil
}
return String(
cString: baseAddress
.advanced(by: numericCast(layout.altPathOffset))
.assumingMemoryBound(to: CChar.self)
)
}

public func dependentLoaderRefs(in cache: DyldCacheLoaded) -> MemorySequence<LoaderRef>? {
// swiftlint:disable:previous unused_parameter
guard layout.dependentLoaderRefsArrayOffset != 0,
Expand All @@ -72,6 +113,18 @@ extension PrebuiltLoader_Pre1165_3 {
numberOfElements: numericCast(layout.depCount)
)
}

public func objcBinaryInfo(in cache: DyldCacheLoaded) -> ObjCBinaryInfo? {
// swiftlint:disable:previous unused_parameter
guard layout.objcBinaryInfoOffset != 0,
let baseAddress = UnsafeRawPointer(bitPattern: address) else {
return nil
}
return baseAddress
.advanced(by: numericCast(layout.objcBinaryInfoOffset))
.assumingMemoryBound(to: ObjCBinaryInfo.self)
.pointee
}
}

extension PrebuiltLoader_Pre1165_3 {
Expand Down
77 changes: 77 additions & 0 deletions Sources/MachOKit/Model/DyldCache/Loader/SectionLocations.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// SectionLocations.swift
// MachOKit
//
// Created by p-x9 on 2024/11/16
//
//

import Foundation

public struct SectionLocations: LayoutWrapper {
public typealias Layout = section_locations

public var layout: Layout
}

extension SectionLocations {
// [dyld implementation](https://github.com/apple-oss-distributions/dyld/blob/65bbeed63cec73f313b1d636e63f243964725a9d/include/mach-o/dyld_priv.h#L62)
public enum SectionKind: Int, CaseIterable {
// TEXT:
case text_swift5_protos
case text_swift5_proto
case text_swift5_types
case text_swift5_replace
case text_swift5_replace2
case text_swift5_ac_funcs

// DATA*:
case objc_image_info
case data_sel_refs
case data_msg_refs
case data_class_refs
case data_super_refs
case data_protocol_refs
case data_class_list
case data_non_lazy_class_list
case data_stub_list
case data_category_list
case data_category_list2
case data_non_lazy_category_list
case data_protocol_list
case data_objc_fork_ok
case data_raw_isa

// ~~ version 1 ~~
}
}

extension SectionLocations {
public struct Section {
public let offset: Int
public let size: Int
public let kind: SectionKind
}
}

extension SectionLocations {
public func section(for kind: SectionKind) -> Section {
var offsets = layout.offsets
var sizes = layout.sizes
let offset = withUnsafePointer(to: &offsets) {
UnsafeRawPointer($0)
.assumingMemoryBound(to: UInt64.self)
.advanced(by: kind.rawValue).pointee
}
let size = withUnsafePointer(to: &sizes) {
UnsafeRawPointer($0)
.assumingMemoryBound(to: UInt64.self)
.advanced(by: kind.rawValue).pointee
}
return .init(
offset: numericCast(offset),
size: numericCast(size),
kind: kind
)
}
}
51 changes: 50 additions & 1 deletion Sources/MachOKitC/include/dyld_cache_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef dyld_cache_loader_h
#define dyld_cache_loader_h

#include <stdbool.h>
// https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/PrebuiltLoader.h#L254
struct prebuilt_loader_set {
uint32_t magic;
Expand Down Expand Up @@ -175,7 +176,55 @@ struct prebuilt_loader {
uint32_t overrideBindTargetRefsOffset;
uint32_t overrideBindTargetRefsCount;

// struct section_locations sectionLocations;
struct section_locations sectionLocations;
};

// https://github.com/apple-oss-distributions/dyld/blob/65bbeed63cec73f313b1d636e63f243964725a9d/dyld/PrebuiltLoader.h#L344
// Stores information about the layout of the objc sections in a binary, as well as other properties relating to
// the objc information in there.
struct objc_binary_info {
// Offset to the __objc_imageinfo section
uint64_t imageInfoRuntimeOffset;

// Offsets to sections containing objc pointers
uint64_t selRefsRuntimeOffset;
uint64_t classListRuntimeOffset;
uint64_t categoryListRuntimeOffset;
uint64_t protocolListRuntimeOffset;

// Counts of the above sections.
uint32_t selRefsCount;
uint32_t classListCount;
uint32_t categoryCount;
uint32_t protocolListCount;

// Do we have stable Swift fixups to apply to at least one class?
bool hasClassStableSwiftFixups;

// Do we have any pointer-based method lists to set as uniqued?
bool hasClassMethodListsToSetUniqued;
bool hasCategoryMethodListsToSetUniqued;
bool hasProtocolMethodListsToSetUniqued;

// Do we have any method lists in which to set selector references.
// Note we only support visiting selector refernces in pointer based method lists
// Relative method lists should have been verified to always point to __objc_selrefs
bool hasClassMethodListsToUnique;
bool hasCategoryMethodListsToUnique;
bool hasProtocolMethodListsToUnique;

// Whwn serialized to the PrebuildLoader, these fields will encode other information about
// the binary.

// Offset to an array of uint8_t's. One for each protocol.
// Note this can be 0 (ie, have no fixups), even if we have protocols. That would be the case
// if this binary contains no canonical protocol definitions, ie, all canonical defs are in other binaries
// or the shared cache.
uint32_t protocolFixupsOffset;
// Offset to an array of BindTargetRef's. One for each selector reference to fix up
// Note we only fix up selector refs in the __objc_selrefs section, and in pointer-based method lists
uint32_t selectorReferencesFixupsOffset;
uint32_t selectorReferencesFixupsCount;
};

#endif /* dyld_cache_loader_h */

0 comments on commit 708af3d

Please sign in to comment.