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