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 (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 };
99 
100 enum EFileType
101 {
102   kType_OBJECT = 1,
103   kType_EXECUTE,
104   kType_FVMLIB,
105   kType_CORE,
106   kType_PRELOAD,
107   kType_DYLIB,
108   kType_DYLINKER,
109   kType_BUNDLE,
110   kType_DYLIB_STUB,
111   kType_DSYM
112 };
113 
114 static const char * const g_FileTypes[] =
115 {
116     "0"
117   , "OBJECT"
118   , "EXECUTE"
119   , "FVMLIB"
120   , "CORE"
121   , "PRELOAD"
122   , "DYLIB"
123   , "DYLINKER"
124   , "BUNDLE"
125   , "DYLIB_STUB"
126   , "DSYM"
127 };
128 
129 
130 static const char * const g_ArcFlags[] =
131 {
132     "NOUNDEFS"
133   , "INCRLINK"
134   , "DYLDLINK"
135   , "BINDATLOAD"
136   , "PREBOUND"
137   , "SPLIT_SEGS"
138   , "LAZY_INIT"
139   , "TWOLEVEL"
140   , "FORCE_FLAT"
141   , "NOMULTIDEFS"
142   , "NOFIXPREBINDING"
143   , "PREBINDABLE"
144   , "ALLMODSBOUND"
145   , "SUBSECTIONS_VIA_SYMBOLS"
146   , "CANONICAL"
147   , "WEAK_DEFINES"
148   , "BINDS_TO_WEAK"
149   , "ALLOW_STACK_EXECUTION"
150   , "ROOT_SAFE"
151   , "SETUID_SAFE"
152   , "NO_REEXPORTED_DYLIBS"
153   , "PIE"
154   , "DEAD_STRIPPABLE_DYLIB"
155   , "HAS_TLV_DESCRIPTORS"
156   , "NO_HEAP_EXECUTION"
157 };
158 
159 
160 static const CUInt32PCharPair g_Flags[] =
161 {
162   { 31, "PURE_INSTRUCTIONS" },
163   { 30, "NO_TOC" },
164   { 29, "STRIP_STATIC_SYMS" },
165   { 28, "NO_DEAD_STRIP" },
166   { 27, "LIVE_SUPPORT" },
167   { 26, "SELF_MODIFYING_CODE" },
168   { 25, "DEBUG" },
169   { 10, "SOME_INSTRUCTIONS" },
170   {  9, "EXT_RELOC" },
171   {  8, "LOC_RELOC" }
172 };
173 
174 static const unsigned kNameSize = 16;
175 
176 struct CSegment
177 {
178   char Name[kNameSize];
179 };
180 
181 struct CSection
182 {
183   char Name[kNameSize];
184   char SegName[kNameSize];
185   UInt64 Va;
186   UInt64 Pa;
187   UInt64 VSize;
188   UInt64 PSize;
189 
190   UInt32 Flags;
191   int SegmentIndex;
192 
193   bool IsDummy;
194 
CSectionNArchive::NMacho::CSection195   CSection(): IsDummy(false) {}
196   // UInt64 GetPackSize() const { return Flags == SECT_ATTR_ZEROFILL ? 0 : Size; }
GetPackSizeNArchive::NMacho::CSection197   UInt64 GetPackSize() const { return PSize; }
198 };
199 
200 
201 class CHandler:
202   public IInArchive,
203   public IArchiveAllowTail,
204   public CMyUnknownImp
205 {
206   CMyComPtr<IInStream> _inStream;
207   CObjectVector<CSegment> _segments;
208   CObjectVector<CSection> _sections;
209   bool _allowTail;
210   bool _mode64;
211   bool _be;
212   UInt32 _cpuType;
213   UInt32 _cpuSubType;
214   UInt32 _type;
215   UInt32 _flags;
216   UInt32 _headersSize;
217   UInt64 _totalSize;
218 
219   HRESULT Open2(ISequentialInStream *stream);
220 public:
221   MY_UNKNOWN_IMP2(IInArchive, IArchiveAllowTail)
222   INTERFACE_IInArchive(;)
223   STDMETHOD(AllowTail)(Int32 allowTail);
CHandler()224   CHandler(): _allowTail(false) {}
225 };
226 
227 static const Byte kArcProps[] =
228 {
229   kpidCpu,
230   kpidBit64,
231   kpidBigEndian,
232   kpidCharacts,
233   kpidHeadersSize
234 };
235 
236 static const Byte kProps[] =
237 {
238   kpidPath,
239   kpidSize,
240   kpidPackSize,
241   kpidCharacts,
242   kpidOffset,
243   kpidVa
244 };
245 
246 IMP_IInArchive_Props
247 IMP_IInArchive_ArcProps
248 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)249 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
250 {
251   COM_TRY_BEGIN
252   CPropVariant prop;
253   switch (propID)
254   {
255     case kpidShortComment:
256     case kpidCpu:
257     {
258       AString s;
259       char temp[16];
260       UInt32 cpu = _cpuType & ~(UInt32)CPU_ARCH_ABI64;
261       UInt32 flag64 = _cpuType & (UInt32)CPU_ARCH_ABI64;
262       {
263         const char *n = NULL;
264         for (unsigned i = 0; i < ARRAY_SIZE(g_CpuPairs); i++)
265         {
266           const CUInt32PCharPair &pair = g_CpuPairs[i];
267           if (pair.Value == cpu || pair.Value == _cpuType)
268           {
269             if (pair.Value == _cpuType)
270               flag64 = 0;
271             n = pair.Name;
272             break;
273           }
274         }
275         if (!n)
276         {
277           ConvertUInt32ToString(cpu, temp);
278           n = temp;
279         }
280         s = n;
281 
282         if (flag64 != 0)
283           s += " 64-bit";
284         else if ((_cpuSubType & CPU_SUBTYPE_LIB64) && _cpuType != CPU_TYPE_AMD64)
285           s += " 64-bit-lib";
286       }
287       UInt32 t = _cpuSubType & ~(UInt32)CPU_SUBTYPE_LIB64;
288       if (t != 0 && (t != CPU_SUBTYPE_I386_ALL || cpu != CPU_TYPE_386))
289       {
290         const char *n = NULL;
291         if (cpu == CPU_TYPE_PPC)
292         {
293           if (t == CPU_SUBTYPE_POWERPC_970)
294             n = "970";
295           else if (t < ARRAY_SIZE(k_PowerPc_SubTypes))
296             n = k_PowerPc_SubTypes[t];
297         }
298         if (!n)
299         {
300           ConvertUInt32ToString(t, temp);
301           n = temp;
302         }
303         s.Add_Space();
304         s += n;
305       }
306       prop = s;
307       break;
308     }
309     case kpidCharacts:
310     {
311       // TYPE_TO_PROP(g_FileTypes, _type, prop); break;
312       AString res (TypeToString(g_FileTypes, ARRAY_SIZE(g_FileTypes), _type));
313       AString s (FlagsToString(g_ArcFlags, ARRAY_SIZE(g_ArcFlags), _flags));
314       if (!s.IsEmpty())
315       {
316         res.Add_Space();
317         res += s;
318       }
319       prop = res;
320       break;
321     }
322     case kpidPhySize:  prop = _totalSize; break;
323     case kpidHeadersSize:  prop = _headersSize; break;
324     case kpidBit64:  if (_mode64) prop = _mode64; break;
325     case kpidBigEndian:  if (_be) prop = _be; break;
326     case kpidExtension:
327     {
328       const char *ext = NULL;
329       if (_type == kType_OBJECT)
330         ext = "o";
331       else if (_type == kType_BUNDLE)
332         ext = "bundle";
333       else if (_type == kType_DYLIB)
334         ext = "dylib"; // main shared library usually does not have extension
335       if (ext)
336         prop = ext;
337       break;
338     }
339     // case kpidIsSelfExe: prop = (_type == kType_EXECUTE); break;
340   }
341   prop.Detach(value);
342   return S_OK;
343   COM_TRY_END
344 }
345 
GetName(const char * name)346 static AString GetName(const char *name)
347 {
348   char res[kNameSize + 1];
349   memcpy(res, name, kNameSize);
350   res[kNameSize] = 0;
351   return (AString)res;
352 }
353 
354 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)355 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
356 {
357   COM_TRY_BEGIN
358   CPropVariant prop;
359   const CSection &item = _sections[index];
360   switch (propID)
361   {
362     case kpidPath:
363     {
364       AString s (GetName(_segments[item.SegmentIndex].Name));
365       if (!item.IsDummy)
366         s += GetName(item.Name);
367       prop = MultiByteToUnicodeString(s);
368       break;
369     }
370     case kpidSize:  /* prop = (UInt64)item.VSize; break; */
371     case kpidPackSize:  prop = (UInt64)item.GetPackSize(); break;
372     case kpidCharacts:
373       if (!item.IsDummy)
374       {
375         AString res (TypeToString(g_SectTypes, ARRAY_SIZE(g_SectTypes), item.Flags & SECT_TYPE_MASK));
376         AString s (FlagsToString(g_Flags, ARRAY_SIZE(g_Flags), item.Flags & SECT_ATTR_MASK));
377         if (!s.IsEmpty())
378         {
379           res.Add_Space();
380           res += s;
381         }
382         prop = res;
383       }
384       break;
385     case kpidOffset:  prop = item.Pa; break;
386     case kpidVa:  prop = item.Va; break;
387   }
388   prop.Detach(value);
389   return S_OK;
390   COM_TRY_END
391 }
392 
393 
Open2(ISequentialInStream * stream)394 HRESULT CHandler::Open2(ISequentialInStream *stream)
395 {
396   const UInt32 kStartHeaderSize = 7 * 4;
397 
398   Byte header[kStartHeaderSize];
399   RINOK(ReadStream_FALSE(stream, header, kStartHeaderSize));
400   bool be, mode64;
401   switch (GetUi32(header))
402   {
403     case 0xCEFAEDFE:  be = true; mode64 = false; break;
404     case 0xCFFAEDFE:  be = true; mode64 = true; break;
405     case 0xFEEDFACE:  be = false; mode64 = false; break;
406     case 0xFEEDFACF:  be = false; mode64 = true; break;
407     default: return S_FALSE;
408   }
409 
410   _mode64 = mode64;
411   _be = be;
412 
413   UInt32 numCommands = Get32(header + 0x10, be);
414   UInt32 commandsSize = Get32(header + 0x14, be);
415 
416   if (numCommands == 0)
417     return S_FALSE;
418 
419   if (commandsSize > (1 << 24) ||
420       numCommands > (1 << 21) ||
421       numCommands * 8 > commandsSize)
422     return S_FALSE;
423 
424   _cpuType = Get32(header + 4, be);
425   _cpuSubType = Get32(header + 8, be);
426   _type = Get32(header + 0xC, be);
427   _flags = Get32(header + 0x18, be);
428 
429   /*
430   // Probably the sections are in first commands. So we can reduce the number of commands.
431   bool reduceCommands = false;
432   const UInt32 kNumReduceCommands = 16;
433   if (numCommands > kNumReduceCommands)
434   {
435     reduceCommands = true;
436     numCommands = kNumReduceCommands;
437   }
438   */
439 
440   UInt32 startHeaderSize = kStartHeaderSize;
441   if (mode64)
442     startHeaderSize += 4;
443   _headersSize = startHeaderSize + commandsSize;
444   _totalSize = _headersSize;
445   CByteArr buffer(_headersSize);
446   RINOK(ReadStream_FALSE(stream, buffer + kStartHeaderSize, _headersSize - kStartHeaderSize));
447   const Byte *buf = buffer + startHeaderSize;
448   size_t size = _headersSize - startHeaderSize;
449   for (UInt32 cmdIndex = 0; cmdIndex < numCommands; cmdIndex++)
450   {
451     if (size < 8)
452       return S_FALSE;
453     UInt32 cmd = Get32(buf, be);
454     UInt32 cmdSize = Get32(buf + 4, be);
455     if (cmdSize < 8)
456       return S_FALSE;
457     if (size < cmdSize)
458       return S_FALSE;
459     if (cmd == CMD_SEGMENT_32 || cmd == CMD_SEGMENT_64)
460     {
461       UInt32 offs = (cmd == CMD_SEGMENT_64) ? 0x48 : 0x38;
462       if (cmdSize < offs)
463         break;
464 
465       UInt64 vmAddr, vmSize, phAddr, phSize;
466 
467       {
468         if (cmd == CMD_SEGMENT_64)
469         {
470           vmAddr = Get64(buf + 0x18, be);
471           vmSize = Get64(buf + 0x20, be);
472           phAddr = Get64(buf + 0x28, be);
473           phSize = Get64(buf + 0x30, be);
474         }
475         else
476         {
477           vmAddr = Get32(buf + 0x18, be);
478           vmSize = Get32(buf + 0x1C, be);
479           phAddr = Get32(buf + 0x20, be);
480           phSize = Get32(buf + 0x24, be);
481         }
482         {
483           UInt64 totalSize = phAddr + phSize;
484           if (totalSize < phAddr)
485             return S_FALSE;
486           if (_totalSize < totalSize)
487             _totalSize = totalSize;
488         }
489       }
490 
491       CSegment seg;
492       memcpy(seg.Name, buf + 8, kNameSize);
493       _segments.Add(seg);
494 
495       UInt32 numSections = Get32(buf + offs - 8, be);
496       if (numSections > (1 << 8))
497         return S_FALSE;
498 
499       if (numSections == 0)
500       {
501         CSection &sect = _sections.AddNew();
502         sect.IsDummy = true;
503         sect.SegmentIndex = _segments.Size() - 1;
504         sect.Va = vmAddr;
505         sect.PSize = phSize;
506         sect.VSize = vmSize;
507         sect.Pa = phAddr;
508         sect.Flags = 0;
509       }
510       else do
511       {
512         UInt32 headSize = (cmd == CMD_SEGMENT_64) ? 0x50 : 0x44;
513         const Byte *p = buf + offs;
514         if (cmdSize - offs < headSize)
515           break;
516         CSection &sect = _sections.AddNew();
517         unsigned f32Offset;
518         if (cmd == CMD_SEGMENT_64)
519         {
520           sect.Va    = Get64(p + 0x20, be);
521           sect.VSize = Get64(p + 0x28, be);
522           f32Offset = 0x30;
523         }
524         else
525         {
526           sect.Va    = Get32(p + 0x20, be);
527           sect.VSize = Get32(p + 0x24, be);
528           f32Offset = 0x28;
529         }
530         sect.Pa    = Get32(p + f32Offset, be);
531         sect.Flags = Get32(p + f32Offset + 10, be);
532         if (sect.Flags == SECT_ATTR_ZEROFILL)
533           sect.PSize = 0;
534         else
535           sect.PSize = sect.VSize;
536         memcpy(sect.Name, p, kNameSize);
537         memcpy(sect.SegName, p + kNameSize, kNameSize);
538         sect.SegmentIndex = _segments.Size() - 1;
539         offs += headSize;
540       }
541       while (--numSections);
542 
543       if (offs != cmdSize)
544         return S_FALSE;
545     }
546     buf += cmdSize;
547     size -= cmdSize;
548   }
549   // return (reduceCommands || (size == 0)) ? S_OK : S_FALSE;
550   if (size != 0)
551     return S_FALSE;
552 
553   return S_OK;
554 }
555 
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback *)556 STDMETHODIMP CHandler::Open(IInStream *inStream,
557     const UInt64 * /* maxCheckStartPosition */,
558     IArchiveOpenCallback * /* openArchiveCallback */)
559 {
560   COM_TRY_BEGIN
561   Close();
562   RINOK(Open2(inStream));
563   if (!_allowTail)
564   {
565     UInt64 fileSize;
566     RINOK(inStream->Seek(0, STREAM_SEEK_END, &fileSize));
567     if (fileSize > _totalSize)
568       return S_FALSE;
569   }
570   _inStream = inStream;
571   return S_OK;
572   COM_TRY_END
573 }
574 
Close()575 STDMETHODIMP CHandler::Close()
576 {
577   _totalSize = 0;
578   _inStream.Release();
579   _sections.Clear();
580   _segments.Clear();
581   return S_OK;
582 }
583 
GetNumberOfItems(UInt32 * numItems)584 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
585 {
586   *numItems = _sections.Size();
587   return S_OK;
588 }
589 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)590 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
591     Int32 testMode, IArchiveExtractCallback *extractCallback)
592 {
593   COM_TRY_BEGIN
594   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
595   if (allFilesMode)
596     numItems = _sections.Size();
597   if (numItems == 0)
598     return S_OK;
599   UInt64 totalSize = 0;
600   UInt32 i;
601   for (i = 0; i < numItems; i++)
602     totalSize += _sections[allFilesMode ? i : indices[i]].GetPackSize();
603   extractCallback->SetTotal(totalSize);
604 
605   UInt64 currentTotalSize = 0;
606   UInt64 currentItemSize;
607 
608   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
609   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
610 
611   CLocalProgress *lps = new CLocalProgress;
612   CMyComPtr<ICompressProgressInfo> progress = lps;
613   lps->Init(extractCallback, false);
614 
615   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
616   CMyComPtr<ISequentialInStream> inStream(streamSpec);
617   streamSpec->SetStream(_inStream);
618 
619   for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
620   {
621     lps->InSize = lps->OutSize = currentTotalSize;
622     RINOK(lps->SetCur());
623     Int32 askMode = testMode ?
624         NExtract::NAskMode::kTest :
625         NExtract::NAskMode::kExtract;
626     UInt32 index = allFilesMode ? i : indices[i];
627     const CSection &item = _sections[index];
628     currentItemSize = item.GetPackSize();
629 
630     CMyComPtr<ISequentialOutStream> outStream;
631     RINOK(extractCallback->GetStream(index, &outStream, askMode));
632     if (!testMode && !outStream)
633       continue;
634 
635     RINOK(extractCallback->PrepareOperation(askMode));
636     RINOK(_inStream->Seek(item.Pa, STREAM_SEEK_SET, NULL));
637     streamSpec->Init(currentItemSize);
638     RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
639     outStream.Release();
640     RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == currentItemSize ?
641         NExtract::NOperationResult::kOK:
642         NExtract::NOperationResult::kDataError));
643   }
644   return S_OK;
645   COM_TRY_END
646 }
647 
AllowTail(Int32 allowTail)648 STDMETHODIMP CHandler::AllowTail(Int32 allowTail)
649 {
650   _allowTail = IntToBool(allowTail);
651   return S_OK;
652 }
653 
654 static const Byte k_Signature[] = {
655   4, 0xCE, 0xFA, 0xED, 0xFE,
656   4, 0xCF, 0xFA, 0xED, 0xFE,
657   4, 0xFE, 0xED, 0xFA, 0xCE,
658   4, 0xFE, 0xED, 0xFA, 0xCF };
659 
660 REGISTER_ARC_I(
661   "MachO", "macho", 0, 0xDF,
662   k_Signature,
663   0,
664   NArcInfoFlags::kMultiSignature |
665   NArcInfoFlags::kPreArc,
666   NULL)
667 
668 }}
669