From d582ebf900a2a5254fd2ee6e6738d9e3831ccff4 Mon Sep 17 00:00:00 2001 From: p-x9 <50244599+p-x9@users.noreply.github.com> Date: Wed, 30 Jul 2025 01:45:17 +0900 Subject: [PATCH 1/4] Fix symbol cache to retain main cache header --- Sources/MachOKit/DyldCache.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/MachOKit/DyldCache.swift b/Sources/MachOKit/DyldCache.swift index 82b96e9..e2787a7 100644 --- a/Sources/MachOKit/DyldCache.swift +++ b/Sources/MachOKit/DyldCache.swift @@ -196,7 +196,10 @@ extension DyldCache { } let suffix = ".symbols" let path = url.path + suffix - return try .init(url: .init(fileURLWithPath: path)) + return try .init( + subcacheUrl: .init(fileURLWithPath: path), + mainCacheHeader: mainCacheHeader + ) } } From 3bcf84180829104a8b7768313306c276052d7cf8 Mon Sep 17 00:00:00 2001 From: p-x9 <50244599+p-x9@users.noreply.github.com> Date: Wed, 30 Jul 2025 01:59:32 +0900 Subject: [PATCH 2/4] Add method to retrieve the entry for the specified mach-o --- .../DyldCache/DyldCacheLocalSymbolsInfo.swift | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/Sources/MachOKit/Model/DyldCache/DyldCacheLocalSymbolsInfo.swift b/Sources/MachOKit/Model/DyldCache/DyldCacheLocalSymbolsInfo.swift index 27ae5d0..e43cac1 100644 --- a/Sources/MachOKit/Model/DyldCache/DyldCacheLocalSymbolsInfo.swift +++ b/Sources/MachOKit/Model/DyldCache/DyldCacheLocalSymbolsInfo.swift @@ -338,3 +338,92 @@ extension DyldCacheLocalSymbolsInfo { } } } + +extension DyldCacheLocalSymbolsInfo { + public func entry64( + for machO: MachOFile, + in cache: DyldCache + ) -> DyldCacheLocalSymbolsEntry64? { + guard let offset = machO.textOffset(in: cache) else { return nil } + return entries64(in: cache)?.first( + where: { + $0.dylibOffset == offset + } + ) + } + + public func entry32( + for machO: MachOFile, + in cache: DyldCache + ) -> DyldCacheLocalSymbolsEntry? { + guard let offset = machO.textOffset(in: cache) else { return nil } + return entries32(in: cache)?.first( + where: { + $0.dylibOffset == offset + } + ) + } + + public func entry( + for machO: MachOFile, + in cache: DyldCache + ) -> (any DyldCacheLocalSymbolsEntryProtocol)? { + guard let offset = machO.textOffset(in: cache) else { return nil } + return entries(in: cache).first( + where: { + $0.dylibOffset == offset + } + ) + } +} + +extension DyldCacheLocalSymbolsInfo { + public func entry64( + for machO: MachOFile, + in cache: FullDyldCache + ) -> DyldCacheLocalSymbolsEntry64? { + guard let offset = machO.textOffset(in: cache) else { return nil } + return entries64(in: cache)?.first( + where: { + $0.dylibOffset == offset + } + ) + } + + public func entry32( + for machO: MachOFile, + in cache: FullDyldCache + ) -> DyldCacheLocalSymbolsEntry? { + guard let offset = machO.textOffset(in: cache) else { return nil } + return entries32(in: cache)?.first( + where: { + $0.dylibOffset == offset + } + ) + } + + public func entry( + for machO: MachOFile, + in cache: FullDyldCache + ) -> (any DyldCacheLocalSymbolsEntryProtocol)? { + guard let offset = machO.textOffset(in: cache) else { return nil } + return entries(in: cache).first( + where: { + $0.dylibOffset == offset + } + ) + } +} + +fileprivate extension MachOFile { + func textOffset(in cache: DyldCache) -> UInt64? { + let loadCommands = loadCommands + let text: (any SegmentCommandProtocol)? = loadCommands.text64 ?? loadCommands.text + guard let text else { return nil } + return numericCast(text.virtualMemoryAddress) - cache.mainCacheHeader.sharedRegionStart + } + + func textOffset(in cache: FullDyldCache) -> UInt64? { + textOffset(in: cache.mainCache) + } +} From 4547989f88b4b8eee817a7e440c938f1edb8fb95 Mon Sep 17 00:00:00 2001 From: p-x9 <50244599+p-x9@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:21:35 +0900 Subject: [PATCH 3/4] Add `nlistRange` property to DyldCacheLocalSymbolsEntryProtocol - Provides a range of local symbols for the dylib based on start index and count --- .../Protocol/DyldCacheLocalSymbolsEntryProtocol.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/MachOKit/Protocol/DyldCacheLocalSymbolsEntryProtocol.swift b/Sources/MachOKit/Protocol/DyldCacheLocalSymbolsEntryProtocol.swift index 52cac87..ef59ddc 100644 --- a/Sources/MachOKit/Protocol/DyldCacheLocalSymbolsEntryProtocol.swift +++ b/Sources/MachOKit/Protocol/DyldCacheLocalSymbolsEntryProtocol.swift @@ -18,3 +18,9 @@ public protocol DyldCacheLocalSymbolsEntryProtocol { /// Number of local symbols for this dylib var nlistCount: Int { get } } + +extension DyldCacheLocalSymbolsEntryProtocol { + public var nlistRange: Range { + nlistStartIndex ..< nlistStartIndex + nlistCount + } +} From efa5ebf89851b7cf028b5736bb77cf88f3e0608e Mon Sep 17 00:00:00 2001 From: p-x9 <50244599+p-x9@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:23:51 +0900 Subject: [PATCH 4/4] Add test for local symbols in symbol cache - Implement `testLocalSymbolsInSymbolCache` method in both test classes - Validate retrieval of local symbols for Mach-O files --- Tests/MachOKitTests/DyldCachePrintTests.swift | 22 +++++++++++++++++++ .../FullDyldCachePrintTests.swift | 22 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/Tests/MachOKitTests/DyldCachePrintTests.swift b/Tests/MachOKitTests/DyldCachePrintTests.swift index 3d599cd..8ef9733 100644 --- a/Tests/MachOKitTests/DyldCachePrintTests.swift +++ b/Tests/MachOKitTests/DyldCachePrintTests.swift @@ -152,6 +152,28 @@ final class DyldCachePrintTests: XCTestCase { } } + func testLocalSymbolsInSymbolCache() throws { + guard let symbolCache = try cache.symbolCache else { + return + } + guard let info = symbolCache.localSymbolsInfo else { + return + } + let machO = cache.machOFiles().first( + where: { + $0.imagePath.contains("/SwiftUICore") + } + )! + guard let entry = info.entry(for: machO, in: symbolCache) else { + XCTFail("No entry found") + return + } + let symbols = Array(info.symbols(in: symbolCache))[entry.nlistRange] + for symbol in symbols.prefix(100) { + print(symbol.name) + } + } + func testMachOFiles() throws { let machOs = cache.machOFiles() for machO in machOs { diff --git a/Tests/MachOKitTests/FullDyldCachePrintTests.swift b/Tests/MachOKitTests/FullDyldCachePrintTests.swift index a59d820..88c8e55 100644 --- a/Tests/MachOKitTests/FullDyldCachePrintTests.swift +++ b/Tests/MachOKitTests/FullDyldCachePrintTests.swift @@ -148,6 +148,28 @@ final class FullDyldCachePrintTests: XCTestCase { } } + func testLocalSymbolsInSymbolCache() throws { + guard let symbolCache = try cache.symbolCache else { + return + } + guard let info = symbolCache.localSymbolsInfo else { + return + } + let machO = cache.machOFiles().first( + where: { + $0.imagePath.contains("/SwiftUICore") + } + )! + guard let entry = info.entry(for: machO, in: symbolCache) else { + XCTFail("No entry found") + return + } + let symbols = Array(info.symbols(in: symbolCache))[entry.nlistRange] + for symbol in symbols.prefix(100) { + print(symbol.name) + } + } + func testMachOFiles() throws { let machOs = cache.machOFiles() for machO in machOs {