@@ -8,9 +8,14 @@ import multiprocessing
88import pathlib
99import signal
1010import sys
11-
1211import progressbar
1312
13+ from typing import (
14+ List ,
15+ BinaryIO ,
16+ Tuple
17+ )
18+
1419from DyldExtractor .converter import (
1520 linkedit_optimizer ,
1621 macho_offset ,
@@ -21,6 +26,7 @@ from DyldExtractor.converter import (
2126from DyldExtractor .dyld .dyld_context import DyldContext
2227from DyldExtractor .extraction_context import ExtractionContext
2328from DyldExtractor .macho .macho_context import MachOContext
29+ from DyldExtractor .file_context import FileContext
2430
2531# check dependencies
2632try :
@@ -82,6 +88,38 @@ def _workerInitializer():
8288 pass
8389
8490
91+ def _openSubCaches (
92+ mainCachePath : str ,
93+ numSubCaches : int
94+ ) -> Tuple [List [FileContext ], List [BinaryIO ]]:
95+ """Create FileContext objects for each sub cache.
96+
97+ Assumes that each sub cache has the same base name as the
98+ main cache, and that the suffixes are preserved.
99+
100+ Also opens the symbols cache, and adds it to the end of
101+ the list.
102+
103+ Returns:
104+ A list of subcaches, and their file objects, which must be closed!
105+ """
106+ subCaches = []
107+ subCachesFiles = []
108+
109+ subCacheSuffixes = [i for i in range (1 , numSubCaches + 1 )]
110+ subCacheSuffixes .append ("symbols" )
111+ for cacheSuffix in subCacheSuffixes :
112+ subCachePath = f"{ mainCachePath } .{ cacheSuffix } "
113+ cacheFileObject = open (subCachePath , mode = "rb" )
114+ cacheFileCtx = FileContext (cacheFileObject )
115+
116+ subCaches .append (cacheFileCtx )
117+ subCachesFiles .append (cacheFileObject )
118+ pass
119+
120+ return subCaches , subCachesFiles
121+
122+
85123def _extractImage (
86124 dyldPath : pathlib .Path ,
87125 outputDir : pathlib .Path ,
@@ -113,43 +151,75 @@ def _extractImage(
113151
114152 # Process the image
115153 with open (dyldPath , "rb" ) as f :
116- dyldFile = mmap .mmap (f .fileno (), 0 , access = mmap .ACCESS_READ )
117- dyldCtx = DyldContext (dyldFile )
118- imageOffset = dyldCtx .convertAddr (dyldCtx .images [imageIndex ].address )
119-
120- machoFile = mmap .mmap (f .fileno (), 0 , access = mmap .ACCESS_COPY )
121- machoCtx = MachOContext (machoFile , imageOffset )
122-
123- extractionCtx = ExtractionContext (
124- dyldCtx ,
125- machoCtx ,
126- _DummyProgressBar (),
127- logger
128- )
129-
154+ subCacheFiles : List [BinaryIO ] = []
130155 try :
156+ dyldFileCtx = FileContext (f )
157+ dyldCtx = DyldContext (dyldFileCtx )
158+
159+ # add sub caches if there are any
160+ if dyldCtx .hasSubCaches ():
161+ subCacheFileCtxs , subCacheFiles = _openSubCaches (
162+ dyldPath ,
163+ dyldCtx .header .numSubCaches
164+ )
165+ dyldCtx .addSubCaches (subCacheFileCtxs )
166+ pass
167+
168+ machoOffset , context = dyldCtx .convertAddr (
169+ dyldCtx .images [imageIndex ].address
170+ )
171+ machoCtx = MachOContext (
172+ context .fileCtx .makeCopy (copyMode = True ),
173+ machoOffset
174+ )
175+
176+ # Add sub caches if necessary
177+ if dyldCtx .hasSubCaches ():
178+ mappings = dyldCtx .mappings
179+ mainFileMap = next (
180+ (mapping [0 ] for mapping in mappings if mapping [1 ] == context )
181+ )
182+ machoCtx .addSubfiles (
183+ mainFileMap ,
184+ ((m , ctx .fileCtx .makeCopy (copyMode = True )) for m , ctx in mappings )
185+ )
186+ pass
187+
188+ extractionCtx = ExtractionContext (
189+ dyldCtx ,
190+ machoCtx ,
191+ _DummyProgressBar (),
192+ logger
193+ )
194+
131195 slide_info .processSlideInfo (extractionCtx )
132196 linkedit_optimizer .optimizeLinkedit (extractionCtx )
133197 stub_fixer .fixStubs (extractionCtx )
134198 objc_fixer .fixObjC (extractionCtx )
135- macho_offset .optimizeOffsets (extractionCtx )
199+
200+ writeProcedures = macho_offset .optimizeOffsets (extractionCtx )
136201
137202 # write the file
138203 outputPath .parent .mkdir (parents = True , exist_ok = True )
139- with open (outputPath , "wb+" ) as outFile :
140- newMachoCtx = extractionCtx .machoCtx
141-
142- # get the size of the new file
143- linkeditSeg = newMachoCtx .segments [b"__LINKEDIT" ].seg
144- fileSize = linkeditSeg .fileoff + linkeditSeg .filesize
145-
146- newMachoCtx .file .seek (0 )
147- outFile .write (newMachoCtx .file .read (fileSize ))
204+ with open (outputPath , "wb" ) as outFile :
205+ for procedure in writeProcedures :
206+ outFile .seek (procedure .writeOffset )
207+ outFile .write (
208+ procedure .fileCtx .getBytes (procedure .readOffset , procedure .size )
209+ )
210+ pass
148211 pass
149212 pass
213+
150214 except Exception as e :
151215 logger .exception (e )
152216 pass
217+
218+ finally :
219+ for file in subCacheFiles :
220+ file .close ()
221+ pass
222+ pass
153223 pass
154224
155225 handler .close ()
@@ -186,11 +256,13 @@ def _main() -> None:
186256 # create a list of image paths
187257 imagePaths : list [str ] = []
188258 with open (args .dyld_path , "rb" ) as f :
189- dyldFile = mmap . mmap ( f . fileno (), 0 , access = mmap . ACCESS_READ )
190- dyldCtx = DyldContext (dyldFile )
259+ dyldFileCtx = FileContext ( f )
260+ dyldCtx = DyldContext (dyldFileCtx )
191261
192262 for image in dyldCtx .images :
193- imagePath = dyldCtx .readString (image .pathFileOffset )[0 :- 1 ].decode ("utf-8" )
263+ imagePath = dyldCtx .fileCtx .readString (
264+ image .pathFileOffset
265+ )[0 :- 1 ].decode ("utf-8" )
194266 imagePaths .append (imagePath )
195267 pass
196268 pass
0 commit comments