Skip to content

Commit a11b232

Browse files
committed
Consider reading when the data segment is separated
1 parent 7c66ce2 commit a11b232

File tree

2 files changed

+103
-9
lines changed

2 files changed

+103
-9
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// FullDyldCache+.swift
3+
// MachOObjCSection
4+
//
5+
// Created by p-x9 on 2025/09/28
6+
//
7+
//
8+
9+
import Foundation
10+
import MachOKit
11+
#if compiler(>=6.0) || (compiler(>=5.10) && hasFeature(AccessLevelOnImport))
12+
internal import FileIO
13+
#else
14+
@_implementationOnly import FileIO
15+
#endif
16+
17+
extension FullDyldCache {
18+
internal typealias File = ConcatenatedMemoryMappedFile
19+
20+
var fileHandle: File {
21+
let mainCache = try! DyldCache(url: url)
22+
23+
let subCacheSuffixes = mainCache.subCaches?.map {
24+
$0.fileSuffix
25+
} ?? []
26+
var urls = [url]
27+
urls += subCacheSuffixes.map {
28+
URL(fileURLWithPath: url.path + $0)
29+
}
30+
31+
return try! .open(
32+
urls: urls,
33+
isWritable: false
34+
)
35+
}
36+
}
37+
38+
extension FullDyldCache {
39+
func fileSegment(forOffset offset: UInt64) -> File.FileSegment? {
40+
try? fileHandle._file(for: numericCast(offset))
41+
}
42+
}

Sources/MachOObjCSection/MachOFile+ObjectiveC.swift

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ extension MachOFile.ObjectiveC {
3535
return nil
3636
}
3737

38-
return try! machO.fileHandle.read(
39-
offset: numericCast(__objc_imageinfo.offset + machO.headerStartOffset)
40-
)
38+
guard let fileSlice = machO._fileSliceForSection(section: __objc_imageinfo) else {
39+
return nil
40+
}
41+
return try? fileSlice.read(offset: 0)
4142
}
4243
}
4344

@@ -272,8 +273,11 @@ extension MachOFile.ObjectiveC {
272273
in machO: MachOFile,
273274
isCatlist2: Bool = false
274275
) -> [Categgory]? {
275-
let data = try! machO.fileHandle.readData(
276-
offset: numericCast(section.offset + machO.headerStartOffset),
276+
guard let fileSlice = machO._fileSliceForSection(section: section) else {
277+
return nil
278+
}
279+
let data = try! fileSlice.readData(
280+
offset: 0,
277281
length: section.size
278282
)
279283

@@ -311,8 +315,11 @@ extension MachOFile.ObjectiveC {
311315
from section: any SectionProtocol,
312316
in machO: MachOFile
313317
) -> [Class]? {
314-
let data = try! machO.fileHandle.readData(
315-
offset: numericCast(section.offset + machO.headerStartOffset),
318+
guard let fileSlice = machO._fileSliceForSection(section: section) else {
319+
return nil
320+
}
321+
let data = try! fileSlice.readData(
322+
offset: 0,
316323
length: section.size
317324
)
318325

@@ -346,8 +353,11 @@ extension MachOFile.ObjectiveC {
346353
from section: any SectionProtocol,
347354
in machO: MachOFile
348355
) -> [Protocol]? {
349-
let data = try! machO.fileHandle.readData(
350-
offset: numericCast(section.offset + machO.headerStartOffset),
356+
guard let fileSlice = machO._fileSliceForSection(section: section) else {
357+
return nil
358+
}
359+
let data = try! fileSlice.readData(
360+
offset: 0,
351361
length: section.size
352362
)
353363

@@ -373,3 +383,45 @@ extension MachOFile.ObjectiveC {
373383
}
374384
}
375385
}
386+
387+
extension MachOFile {
388+
fileprivate func _fileSliceForSection(
389+
section: any SectionProtocol
390+
) -> File.FileSlice? {
391+
let text: (any SegmentCommandProtocol)? = loadCommands.text64 ?? loadCommands.text
392+
guard let text else { return nil }
393+
394+
let maxFileOffsetToCheck = text.fileOffset + section.address - text.virtualMemoryAddress
395+
let isWithinFileRange: Bool = fileHandle.size >= maxFileOffsetToCheck
396+
397+
// 1) text.vmaddr < linkedit.vmaddr
398+
// 2) fileoff_diff <= vmaddr_diff
399+
// 3) If both exist in the same file
400+
// text.fileoff < linkedit.fileoff <= text.fileoff + vmaddr_diff
401+
// 4) if fileHandle.size < text.fileoff + vmaddr_diff
402+
// both exist in the same file
403+
404+
// The linkedit data in iOS is stored together in a separate, independent cache.
405+
// (.0x.linkeditdata)
406+
if isLoadedFromDyldCache && !isWithinFileRange {
407+
guard let fullCache = self.fullCache,
408+
let fileOffset = fullCache.fileOffset(
409+
of: numericCast(section.address)
410+
),
411+
let segment = fullCache.fileSegment(
412+
forOffset: fileOffset
413+
) else {
414+
return nil
415+
}
416+
return try? segment._file.fileSlice(
417+
offset: numericCast(fileOffset) - segment.offset,
418+
length: section.size
419+
)
420+
} else {
421+
return try? fileHandle.fileSlice(
422+
offset: headerStartOffset + section.offset,
423+
length: section.size
424+
)
425+
}
426+
}
427+
}

0 commit comments

Comments
 (0)