Skip to content

Commit 5daf12a

Browse files
authored
Merge pull request #231 from p-x9/feature/fix-linkeditdata-reading
Fix to read correctly even when linkedit does not exist in the same cache
2 parents cc15a6b + 02c13d7 commit 5daf12a

File tree

5 files changed

+170
-65
lines changed

5 files changed

+170
-65
lines changed

Sources/MachOKit/MachOFile+ExportTrie.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,10 @@ extension MachOFile.ExportTrie {
113113
exportSize: Int,
114114
ldVersion: Version?
115115
) {
116-
let offset = machO.headerStartOffset + exportOffset
117-
let data = try! machO.fileHandle.readData(
118-
offset: offset,
116+
let data = machO._readLinkEditData(
117+
offset: exportOffset,
119118
length: exportSize
120-
)
119+
)!
121120

122121
self.init(
123122
exportOffset: exportOffset,

Sources/MachOKit/MachOFile+FunctionStarts.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@ extension MachOFile.FunctionStarts {
3131
functionStartsSize: Int,
3232
functionStartBase: UInt
3333
) {
34-
let offset = machO.headerStartOffset + functionStartsOffset
35-
let data = try! machO.fileHandle.readData(
36-
offset: offset,
34+
let data = machO._readLinkEditData(
35+
offset: functionStartsOffset,
3736
length: functionStartsSize
38-
)
37+
)!
3938

4039
self.init(
4140
data: data,

Sources/MachOKit/MachOFile+Strings.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ extension MachOFile {
3030

3131
public let isSwapped: Bool
3232

33+
init(
34+
fileSlice: FileSlice,
35+
offset: Int,
36+
size: Int,
37+
isSwapped: Bool
38+
) {
39+
self.fileSlice = fileSlice
40+
self.offset = offset
41+
self.size = size
42+
self.isSwapped = isSwapped
43+
}
44+
3345
public func makeIterator() -> Iterator {
3446
.init(fileSlice: fileSlice, isSwapped: isSwapped)
3547
}

Sources/MachOKit/MachOFile+Symbols.swift

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,15 @@ extension MachOFile.Symbols64 {
6868
machO: MachOFile,
6969
symtab: LoadCommandInfo<symtab_command>
7070
) {
71-
let stringsSlice = try! machO.fileHandle.fileSlice(
72-
offset: machO.headerStartOffset + numericCast(symtab.stroff),
71+
let stringsSlice = machO._fileSliceForLinkEditData(
72+
offset: numericCast(symtab.stroff),
7373
length: numericCast(symtab.strsize)
74-
)
74+
)!
7575

76-
let symbolsSlice = try! machO.fileHandle.fileSlice(
77-
offset: machO.headerStartOffset + numericCast(symtab.symoff),
76+
let symbolsSlice = machO._fileSliceForLinkEditData(
77+
offset: numericCast(symtab.symoff),
7878
length: numericCast(symtab.nsyms) * MemoryLayout<nlist_64>.size
79-
)
79+
)!
8080

8181
self.init(
8282
symtab: symtab,
@@ -214,15 +214,15 @@ extension MachOFile.Symbols {
214214
machO: MachOFile,
215215
symtab: LoadCommandInfo<symtab_command>
216216
) {
217-
let stringsSlice = try! machO.fileHandle.fileSlice(
218-
offset: machO.headerStartOffset + numericCast(symtab.stroff),
219-
length: Int(symtab.strsize)
220-
)
217+
let stringsSlice = machO._fileSliceForLinkEditData(
218+
offset: numericCast(symtab.stroff),
219+
length: numericCast(symtab.strsize)
220+
)!
221221

222-
let symbolsSlice = try! machO.fileHandle.fileSlice(
223-
offset: machO.headerStartOffset + numericCast(symtab.symoff),
224-
length: Int(symtab.nsyms) * MemoryLayout<nlist>.size
225-
)
222+
let symbolsSlice = machO._fileSliceForLinkEditData(
223+
offset: numericCast(symtab.symoff),
224+
length: numericCast(symtab.nsyms) * MemoryLayout<nlist>.size
225+
)!
226226

227227
self.init(
228228
symtab: symtab,

Sources/MachOKit/MachOFile.swift

Lines changed: 138 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -218,36 +218,46 @@ extension MachOFile {
218218
public var indirectSymbols: IndirectSymbols? {
219219
guard let dysymtab = loadCommands.dysymtab else { return nil }
220220

221-
let offset: UInt64 = numericCast(headerStartOffset) + numericCast(dysymtab.indirectsymoff)
221+
let offset: UInt64 = numericCast(dysymtab.indirectsymoff)
222222
let numberOfElements: Int = numericCast(dysymtab.nindirectsyms)
223223

224-
return fileHandle.readDataSequence(
225-
offset: offset,
226-
numberOfElements: numberOfElements,
227-
swapHandler: { data in
228-
guard self.isSwapped else { return }
229-
data.withUnsafeMutableBytes {
230-
let buffer = $0.assumingMemoryBound(to: UInt32.self)
231-
for i in 0 ..< numberOfElements {
232-
buffer[i] = buffer[i].byteSwapped
233-
}
224+
guard var data = _readLinkEditData(
225+
offset: numericCast(offset),
226+
length: MemoryLayout<UInt32>.size * numberOfElements
227+
) else { return nil }
228+
229+
if isSwapped {
230+
data.withUnsafeMutableBytes {
231+
let buffer = $0.assumingMemoryBound(to: UInt32.self)
232+
for i in 0 ..< numberOfElements {
233+
buffer[i] = buffer[i].byteSwapped
234234
}
235235
}
236+
}
237+
238+
return .init(
239+
data: data,
240+
numberOfElements: numberOfElements
236241
)
237242
}
238243
}
239244

240245
extension MachOFile {
241246
public var symbolStrings: Strings? {
242-
if let symtab = loadCommands.symtab {
243-
return Strings(
244-
machO: self,
245-
offset: headerStartOffset + Int(symtab.stroff),
246-
size: Int(symtab.strsize),
247-
isSwapped: isSwapped
248-
)
247+
guard let symtab = loadCommands.symtab else {
248+
return nil
249249
}
250-
return nil
250+
guard let fileSlice = _fileSliceForLinkEditData(
251+
offset: numericCast(symtab.stroff),
252+
length: numericCast(symtab.strsize)
253+
) else { return nil }
254+
255+
return .init(
256+
fileSlice: fileSlice,
257+
offset: numericCast(symtab.stroff),
258+
size: numericCast(symtab.strsize),
259+
isSwapped: isSwapped
260+
)
251261
}
252262
}
253263

@@ -421,8 +431,13 @@ extension MachOFile {
421431
return nil
422432
}
423433

424-
let entries: DataSequence<DataInCodeEntry> = fileHandle.readDataSequence(
425-
offset: numericCast(headerStartOffset) + numericCast(dataInCode.dataoff),
434+
guard let data = _readLinkEditData(
435+
offset: numericCast(dataInCode.dataoff),
436+
length: numericCast(dataInCode.datasize)
437+
) else { return nil }
438+
439+
let entries: DataSequence<DataInCodeEntry> = .init(
440+
data: data,
426441
numberOfElements: numericCast(dataInCode.datasize) / DataInCodeEntry.layoutSize
427442
)
428443

@@ -449,12 +464,13 @@ extension MachOFile {
449464
guard let info = loadCommands.dyldChainedFixups else {
450465
return nil
451466
}
467+
guard let fileSlice = _fileSliceForLinkEditData(
468+
offset: numericCast(info.dataoff),
469+
length: numericCast(info.datasize)
470+
) else { return nil }
452471

453472
return .init(
454-
fileSice: try! fileHandle.fileSlice(
455-
offset: headerStartOffset + numericCast(info.dataoff),
456-
length: numericCast(info.datasize)
457-
),
473+
fileSice: fileSlice,
458474
isSwapped: isSwapped
459475
)
460476
}
@@ -465,18 +481,27 @@ extension MachOFile {
465481
guard let dysymtab = loadCommands.dysymtab else {
466482
return nil
467483
}
468-
return fileHandle.readDataSequence(
469-
offset: numericCast(dysymtab.extreloff),
470-
numberOfElements: numericCast(dysymtab.nextrel),
471-
swapHandler: { data in
472-
guard self.isSwapped else { return }
473-
data.withUnsafeMutableBytes {
474-
guard let baseAddress = $0.baseAddress else { return }
475-
let ptr = baseAddress
476-
.assumingMemoryBound(to: relocation_info.self)
477-
swap_relocation_info(ptr, dysymtab.nextrel, NXHostByteOrder())
478-
}
484+
485+
let offset: UInt64 = numericCast(dysymtab.extreloff)
486+
let numberOfElements: Int = numericCast(dysymtab.nextrel)
487+
488+
guard var data = _readLinkEditData(
489+
offset: numericCast(offset),
490+
length: MemoryLayout<UInt64>.size * numberOfElements
491+
) else { return nil }
492+
493+
if isSwapped {
494+
data.withUnsafeMutableBytes {
495+
guard let baseAddress = $0.baseAddress else { return }
496+
let ptr = baseAddress
497+
.assumingMemoryBound(to: relocation_info.self)
498+
swap_relocation_info(ptr, dysymtab.nextrel, NXHostByteOrder())
479499
}
500+
}
501+
502+
return .init(
503+
data: data,
504+
numberOfElements: numberOfElements
480505
)
481506
}
482507

@@ -508,11 +533,13 @@ extension MachOFile {
508533
guard let info = loadCommands.codeSignature else {
509534
return nil
510535
}
536+
guard let fileSlice = _fileSliceForLinkEditData(
537+
offset: numericCast(info.dataoff),
538+
length: numericCast(info.datasize)
539+
) else { return nil }
540+
511541
return .init(
512-
fileSice: try! fileHandle.fileSlice(
513-
offset: headerStartOffset + numericCast(info.dataoff),
514-
length: numericCast(info.datasize)
515-
)
542+
fileSice: fileSlice
516543
)
517544
}
518545
}
@@ -522,6 +549,18 @@ extension MachOFile {
522549
public var isLoadedFromDyldCache: Bool {
523550
headerStartOffsetInCache > 0
524551
}
552+
553+
internal var cache: DyldCache? {
554+
try? .init(url: url)
555+
}
556+
557+
internal var fullCache: FullDyldCache? {
558+
try? .init(
559+
url: url
560+
.deletingPathExtension()
561+
.deletingPathExtension()
562+
)
563+
}
525564
}
526565

527566
extension MachOFile {
@@ -587,11 +626,67 @@ extension MachOFile {
587626
}
588627
}
589628

629+
extension MachOFile {
630+
internal func _fileSliceForLinkEditData(
631+
offset: Int, // linkedit_data_command->dataoff (linkedit.fileoff + x)
632+
length: Int
633+
) -> File.FileSlice? {
634+
let linkedit: (any SegmentCommandProtocol)? = loadCommands.linkedit64 ?? loadCommands.linkedit
635+
guard let linkedit else { return nil }
636+
guard linkedit.fileOffset + linkedit.fileSize >= offset + length else { return nil }
637+
638+
// The linkeditdata in iOS is stored together in a separate, independent cache.
639+
// (.0x.linkeditdata)
640+
if isLoadedFromDyldCache {
641+
let offset = offset - numericCast(linkedit.fileOffset)
642+
guard let fullCache = self.fullCache,
643+
let fileOffset = fullCache.fileOffset(
644+
of: numericCast(linkedit.virtualMemoryAddress + offset)
645+
),
646+
let (_, segment) = fullCache.urlAndFileSegment(
647+
forOffset: fileOffset
648+
) else {
649+
return nil
650+
}
651+
return try? segment._file.fileSlice(
652+
offset: numericCast(fileOffset) - segment.offset,
653+
length: length
654+
)
655+
} else {
656+
return try? fileHandle.fileSlice(
657+
offset: headerStartOffset + offset,
658+
length: length
659+
)
660+
}
661+
}
662+
663+
/// Reads the data in the linkedit segment appropriately.
664+
///
665+
/// The linkedit data in the machO file obtained from the dyld cache may be separated in a separate sub cache file.
666+
/// (e.g. dyld cache in iOS except Simulator)
667+
///
668+
/// The data related to the following load command exists in linkedit.
669+
/// - symtab
670+
/// - dysymtab
671+
/// - linkedit_data_command
672+
/// - exports trie
673+
public func _readLinkEditData(
674+
offset: Int, // linkedit_data_command->dataoff (linkedit.fileoff + x)
675+
length: Int
676+
) -> Data? {
677+
guard let fileSlice = _fileSliceForLinkEditData(
678+
offset: offset,
679+
length: length
680+
) else { return nil }
681+
return try? fileSlice.readAllData()
682+
}
683+
}
684+
590685
extension MachOFile {
591686
// https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/common/MetadataVisitor.cpp#L262
592687
public func resolveRebase(at offset: UInt64) -> UInt64? {
593688
if isLoadedFromDyldCache,
594-
let cache = try? DyldCache(url: url) {
689+
let cache = self.cache {
595690
return cache.resolveRebase(at: offset)
596691
}
597692

@@ -609,7 +704,7 @@ extension MachOFile {
609704

610705
public func resolveOptionalRebase(at offset: UInt64) -> UInt64? {
611706
if isLoadedFromDyldCache,
612-
let cache = try? DyldCache(url: url) {
707+
let cache = self.cache {
613708
return cache.resolveOptionalRebase(at: offset)
614709
}
615710

@@ -759,7 +854,7 @@ extension MachOFile {
759854
}
760855

761856
if header.isInDyldCache,
762-
let cache = try? DyldCache(url: url) {
857+
let cache = self.cache {
763858
return cache.header.platform == .macOS
764859
}
765860

0 commit comments

Comments
 (0)