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 § = _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 § = _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