xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/MachoHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // MachoHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 #include "../../Common/MyBuffer.h"
9 #include "../../Common/StringConvert.h"
10 #include "../../Common/IntToString.h"
11 
12 #include "../../Windows/PropVariantUtils.h"
13 
14 #include "../Common/LimitedStreams.h"
15 #include "../Common/ProgressUtils.h"
16 #include "../Common/RegisterArc.h"
17 #include "../Common/StreamUtils.h"
18 
19 #include "../Compress/CopyCoder.h"
20 
Get32(const Byte * p,bool be)21 static UInt32 Get32(const Byte *p, bool be) { if (be) return GetBe32(p); return GetUi32(p); }
Get64(const Byte * p,bool be)22 static UInt64 Get64(const Byte *p, bool be) { if (be) return GetBe64(p); return GetUi64(p); }
23 
24 using namespace NWindows;
25 using namespace NCOM;
26 
27 namespace NArchive {
28 namespace NMacho {
29 
30 #define CPU_ARCH_ABI64 (1 << 24)
31 #define CPU_TYPE_386    7
32 #define CPU_TYPE_ARM   12
33 #define CPU_TYPE_SPARC 14
34 #define CPU_TYPE_PPC   18
35 
36 #define CPU_SUBTYPE_I386_ALL 3
37 
38 // #define CPU_TYPE_PPC64 (CPU_ARCH_ABI64 | CPU_TYPE_PPC)
39 #define CPU_TYPE_AMD64 (CPU_ARCH_ABI64 | CPU_TYPE_386)
40 #define CPU_TYPE_ARM64 (CPU_ARCH_ABI64 | CPU_TYPE_ARM)
41 
42 #define CPU_SUBTYPE_LIB64 ((UInt32)1 << 31)
43 
44 #define CPU_SUBTYPE_POWERPC_970 100
45 
46 static const char * const k_PowerPc_SubTypes[] =
47 {
48   NULL
49   , "601"
50   , "602"
51   , "603"
52   , "603e"
53   , "603ev"
54   , "604"
55   , "604e"
56   , "620"
57   , "750"
58   , "7400"
59   , "7450"
60 };
61 
62 static const CUInt32PCharPair g_CpuPairs[] =
63 {
64   { CPU_TYPE_AMD64, "x64" },
65   { CPU_TYPE_ARM64, "ARM64" },
66   { CPU_TYPE_386, "x86" },
67   { CPU_TYPE_ARM, "ARM" },
68   { CPU_TYPE_SPARC, "SPARC" },
69   { CPU_TYPE_PPC, "PowerPC" }
70 };
71 
72 
73 #define CMD_SEGMENT_32 1
74 #define CMD_SEGMENT_64 0x19
75 
76 #define SECT_TYPE_MASK 0x000000FF
77 #define SECT_ATTR_MASK 0xFFFFFF00
78 
79 #define SECT_ATTR_ZEROFILL 1
80 
81 static const char * const g_SectTypes[] =
82 {
83     "REGULAR"
84   , "ZEROFILL"
85   , "CSTRINGS"
86   , "4BYTE_LITERALS"
87   , "8BYTE_LITERALS"
88   , "LITERAL_POINTERS"
89   , "NON_LAZY_SYMBOL_POINTERS"
90   , "LAZY_SYMBOL_POINTERS"
91   , "SYMBOL_STUBS"
92   , "MOD_INIT_FUNC_POINTERS"
93   , "MOD_TERM_FUNC_POINTERS"
94   , "COALESCED"
95   , "GB_ZEROFILL"
96   , "INTERPOSING"
97   , "16BYTE_LITERALS"
98   , "DTRACE_DOF"
99   , "LAZY_DYLIB_SYMBOL_POINTERS"
100   , "THREAD_LOCAL_REGULAR"
101   , "THREAD_LOCAL_ZEROFILL"
102   , "THREAD_LOCAL_VARIABLES"
103   , "THREAD_LOCAL_VARIABLE_POINTERS"
104   , "THREAD_LOCAL_INIT_FUNCTION_POINTERS"
105 };
106 
107 enum EFileType
108 {
109   kType_OBJECT = 1,
110   kType_EXECUTE,
111   kType_FVMLIB,
112   kType_CORE,
113   kType_PRELOAD,
114   kType_DYLIB,
115   kType_DYLINKER,
116   kType_BUNDLE,
117   kType_DYLIB_STUB,
118   kType_DSYM
119 };
120 
121 static const char * const g_FileTypes[] =
122 {
123     "0"
124   , "OBJECT"
125   , "EXECUTE"
126   , "FVMLIB"
127   , "CORE"
128   , "PRELOAD"
129   , "DYLIB"
130   , "DYLINKER"
131   , "BUNDLE"
132   , "DYLIB_STUB"
133   , "DSYM"
134 };
135 
136 
137 static const char * const g_ArcFlags[] =
138 {
139     "NOUNDEFS"
140   , "INCRLINK"
141   , "DYLDLINK"
142   , "BINDATLOAD"
143   , "PREBOUND"
144   , "SPLIT_SEGS"
145   , "LAZY_INIT"
146   , "TWOLEVEL"
147   , "FORCE_FLAT"
148   , "NOMULTIDEFS"
149   , "NOFIXPREBINDING"
150   , "PREBINDABLE"
151   , "ALLMODSBOUND"
152   , "SUBSECTIONS_VIA_SYMBOLS"
153   , "CANONICAL"
154   , "WEAK_DEFINES"
155   , "BINDS_TO_WEAK"
156   , "ALLOW_STACK_EXECUTION"
157   , "ROOT_SAFE"
158   , "SETUID_SAFE"
159   , "NO_REEXPORTED_DYLIBS"
160   , "PIE"
161   , "DEAD_STRIPPABLE_DYLIB"
162   , "HAS_TLV_DESCRIPTORS"
163   , "NO_HEAP_EXECUTION"
164 };
165 
166 
167 static const CUInt32PCharPair g_SectFlags[] =
168 {
169   { 31, "PURE_INSTRUCTIONS" },
170   { 30, "NO_TOC" },
171   { 29, "STRIP_STATIC_SYMS" },
172   { 28, "NO_DEAD_STRIP" },
173   { 27, "LIVE_SUPPORT" },
174   { 26, "SELF_MODIFYING_CODE" },
175   { 25, "DEBUG" },
176   { 10, "SOME_INSTRUCTIONS" },
177   {  9, "EXT_RELOC" },
178   {  8, "LOC_RELOC" }
179 };
180 
181 
182 // VM_PROT_*
183 static const char * const g_SegmentProt[] =
184 {
185     "READ"
186   , "WRITE"
187   , "EXECUTE"
188   /*
189   , "NO_CHANGE"
190   , "COPY"
191   , "TRUSTED"
192   , "IS_MASK"
193   */
194 };
195 
196 // SG_*
197 
198 static const char * const g_SegmentFlags[] =
199 {
200     "SG_HIGHVM"
201   , "SG_FVMLIB"
202   , "SG_NORELOC"
203   , "SG_PROTECTED_VERSION_1"
204   , "SG_READ_ONLY"
205 };
206 
207 static const unsigned kNameSize = 16;
208 
209 struct CSegment
210 {
211   char Name[kNameSize];
212   UInt32 MaxProt;
213   UInt32 InitProt;
214   UInt32 Flags;
215 };
216 
217 struct CSection
218 {
219   char Name[kNameSize];
220   // char SegName[kNameSize];
221   UInt64 Va;
222   UInt64 Pa;
223   UInt64 VSize;
224   UInt64 PSize;
225 
226   UInt32 Align;
227   UInt32 Flags;
228   unsigned SegmentIndex;
229   bool IsDummy;
230 
CSectionNArchive::NMacho::CSection231   CSection(): IsDummy(false) {}
232   // UInt64 GetPackSize() const { return Flags == SECT_ATTR_ZEROFILL ? 0 : Size; }
GetPackSizeNArchive::NMacho::CSection233   UInt64 GetPackSize() const { return PSize; }
234 };
235 
236 
237 Z7_CLASS_IMP_CHandler_IInArchive_1(
238   IArchiveAllowTail
239 )
240   CMyComPtr<IInStream> _inStream;
241   CObjectVector<CSegment> _segments;
242   CObjectVector<CSection> _sections;
243   bool _allowTail;
244   bool _mode64;
245   bool _be;
246   UInt32 _cpuType;
247   UInt32 _cpuSubType;
248   UInt32 _type;
249   UInt32 _flags;
250   UInt32 _headersSize;
251   UInt64 _totalSize;
252 
253   HRESULT Open2(ISequentialInStream *stream);
254 public:
255   CHandler(): _allowTail(false) {}
256 };
257 
258 static const Byte kArcProps[] =
259 {
260   kpidCpu,
261   kpidBit64,
262   kpidBigEndian,
263   kpidCharacts,
264   kpidHeadersSize
265 };
266 
267 static const Byte kProps[] =
268 {
269   kpidPath,
270   kpidSize,
271   kpidPackSize,
272   kpidCharacts,
273   kpidOffset,
274   kpidVa,
275   kpidClusterSize // Align
276 };
277 
278 IMP_IInArchive_Props
279 IMP_IInArchive_ArcProps
280 
281 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
282 {
283   COM_TRY_BEGIN
284   CPropVariant prop;
285   switch (propID)
286   {
287     case kpidShortComment:
288     case kpidCpu:
289     {
290       AString s;
291       const UInt32 cpu = _cpuType & ~(UInt32)CPU_ARCH_ABI64;
292       UInt32 flag64 = _cpuType & (UInt32)CPU_ARCH_ABI64;
293       {
294         s.Add_UInt32(cpu);
295         for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_CpuPairs); i++)
296         {
297           const CUInt32PCharPair &pair = g_CpuPairs[i];
298           if (pair.Value == cpu || pair.Value == _cpuType)
299           {
300             if (pair.Value == _cpuType)
301               flag64 = 0;
302             s = pair.Name;
303             break;
304           }
305         }
306 
307         if (flag64 != 0)
308           s.Add_OptSpaced("64-bit");
309         else if ((_cpuSubType & CPU_SUBTYPE_LIB64) && _cpuType != CPU_TYPE_AMD64)
310           s.Add_OptSpaced("64-bit-lib");
311       }
312       const UInt32 t = _cpuSubType & ~(UInt32)CPU_SUBTYPE_LIB64;
313       if (t != 0 && (t != CPU_SUBTYPE_I386_ALL || cpu != CPU_TYPE_386))
314       {
315         const char *n = NULL;
316         if (cpu == CPU_TYPE_PPC)
317         {
318           if (t == CPU_SUBTYPE_POWERPC_970)
319             n = "970";
320           else if (t < Z7_ARRAY_SIZE(k_PowerPc_SubTypes))
321             n = k_PowerPc_SubTypes[t];
322         }
323         s.Add_Space();
324         if (n)
325           s += n;
326         else
327           s.Add_UInt32(t);
328       }
329       prop = s;
330       break;
331     }
332     case kpidCharacts:
333     {
334       // TYPE_TO_PROP(g_FileTypes, _type, prop); break;
335       AString res (TypeToString(g_FileTypes, Z7_ARRAY_SIZE(g_FileTypes), _type));
336       const AString s (FlagsToString(g_ArcFlags, Z7_ARRAY_SIZE(g_ArcFlags), _flags));
337       if (!s.IsEmpty())
338       {
339         res.Add_Space();
340         res += s;
341       }
342       prop = res;
343       break;
344     }
345     case kpidPhySize:  prop = _totalSize; break;
346     case kpidHeadersSize:  prop = _headersSize; break;
347     case kpidBit64:  if (_mode64) prop = _mode64; break;
348     case kpidBigEndian:  if (_be) prop = _be; break;
349     case kpidExtension:
350     {
351       const char *ext = NULL;
352       if (_type == kType_OBJECT)
353         ext = "o";
354       else if (_type == kType_BUNDLE)
355         ext = "bundle";
356       else if (_type == kType_DYLIB)
357         ext = "dylib"; // main shared library usually does not have extension
358       if (ext)
359         prop = ext;
360       break;
361     }
362     // case kpidIsSelfExe: prop = (_type == kType_EXECUTE); break;
363   }
364   prop.Detach(value);
365   return S_OK;
366   COM_TRY_END
367 }
368 
369 static void AddName(AString &s, const char *name)
370 {
371   char temp[kNameSize + 1];
372   memcpy(temp, name, kNameSize);
373   temp[kNameSize] = 0;
374   s += temp;
375 }
376 
377 
378 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
379 {
380   COM_TRY_BEGIN
381   CPropVariant prop;
382   const CSection &item = _sections[index];
383   switch (propID)
384   {
385     case kpidPath:
386     {
387       AString s;
388       AddName(s, _segments[item.SegmentIndex].Name);
389       if (!item.IsDummy)
390       {
391         // CSection::SegName and CSegment::Name are same in real cases.
392         // AddName(s, item.SegName);
393         AddName(s, item.Name);
394       }
395       prop = MultiByteToUnicodeString(s);
396       break;
397     }
398     case kpidSize:  /* prop = (UInt64)item.VSize; break; */
399     case kpidPackSize:  prop = (UInt64)item.GetPackSize(); break;
400     case kpidCharacts:
401     {
402       AString res;
403       {
404         if (!item.IsDummy)
405         {
406           {
407             const AString s (TypeToString(g_SectTypes, Z7_ARRAY_SIZE(g_SectTypes), item.Flags & SECT_TYPE_MASK));
408             if (!s.IsEmpty())
409             {
410               res.Add_OptSpaced("sect_type:");
411               res.Add_OptSpaced(s);
412             }
413           }
414           {
415             const AString s (FlagsToString(g_SectFlags, Z7_ARRAY_SIZE(g_SectFlags), item.Flags & SECT_ATTR_MASK));
416             if (!s.IsEmpty())
417             {
418               res.Add_OptSpaced("sect_flags:");
419               res.Add_OptSpaced(s);
420             }
421           }
422         }
423         const CSegment &seg = _segments[item.SegmentIndex];
424         {
425           const AString s (FlagsToString(g_SegmentFlags, Z7_ARRAY_SIZE(g_SegmentFlags), seg.Flags));
426           if (!s.IsEmpty())
427           {
428             res.Add_OptSpaced("seg_flags:");
429             res.Add_OptSpaced(s);
430           }
431         }
432         {
433           const AString s (FlagsToString(g_SegmentProt, Z7_ARRAY_SIZE(g_SegmentProt), seg.MaxProt));
434           if (!s.IsEmpty())
435           {
436             res.Add_OptSpaced("max_prot:");
437             res.Add_OptSpaced(s);
438           }
439         }
440         {
441           const AString s (FlagsToString(g_SegmentProt, Z7_ARRAY_SIZE(g_SegmentProt), seg.InitProt));
442           if (!s.IsEmpty())
443           {
444             res.Add_OptSpaced("init_prot:");
445             res.Add_OptSpaced(s);
446           }
447         }
448       }
449       if (!res.IsEmpty())
450         prop = res;
451       break;
452     }
453     case kpidOffset:  prop = item.Pa; break;
454     case kpidVa:  prop = item.Va; break;
455     case kpidClusterSize:  prop = (UInt32)1 << item.Align; break;
456   }
457   prop.Detach(value);
458   return S_OK;
459   COM_TRY_END
460 }
461 
462 
463 HRESULT CHandler::Open2(ISequentialInStream *stream)
464 {
465   const UInt32 kStartHeaderSize = 7 * 4;
466 
467   Byte header[kStartHeaderSize];
468   RINOK(ReadStream_FALSE(stream, header, kStartHeaderSize))
469   bool be, mode64;
470   switch (GetUi32(header))
471   {
472     case 0xCEFAEDFE:  be = true; mode64 = false; break;
473     case 0xCFFAEDFE:  be = true; mode64 = true; break;
474     case 0xFEEDFACE:  be = false; mode64 = false; break;
475     case 0xFEEDFACF:  be = false; mode64 = true; break;
476     default: return S_FALSE;
477   }
478 
479   _mode64 = mode64;
480   _be = be;
481 
482   const UInt32 numCommands = Get32(header + 0x10, be);
483   const UInt32 commandsSize = Get32(header + 0x14, be);
484 
485   if (numCommands == 0)
486     return S_FALSE;
487 
488   if (commandsSize > (1 << 24) ||
489       numCommands > (1 << 21) ||
490       numCommands * 8 > commandsSize)
491     return S_FALSE;
492 
493   _cpuType = Get32(header + 4, be);
494   _cpuSubType = Get32(header + 8, be);
495   _type = Get32(header + 0xC, be);
496   _flags = Get32(header + 0x18, be);
497 
498   /*
499   // Probably the sections are in first commands. So we can reduce the number of commands.
500   bool reduceCommands = false;
501   const UInt32 kNumReduceCommands = 16;
502   if (numCommands > kNumReduceCommands)
503   {
504     reduceCommands = true;
505     numCommands = kNumReduceCommands;
506   }
507   */
508 
509   UInt32 startHeaderSize = kStartHeaderSize;
510   if (mode64)
511     startHeaderSize += 4;
512   _headersSize = startHeaderSize + commandsSize;
513   _totalSize = _headersSize;
514   CByteArr buffer(_headersSize);
515   RINOK(ReadStream_FALSE(stream, buffer + kStartHeaderSize, _headersSize - kStartHeaderSize))
516   const Byte *buf = buffer + startHeaderSize;
517   size_t size = _headersSize - startHeaderSize;
518   for (UInt32 cmdIndex = 0; cmdIndex < numCommands; cmdIndex++)
519   {
520     if (size < 8)
521       return S_FALSE;
522     UInt32 cmd = Get32(buf, be);
523     UInt32 cmdSize = Get32(buf + 4, be);
524     if (cmdSize < 8)
525       return S_FALSE;
526     if (size < cmdSize)
527       return S_FALSE;
528     if (cmd == CMD_SEGMENT_32 || cmd == CMD_SEGMENT_64)
529     {
530       UInt32 offs = (cmd == CMD_SEGMENT_64) ? 0x48 : 0x38;
531       if (cmdSize < offs)
532         break;
533 
534       UInt64 vmAddr, vmSize, phAddr, phSize;
535 
536       {
537         if (cmd == CMD_SEGMENT_64)
538         {
539           vmAddr = Get64(buf + 0x18, be);
540           vmSize = Get64(buf + 0x20, be);
541           phAddr = Get64(buf + 0x28, be);
542           phSize = Get64(buf + 0x30, be);
543         }
544         else
545         {
546           vmAddr = Get32(buf + 0x18, be);
547           vmSize = Get32(buf + 0x1C, be);
548           phAddr = Get32(buf + 0x20, be);
549           phSize = Get32(buf + 0x24, be);
550         }
551         {
552           UInt64 totalSize = phAddr + phSize;
553           if (totalSize < phAddr)
554             return S_FALSE;
555           if (_totalSize < totalSize)
556             _totalSize = totalSize;
557         }
558       }
559 
560       CSegment seg;
561       seg.MaxProt = Get32(buf + offs - 16, be);
562       seg.InitProt = Get32(buf + offs - 12, be);
563       seg.Flags = Get32(buf + offs - 4, be);
564       memcpy(seg.Name, buf + 8, kNameSize);
565       _segments.Add(seg);
566 
567       UInt32 numSections = Get32(buf + offs - 8, be);
568       if (numSections > (1 << 8))
569         return S_FALSE;
570 
571       if (numSections == 0)
572       {
573         CSection &sect = _sections.AddNew();
574         sect.IsDummy = true;
575         sect.SegmentIndex = _segments.Size() - 1;
576         sect.Va = vmAddr;
577         sect.PSize = phSize;
578         sect.VSize = vmSize;
579         sect.Pa = phAddr;
580         sect.Align = 0;
581         sect.Flags = 0;
582       }
583       else do
584       {
585         const UInt32 headSize = (cmd == CMD_SEGMENT_64) ? 0x50 : 0x44;
586         const Byte *p = buf + offs;
587         if (cmdSize - offs < headSize)
588           break;
589         CSection &sect = _sections.AddNew();
590         unsigned f32Offset;
591         if (cmd == CMD_SEGMENT_64)
592         {
593           sect.Va    = Get64(p + 0x20, be);
594           sect.VSize = Get64(p + 0x28, be);
595           f32Offset = 0x30;
596         }
597         else
598         {
599           sect.Va    = Get32(p + 0x20, be);
600           sect.VSize = Get32(p + 0x24, be);
601           f32Offset = 0x28;
602         }
603         sect.Pa    = Get32(p + f32Offset, be);
604         sect.Align = Get32(p + f32Offset + 4, be);
605         // sect.reloff = Get32(p + f32Offset + 8, be);
606         // sect.nreloc = Get32(p + f32Offset + 12, be);
607         sect.Flags = Get32(p + f32Offset + 16, be);
608         if ((sect.Flags & SECT_TYPE_MASK) == SECT_ATTR_ZEROFILL)
609           sect.PSize = 0;
610         else
611           sect.PSize = sect.VSize;
612         memcpy(sect.Name, p, kNameSize);
613         // memcpy(sect.SegName, p + kNameSize, kNameSize);
614         sect.SegmentIndex = _segments.Size() - 1;
615         offs += headSize;
616       }
617       while (--numSections);
618 
619       if (offs != cmdSize)
620         return S_FALSE;
621     }
622     buf += cmdSize;
623     size -= cmdSize;
624   }
625   // return (reduceCommands || (size == 0)) ? S_OK : S_FALSE;
626   if (size != 0)
627     return S_FALSE;
628 
629   return S_OK;
630 }
631 
632 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream,
633     const UInt64 * /* maxCheckStartPosition */,
634     IArchiveOpenCallback * /* openArchiveCallback */))
635 {
636   COM_TRY_BEGIN
637   Close();
638   RINOK(Open2(inStream))
639   if (!_allowTail)
640   {
641     UInt64 fileSize;
642     RINOK(InStream_GetSize_SeekToEnd(inStream, fileSize))
643     if (fileSize > _totalSize)
644       return S_FALSE;
645   }
646   _inStream = inStream;
647   return S_OK;
648   COM_TRY_END
649 }
650 
651 Z7_COM7F_IMF(CHandler::Close())
652 {
653   _totalSize = 0;
654   _inStream.Release();
655   _sections.Clear();
656   _segments.Clear();
657   return S_OK;
658 }
659 
660 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
661 {
662   *numItems = _sections.Size();
663   return S_OK;
664 }
665 
666 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
667     Int32 testMode, IArchiveExtractCallback *extractCallback))
668 {
669   COM_TRY_BEGIN
670   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
671   if (allFilesMode)
672     numItems = _sections.Size();
673   if (numItems == 0)
674     return S_OK;
675   UInt64 totalSize = 0;
676   UInt32 i;
677   for (i = 0; i < numItems; i++)
678     totalSize += _sections[allFilesMode ? i : indices[i]].GetPackSize();
679   extractCallback->SetTotal(totalSize);
680 
681   UInt64 currentTotalSize = 0;
682   UInt64 currentItemSize;
683 
684   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
685   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
686 
687   CLocalProgress *lps = new CLocalProgress;
688   CMyComPtr<ICompressProgressInfo> progress = lps;
689   lps->Init(extractCallback, false);
690 
691   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
692   CMyComPtr<ISequentialInStream> inStream(streamSpec);
693   streamSpec->SetStream(_inStream);
694 
695   for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
696   {
697     lps->InSize = lps->OutSize = currentTotalSize;
698     RINOK(lps->SetCur())
699     const Int32 askMode = testMode ?
700         NExtract::NAskMode::kTest :
701         NExtract::NAskMode::kExtract;
702     const UInt32 index = allFilesMode ? i : indices[i];
703     const CSection &item = _sections[index];
704     currentItemSize = item.GetPackSize();
705 
706     CMyComPtr<ISequentialOutStream> outStream;
707     RINOK(extractCallback->GetStream(index, &outStream, askMode))
708     if (!testMode && !outStream)
709       continue;
710 
711     RINOK(extractCallback->PrepareOperation(askMode))
712     RINOK(InStream_SeekSet(_inStream, item.Pa))
713     streamSpec->Init(currentItemSize);
714     RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress))
715     outStream.Release();
716     RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == currentItemSize ?
717         NExtract::NOperationResult::kOK:
718         NExtract::NOperationResult::kDataError))
719   }
720   return S_OK;
721   COM_TRY_END
722 }
723 
724 Z7_COM7F_IMF(CHandler::AllowTail(Int32 allowTail))
725 {
726   _allowTail = IntToBool(allowTail);
727   return S_OK;
728 }
729 
730 static const Byte k_Signature[] = {
731   4, 0xCE, 0xFA, 0xED, 0xFE,
732   4, 0xCF, 0xFA, 0xED, 0xFE,
733   4, 0xFE, 0xED, 0xFA, 0xCE,
734   4, 0xFE, 0xED, 0xFA, 0xCF };
735 
736 REGISTER_ARC_I(
737   "MachO", "macho", NULL, 0xDF,
738   k_Signature,
739   0,
740   NArcInfoFlags::kMultiSignature |
741   NArcInfoFlags::kPreArc,
742   NULL)
743 
744 }}
745