1 // ElfHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 #include "../../Common/IntToString.h"
9 #include "../../Common/MyBuffer.h"
10 
11 #include "../../Windows/PropVariantUtils.h"
12 
13 #include "../Common/LimitedStreams.h"
14 #include "../Common/ProgressUtils.h"
15 #include "../Common/RegisterArc.h"
16 #include "../Common/StreamUtils.h"
17 
18 #include "../Compress/CopyCoder.h"
19 
20 using namespace NWindows;
21 
Get16(const Byte * p,bool be)22 static UInt16 Get16(const Byte *p, bool be) { if (be) return GetBe16(p); return GetUi16(p); }
Get32(const Byte * p,bool be)23 static UInt32 Get32(const Byte *p, bool be) { if (be) return GetBe32(p); return GetUi32(p); }
Get64(const Byte * p,bool be)24 static UInt64 Get64(const Byte *p, bool be) { if (be) return GetBe64(p); return GetUi64(p); }
25 
26 #define G16(offs, v) v = Get16(p + (offs), be)
27 #define G32(offs, v) v = Get32(p + (offs), be)
28 #define G64(offs, v) v = Get64(p + (offs), be)
29 
30 namespace NArchive {
31 namespace NElf {
32 
33 /*
34    ELF Structure for most files (real order can be different):
35    Header
36    Program (segment) header table (used at runtime)
37      Segment1 (Section ... Section)
38      Segment2
39      ...
40      SegmentN
41    Section header table (the data for linking and relocation)
42 */
43 
44 #define ELF_CLASS_32 1
45 #define ELF_CLASS_64 2
46 
47 #define ELF_DATA_2LSB 1
48 #define ELF_DATA_2MSB 2
49 
50 static const UInt32 kHeaderSize32 = 0x34;
51 static const UInt32 kHeaderSize64 = 0x40;
52 
53 static const UInt32 kSegmentSize32 = 0x20;
54 static const UInt32 kSegmentSize64 = 0x38;
55 
56 static const UInt32 kSectionSize32 = 0x28;
57 static const UInt32 kSectionSize64 = 0x40;
58 
59 struct CHeader
60 {
61   bool Mode64;
62   bool Be;
63   Byte Os;
64   Byte AbiVer;
65 
66   UInt16 Type;
67   UInt16 Machine;
68   // UInt32 Version;
69 
70   // UInt64 EntryVa;
71   UInt64 ProgOffset;
72   UInt64 SectOffset;
73   UInt32 Flags;
74   UInt16 HeaderSize;
75   UInt16 SegmentEntrySize;
76   UInt16 NumSegments;
77   UInt16 SectionEntrySize;
78   UInt16 NumSections;
79   UInt16 NamesSectIndex;
80 
81   bool Parse(const Byte *buf);
82 
GetHeadersSizeNArchive::NElf::CHeader83   UInt64 GetHeadersSize() const { return (UInt64)HeaderSize +
84       (UInt32)NumSegments * SegmentEntrySize +
85       (UInt32)NumSections * SectionEntrySize; }
86 };
87 
Parse(const Byte * p)88 bool CHeader::Parse(const Byte *p)
89 {
90   switch (p[4])
91   {
92     case ELF_CLASS_32: Mode64 = false; break;
93     case ELF_CLASS_64: Mode64 = true; break;
94     default: return false;
95   }
96   bool be;
97   switch (p[5])
98   {
99     case ELF_DATA_2LSB: be = false; break;
100     case ELF_DATA_2MSB: be = true; break;
101     default: return false;
102   }
103   Be = be;
104   if (p[6] != 1) // Version
105     return false;
106   Os = p[7];
107   AbiVer = p[8];
108   for (int i = 9; i < 16; i++)
109     if (p[i] != 0)
110       return false;
111 
112   G16(0x10, Type);
113   G16(0x12, Machine);
114   if (Get32(p + 0x14, be) != 1) // Version
115     return false;
116 
117   if (Mode64)
118   {
119     // G64(0x18, EntryVa);
120     G64(0x20, ProgOffset);
121     G64(0x28, SectOffset);
122     p += 0x30;
123   }
124   else
125   {
126     // G32(0x18, EntryVa);
127     G32(0x1C, ProgOffset);
128     G32(0x20, SectOffset);
129     p += 0x24;
130   }
131 
132   G32(0, Flags);
133   G16(4, HeaderSize);
134   if (HeaderSize != (Mode64 ? kHeaderSize64 : kHeaderSize32))
135     return false;
136 
137   G16(6, SegmentEntrySize);
138   G16(8, NumSegments);
139   G16(10, SectionEntrySize);
140   G16(12, NumSections);
141   G16(14, NamesSectIndex);
142 
143   if (ProgOffset < HeaderSize && (ProgOffset != 0 || NumSegments != 0)) return false;
144   if (SectOffset < HeaderSize && (SectOffset != 0 || NumSections != 0)) return false;
145 
146   if (SegmentEntrySize == 0) { if (NumSegments != 0) return false; }
147   else if (SegmentEntrySize != (Mode64 ? kSegmentSize64 : kSegmentSize32)) return false;
148 
149   if (SectionEntrySize == 0) { if (NumSections != 0) return false; }
150   else if (SectionEntrySize != (Mode64 ? kSectionSize64 : kSectionSize32)) return false;
151 
152   return true;
153 }
154 
155 // The program header table itself.
156 
157 #define PT_PHDR 6
158 
159 static const char * const g_SegnmentTypes[] =
160 {
161     "Unused"
162   , "Loadable segment"
163   , "Dynamic linking tables"
164   , "Program interpreter path name"
165   , "Note section"
166   , "SHLIB"
167   , "Program header table"
168   , "TLS"
169 };
170 
171 static const char * const g_SegmentFlags[] =
172 {
173     "Execute"
174   , "Write"
175   , "Read"
176 };
177 
178 struct CSegment
179 {
180   UInt32 Type;
181   UInt32 Flags;
182   UInt64 Offset;
183   UInt64 Va;
184   // UInt64 Pa;
185   UInt64 Size;
186   UInt64 VSize;
187   UInt64 Align;
188 
UpdateTotalSizeNArchive::NElf::CSegment189   void UpdateTotalSize(UInt64 &totalSize)
190   {
191     UInt64 t = Offset + Size;
192     if (totalSize < t)
193       totalSize = t;
194   }
195   void Parse(const Byte *p, bool mode64, bool be);
196 };
197 
Parse(const Byte * p,bool mode64,bool be)198 void CSegment::Parse(const Byte *p, bool mode64, bool be)
199 {
200   G32(0, Type);
201   if (mode64)
202   {
203     G32(4, Flags);
204     G64(8, Offset);
205     G64(0x10, Va);
206     // G64(0x18, Pa);
207     G64(0x20, Size);
208     G64(0x28, VSize);
209     G64(0x30, Align);
210   }
211   else
212   {
213     G32(4, Offset);
214     G32(8, Va);
215     // G32(0x0C, Pa);
216     G32(0x10, Size);
217     G32(0x14, VSize);
218     G32(0x18, Flags);
219     G32(0x1C, Align);
220   }
221 }
222 
223 // Section_index = 0 means NO section
224 
225 #define SHN_UNDEF 0
226 
227 // Section types
228 
229 #define SHT_NULL           0
230 #define SHT_PROGBITS       1
231 #define SHT_SYMTAB         2
232 #define SHT_STRTAB         3
233 #define SHT_RELA           4
234 #define SHT_HASH           5
235 #define SHT_DYNAMIC        6
236 #define SHT_NOTE           7
237 #define SHT_NOBITS         8
238 #define SHT_REL            9
239 #define SHT_SHLIB         10
240 #define SHT_DYNSYM        11
241 #define SHT_UNKNOWN12     12
242 #define SHT_UNKNOWN13     13
243 #define SHT_INIT_ARRAY    14
244 #define SHT_FINI_ARRAY    15
245 #define SHT_PREINIT_ARRAY 16
246 #define SHT_GROUP         17
247 #define SHT_SYMTAB_SHNDX  18
248 
249 
250 static const CUInt32PCharPair g_SectTypes[] =
251 {
252   { 0, "NULL" },
253   { 1, "PROGBITS" },
254   { 2, "SYMTAB" },
255   { 3, "STRTAB" },
256   { 4, "RELA" },
257   { 5, "HASH" },
258   { 6, "DYNAMIC" },
259   { 7, "NOTE" },
260   { 8, "NOBITS" },
261   { 9, "REL" },
262   { 10, "SHLIB" },
263   { 11, "DYNSYM" },
264   { 12, "UNKNOWN12" },
265   { 13, "UNKNOWN13" },
266   { 14, "INIT_ARRAY" },
267   { 15, "FINI_ARRAY" },
268   { 16, "PREINIT_ARRAY" },
269   { 17, "GROUP" },
270   { 18, "SYMTAB_SHNDX" },
271   { 0x6ffffff5, "GNU_ATTRIBUTES" },
272   { 0x6ffffff6, "GNU_HASH" },
273   { 0x6ffffffd, "GNU_verdef" },
274   { 0x6ffffffe, "GNU_verneed" },
275   { 0x6fffffff, "GNU_versym" },
276   // { 0x70000001, "X86_64_UNWIND" },
277   { 0x70000001, "ARM_EXIDX" },
278   { 0x70000002, "ARM_PREEMPTMAP" },
279   { 0x70000003, "ARM_ATTRIBUTES" },
280   { 0x70000004, "ARM_DEBUGOVERLAY" },
281   { 0x70000005, "ARM_OVERLAYSECTION" }
282 };
283 
284 static const CUInt32PCharPair g_SectionFlags[] =
285 {
286   { 0, "WRITE" },
287   { 1, "ALLOC" },
288   { 2, "EXECINSTR" },
289 
290   { 4, "MERGE" },
291   { 5, "STRINGS" },
292   { 6, "INFO_LINK" },
293   { 7, "LINK_ORDER" },
294   { 8, "OS_NONCONFORMING" },
295   { 9, "GROUP" },
296   { 10, "TLS" },
297   { 11, "CP_SECTION" },
298   { 12, "DP_SECTION" },
299   { 13, "XCORE_SHF_CP_SECTION" },
300   { 28, "64_LARGE" },
301 };
302 
303 struct CSection
304 {
305   UInt32 Name;
306   UInt32 Type;
307   UInt64 Flags;
308   UInt64 Va;
309   UInt64 Offset;
310   UInt64 VSize;
311   UInt32 Link;
312   UInt32 Info;
313   UInt64 AddrAlign;
314   UInt64 EntSize;
315 
GetSizeNArchive::NElf::CSection316   UInt64 GetSize() const { return Type == SHT_NOBITS ? 0 : VSize; }
317 
UpdateTotalSizeNArchive::NElf::CSection318   void UpdateTotalSize(UInt64 &totalSize)
319   {
320     UInt64 t = Offset + GetSize();
321     if (totalSize < t)
322       totalSize = t;
323   }
324   bool Parse(const Byte *p, bool mode64, bool be);
325 };
326 
Parse(const Byte * p,bool mode64,bool be)327 bool CSection::Parse(const Byte *p, bool mode64, bool be)
328 {
329   G32(0, Name);
330   G32(4, Type);
331   if (mode64)
332   {
333     G64(0x08, Flags);
334     G64(0x10, Va);
335     G64(0x18, Offset);
336     G64(0x20, VSize);
337     G32(0x28, Link);
338     G32(0x2C, Info);
339     G64(0x30, AddrAlign);
340     G64(0x38, EntSize);
341   }
342   else
343   {
344     G32(0x08, Flags);
345     G32(0x0C, Va);
346     G32(0x10, Offset);
347     G32(0x14, VSize);
348     G32(0x18, Link);
349     G32(0x1C, Info);
350     G32(0x20, AddrAlign);
351     G32(0x24, EntSize);
352   }
353   if (EntSize >= ((UInt32)1 << 31))
354     return false;
355   if (EntSize >= ((UInt32)1 << 10) &&
356       EntSize >= VSize &&
357       VSize != 0)
358     return false;
359   return true;
360 }
361 
362 
363 static const char * const g_Machines[] =
364 {
365     "None"
366   , "AT&T WE 32100"
367   , "SPARC"
368   , "Intel 386"
369   , "Motorola 68000"
370   , "Motorola 88000"
371   , "Intel 486"
372   , "Intel i860"
373   , "MIPS"
374   , "IBM S/370"
375   , "MIPS RS3000 LE"
376   , "RS6000"
377   , NULL
378   , NULL
379   , NULL
380   , "PA-RISC"
381   , "nCUBE"
382   , "Fujitsu VPP500"
383   , "SPARC 32+"
384   , "Intel i960"
385   , "PowerPC"
386   , "PowerPC 64-bit"
387   , "IBM S/390"
388   , "SPU"
389   , NULL
390   , NULL
391   , NULL
392   , NULL
393   , NULL
394   , NULL
395   , NULL
396   , NULL
397   , NULL
398   , NULL
399   , NULL
400   , NULL
401   , "NEX v800"
402   , "Fujitsu FR20"
403   , "TRW RH-32"
404   , "Motorola RCE"
405   , "ARM"
406   , "Alpha"
407   , "Hitachi SH"
408   , "SPARC-V9"
409   , "Siemens Tricore"
410   , "ARC"
411   , "H8/300"
412   , "H8/300H"
413   , "H8S"
414   , "H8/500"
415   , "IA-64"
416   , "Stanford MIPS-X"
417   , "Motorola ColdFire"
418   , "M68HC12"
419   , "Fujitsu MMA"
420   , "Siemens PCP"
421   , "Sony nCPU"
422   , "Denso NDR1"
423   , "Motorola StarCore"
424   , "Toyota ME16"
425   , "ST100"
426   , "Advanced Logic TinyJ"
427   , "AMD64"
428   , "Sony DSP"
429   , NULL
430   , NULL
431   , "Siemens FX66"
432   , "ST9+"
433   , "ST7"
434   , "MC68HC16"
435   , "MC68HC11"
436   , "MC68HC08"
437   , "MC68HC05"
438   , "Silicon Graphics SVx"
439   , "ST19"
440   , "Digital VAX"
441   , "Axis CRIS"
442   , "Infineon JAVELIN"
443   , "Element 14 FirePath"
444   , "LSI ZSP"
445   , "MMIX"
446   , "HUANY"
447   , "SiTera Prism"
448   , "Atmel AVR"
449   , "Fujitsu FR30"
450   , "Mitsubishi D10V"
451   , "Mitsubishi D30V"
452   , "NEC v850"
453   , "Mitsubishi M32R"
454   , "Matsushita MN10300"
455   , "Matsushita MN10200"
456   , "picoJava"
457   , "OpenRISC"
458   , "ARC Tangent-A5"
459   , "Tensilica Xtensa"
460   , "Alphamosaic VideoCore"
461   , "Thompson MM GPP"
462   , "National Semiconductor 32K"
463   , "Tenor Network TPC"
464   , "Trebia SNP 1000"
465   , "ST200"
466   , "Ubicom IP2xxx"
467   , "MAX"
468   , "NS CompactRISC"
469   , "Fujitsu F2MC16"
470   , "TI msp430"
471   , "Blackfin (DSP)"
472   , "SE S1C33"
473   , "Sharp embedded"
474   , "Arca RISC"
475   , "Unicore"
476   , "eXcess"
477   , "DXP"
478   , "Altera Nios II"
479   , "NS CRX"
480   , "Motorola XGATE"
481   , "Infineon C16x/XC16x"
482   , "Renesas M16C"
483   , "Microchip Technology dsPIC30F"
484   , "Freescale CE"
485   , "Renesas M32C"
486   , NULL
487   , NULL
488   , NULL
489   , NULL
490   , NULL
491   , NULL
492   , NULL
493   , NULL
494   , NULL
495   , NULL
496   , "Altium TSK3000"
497   , "Freescale RS08"
498   , "Analog Devices SHARC"
499   , "Cyan Technology eCOG2"
500   , "Sunplus S+core7 RISC"
501   , "NJR 24-bit DSP"
502   , "Broadcom VideoCore III"
503   , "Lattice FPGA"
504   , "SE C17"
505   , "TI TMS320C6000"
506   , "TI TMS320C2000"
507   , "TI TMS320C55x"
508   , NULL
509   , NULL
510   , NULL
511   , NULL
512   , NULL
513   , NULL
514   , NULL
515   , NULL
516   , NULL
517   , NULL
518   , NULL
519   , NULL
520   , NULL
521   , NULL
522   , NULL
523   , NULL
524   , NULL
525   , "STM 64bit VLIW Data Signal"
526   , "Cypress M8C"
527   , "Renesas R32C"
528   , "NXP TriMedia"
529   , "Qualcomm Hexagon"
530   , "Intel 8051"
531   , "STMicroelectronics STxP7x"
532   , "Andes"
533   , "Cyan Technology eCOG1X"
534   , "Dallas Semiconductor MAXQ30"
535   , "NJR 16-bit DSP"
536   , "M2000"
537   , "Cray NV2"
538   , "Renesas RX"
539   , "Imagination Technologies META"
540   , "MCST Elbrus"
541   , "Cyan Technology eCOG16"
542   , "National Semiconductor CR16"
543   , "Freescale ETPUnit"
544   , "Infineon SLE9X"
545   , "Intel L10M"
546   , "Intel K10M"
547   , NULL
548   , "ARM64"
549   , NULL
550   , "Atmel AVR32"
551   , "STM8"
552   , "Tilera TILE64"
553   , "Tilera TILEPro"
554   , "Xilinx MicroBlaze"
555   , "NVIDIA CUDA"
556   , "Tilera TILE-Gx"
557   , "CloudShield"
558   , "KIPO-KAIST Core-A 1st"
559   , "KIPO-KAIST Core-A 2nd"
560   , "Synopsys ARCompact V2"
561   , "Open8"
562   , "Renesas RL78"
563   , "Broadcom VideoCore V"
564   , "Renesas 78KOR"
565   , "Freescale 56800EX" // 200
566 };
567 
568 static const CUInt32PCharPair g_MachinePairs[] =
569 {
570   { 47787, "Xilinx MicroBlaze" }
571   // { 0x9026, "Alpha" }
572 };
573 
574 static const CUInt32PCharPair g_OS[] =
575 {
576   { 0, "None" },
577   { 1, "HP-UX" },
578   { 2, "NetBSD" },
579   { 3, "Linux" },
580   { 4, "Hurd" },
581 
582   { 6, "Solaris" },
583   { 7, "AIX" },
584   { 8, "IRIX" },
585   { 9, "FreeBSD" },
586   { 10, "TRU64" },
587   { 11, "Novell Modesto" },
588   { 12, "OpenBSD" },
589   { 13, "OpenVMS" },
590   { 14, "HP NSK" },
591   { 15, "AROS" },
592   { 16, "FenixOS" },
593   { 64, "Bare-metal TMS320C6000" },
594   { 65, "Linux TMS320C6000" },
595   { 97, "ARM" },
596   { 255, "Standalone" }
597 };
598 
599 #define k_Machine_MIPS 8
600 #define k_Machine_ARM 40
601 
602 /*
603 #define EF_ARM_ABIMASK        0xFF000000
604 #define EF_ARM_BE8            0x00800000
605 #define EF_ARM_GCCMASK        0x00400FFF
606 #define EF_ARM_ABI_FLOAT_SOFT 0x00000200
607 #define EF_ARM_ABI_FLOAT_HARD 0x00000400
608 */
609 
610 static const CUInt32PCharPair g_ARM_Flags[] =
611 {
612   { 1, "HasEntry" },
613   { 9, "SF" },
614   { 10, "HF" },
615   { 23, "BE8" }
616 };
617 
618 
619 static const CUInt32PCharPair g_MIPS_Flags[] =
620 {
621   { 0, "NOREORDER" },
622   { 1, "PIC" },
623   { 2, "CPIC" },
624   { 3, "XGOT" },
625   { 4, "64BIT_WHIRL" },
626   { 5, "ABI2" },
627   { 6, "ABI_ON32" },
628   { 10, "NAN2008" },
629   { 25, "MicroMIPS" },
630   { 26, "M16" },
631   { 27, "MDMX" }
632 };
633 
634 
635 #define ET_NONE 0
636 #define ET_REL  1
637 #define ET_EXEC 2
638 #define ET_DYN  3
639 #define ET_CORE 4
640 
641 static const char * const g_Types[] =
642 {
643     "None"
644   , "Relocatable file"
645   , "Executable file"
646   , "Shared object file"
647   , "Core file"
648 };
649 
650 
651 
652 
653 class CHandler:
654   public IInArchive,
655   public IArchiveAllowTail,
656   public CMyUnknownImp
657 {
658   CRecordVector<CSegment> _segments;
659   CRecordVector<CSection> _sections;
660   CByteBuffer _namesData;
661   CMyComPtr<IInStream> _inStream;
662   UInt64 _totalSize;
663   CHeader _header;
664   bool _headersError;
665   bool _allowTail;
666 
667   void GetSectionName(UInt32 index, NCOM::CPropVariant &prop, bool showNULL) const;
668   HRESULT Open2(IInStream *stream);
669 public:
670   MY_UNKNOWN_IMP2(IInArchive, IArchiveAllowTail)
671   INTERFACE_IInArchive(;)
672   STDMETHOD(AllowTail)(Int32 allowTail);
673 
CHandler()674   CHandler(): _allowTail(false) {}
675 };
676 
GetSectionName(UInt32 index,NCOM::CPropVariant & prop,bool showNULL) const677 void CHandler::GetSectionName(UInt32 index, NCOM::CPropVariant &prop, bool showNULL) const
678 {
679   if (index >= _sections.Size())
680     return;
681   const CSection &section = _sections[index];
682   UInt32 offset = section.Name;
683   if (index == SHN_UNDEF /* && section.Type == SHT_NULL && offset == 0 */)
684   {
685     if (showNULL)
686       prop = "NULL";
687     return;
688   }
689   const Byte *p = _namesData;
690   size_t size = _namesData.Size();
691   for (size_t i = offset; i < size; i++)
692     if (p[i] == 0)
693     {
694       prop = (const char *)(p + offset);
695       return;
696     }
697 }
698 
699 static const Byte kArcProps[] =
700 {
701   kpidCpu,
702   kpidBit64,
703   kpidBigEndian,
704   kpidHostOS,
705   kpidCharacts,
706   kpidHeadersSize
707 };
708 
709 enum
710 {
711   kpidLinkSection = kpidUserDefined,
712   kpidInfoSection
713 };
714 
715 static const CStatProp kProps[] =
716 {
717   { NULL, kpidPath, VT_BSTR },
718   { NULL, kpidSize, VT_UI8 },
719   { NULL, kpidVirtualSize, VT_UI8 },
720   { NULL, kpidOffset, VT_UI8 },
721   { NULL, kpidVa, VT_UI8 },
722   { NULL, kpidType, VT_BSTR },
723   { NULL, kpidCharacts, VT_BSTR }
724   , { "Link Section", kpidLinkSection, VT_BSTR}
725   , { "Info Section", kpidInfoSection, VT_BSTR}
726 };
727 
728 IMP_IInArchive_Props_WITH_NAME
729 IMP_IInArchive_ArcProps
730 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)731 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
732 {
733   COM_TRY_BEGIN
734   NCOM::CPropVariant prop;
735   switch (propID)
736   {
737     case kpidPhySize: prop = _totalSize; break;
738     case kpidHeadersSize: prop = _header.GetHeadersSize(); break;
739     case kpidBit64: if (_header.Mode64) prop = _header.Mode64; break;
740     case kpidBigEndian: if (_header.Be) prop = _header.Be; break;
741     case kpidShortComment:
742 
743     case kpidCpu:
744     {
745       AString s;
746       if (_header.Machine < ARRAY_SIZE(g_Machines))
747       {
748         const char *name = g_Machines[_header.Machine];
749         if (name)
750           s = name;
751       }
752       if (s.IsEmpty())
753         s = TypePairToString(g_MachinePairs, ARRAY_SIZE(g_MachinePairs), _header.Machine);
754       UInt32 flags = _header.Flags;
755       if (flags != 0)
756       {
757         s.Add_Space();
758         if (_header.Machine == k_Machine_ARM)
759         {
760           s += FlagsToString(g_ARM_Flags, ARRAY_SIZE(g_ARM_Flags), flags & (((UInt32)1 << 24) - 1));
761           s += " ABI:";
762           s.Add_UInt32(flags >> 24);
763         }
764         else if (_header.Machine == k_Machine_MIPS)
765         {
766           UInt32 ver = flags >> 28;
767           s += "v";
768           s.Add_UInt32(ver);
769           flags &= (((UInt32)1 << 28) - 1);
770 
771           UInt32 abi = (flags >> 12) & 7;
772           if (abi != 0)
773           {
774             s += " ABI:";
775             s.Add_UInt32(abi);
776           }
777           flags &= ~((UInt32)7 << 12);
778 
779           s.Add_Space();
780           s += FlagsToString(g_MIPS_Flags, ARRAY_SIZE(g_MIPS_Flags), flags);
781         }
782         else
783         {
784           char sz[16];
785           ConvertUInt32ToHex(flags, sz);
786           s += sz;
787         }
788       }
789       prop = s;
790       break;
791     }
792 
793     case kpidHostOS: PAIR_TO_PROP(g_OS, _header.Os, prop); break;
794     case kpidCharacts: TYPE_TO_PROP(g_Types, _header.Type, prop); break;
795     case kpidExtension:
796     {
797       const char *s = NULL;
798       if (_header.Type == ET_DYN)
799         s = "so";
800       else if (_header.Type == ET_REL)
801         s = "o";
802       if (s)
803         prop = s;
804       break;
805     }
806     // case kpidIsSelfExe: prop = (_header.Type != ET_DYN)  && (_header.Type == ET_REL); break;
807     case kpidErrorFlags:
808     {
809       UInt32 flags = 0;
810       if (_headersError) flags |= kpv_ErrorFlags_HeadersError;
811       if (flags != 0)
812         prop = flags;
813       break;
814     }
815   }
816   prop.Detach(value);
817   return S_OK;
818   COM_TRY_END
819 }
820 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)821 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
822 {
823   COM_TRY_BEGIN
824   NCOM::CPropVariant prop;
825   if (index < _segments.Size())
826   {
827     const CSegment &item = _segments[index];
828     switch (propID)
829     {
830       case kpidPath:
831       {
832         char sz[16];
833         ConvertUInt32ToString(index, sz);
834         prop = sz;
835         break;
836       }
837       case kpidOffset: prop = item.Offset; break;
838       case kpidVa: prop = item.Va; break;
839       case kpidSize:
840       case kpidPackSize: prop = (UInt64)item.Size; break;
841       case kpidVirtualSize: prop = (UInt64)item.VSize; break;
842       case kpidType: TYPE_TO_PROP(g_SegnmentTypes, item.Type, prop); break;
843       case kpidCharacts: FLAGS_TO_PROP(g_SegmentFlags, item.Flags, prop); break;
844 
845     }
846   }
847   else
848   {
849     index -= _segments.Size();
850     const CSection &item = _sections[index];
851     switch (propID)
852     {
853       case kpidPath: GetSectionName(index, prop, true); break;
854       case kpidOffset: prop = item.Offset; break;
855       case kpidVa: prop = item.Va; break;
856       case kpidSize:
857       case kpidPackSize: prop = (UInt64)(item.Type == SHT_NOBITS ? 0 : item.VSize); break;
858       case kpidVirtualSize: prop = item.GetSize(); break;
859       case kpidType: PAIR_TO_PROP(g_SectTypes, item.Type, prop); break;
860       case kpidCharacts: FLAGS_TO_PROP(g_SectionFlags, (UInt32)item.Flags, prop); break;
861       case kpidLinkSection: GetSectionName(item.Link, prop, false); break;
862       case kpidInfoSection: GetSectionName(item.Info, prop, false); break;
863     }
864   }
865   prop.Detach(value);
866   return S_OK;
867   COM_TRY_END
868 }
869 
Open2(IInStream * stream)870 HRESULT CHandler::Open2(IInStream *stream)
871 {
872   const UInt32 kStartSize = kHeaderSize64;
873   Byte h[kStartSize];
874   RINOK(ReadStream_FALSE(stream, h, kStartSize));
875   if (h[0] != 0x7F || h[1] != 'E' || h[2] != 'L' || h[3] != 'F')
876     return S_FALSE;
877   if (!_header.Parse(h))
878     return S_FALSE;
879 
880   _totalSize = _header.HeaderSize;
881 
882   bool addSegments = false;
883   bool addSections = false;
884 
885   if (_header.NumSections > 1)
886     addSections = true;
887   else
888     addSegments = true;
889 
890   if (_header.NumSegments != 0)
891   {
892     if (_header.ProgOffset > (UInt64)1 << 60) return S_FALSE;
893     RINOK(stream->Seek(_header.ProgOffset, STREAM_SEEK_SET, NULL));
894     size_t size = (size_t)_header.SegmentEntrySize * _header.NumSegments;
895 
896     CByteArr buf(size);
897 
898     RINOK(ReadStream_FALSE(stream, buf, size));
899 
900     UInt64 total = _header.ProgOffset + size;
901     if (_totalSize < total)
902       _totalSize = total;
903 
904     const Byte *p = buf;
905 
906     if (addSegments)
907       _segments.ClearAndReserve(_header.NumSegments);
908     for (unsigned i = 0; i < _header.NumSegments; i++, p += _header.SegmentEntrySize)
909     {
910       CSegment seg;
911       seg.Parse(p, _header.Mode64, _header.Be);
912       seg.UpdateTotalSize(_totalSize);
913       if (addSegments)
914         if (seg.Type != PT_PHDR)
915           _segments.AddInReserved(seg);
916     }
917   }
918 
919   if (_header.NumSections != 0)
920   {
921     if (_header.SectOffset > (UInt64)1 << 60) return S_FALSE;
922     RINOK(stream->Seek(_header.SectOffset, STREAM_SEEK_SET, NULL));
923     size_t size = (size_t)_header.SectionEntrySize * _header.NumSections;
924 
925     CByteArr buf(size);
926 
927     RINOK(ReadStream_FALSE(stream, buf, size));
928 
929     UInt64 total = _header.SectOffset + size;
930     if (_totalSize < total)
931       _totalSize = total;
932 
933     const Byte *p = buf;
934 
935     if (addSections)
936       _sections.ClearAndReserve(_header.NumSections);
937     for (unsigned i = 0; i < _header.NumSections; i++, p += _header.SectionEntrySize)
938     {
939       CSection sect;
940       if (!sect.Parse(p, _header.Mode64, _header.Be))
941       {
942         _headersError = true;
943         return S_FALSE;
944       }
945       sect.UpdateTotalSize(_totalSize);
946       if (addSections)
947         _sections.AddInReserved(sect);
948     }
949   }
950 
951   if (addSections)
952   {
953     if (_header.NamesSectIndex < _sections.Size())
954     {
955       const CSection &sect = _sections[_header.NamesSectIndex];
956       UInt64 size = sect.GetSize();
957       if (size != 0
958         && size < ((UInt64)1 << 31)
959         && (Int64)sect.Offset >= 0)
960       {
961         _namesData.Alloc((size_t)size);
962         RINOK(stream->Seek(sect.Offset, STREAM_SEEK_SET, NULL));
963         RINOK(ReadStream_FALSE(stream, _namesData, (size_t)size));
964       }
965     }
966 
967     /*
968     // we will not delete NULL sections, since we have links to section via indexes
969     for (int i = _sections.Size() - 1; i >= 0; i--)
970       if (_sections[i].Type == SHT_NULL)
971         _items.Delete(i);
972     */
973   }
974 
975   if (!_allowTail)
976   {
977     UInt64 fileSize;
978     RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));
979     if (fileSize > _totalSize)
980       return S_FALSE;
981   }
982 
983   return S_OK;
984 }
985 
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback *)986 STDMETHODIMP CHandler::Open(IInStream *inStream,
987     const UInt64 * /* maxCheckStartPosition */,
988     IArchiveOpenCallback * /* openArchiveCallback */)
989 {
990   COM_TRY_BEGIN
991   Close();
992   RINOK(Open2(inStream));
993   _inStream = inStream;
994   return S_OK;
995   COM_TRY_END
996 }
997 
Close()998 STDMETHODIMP CHandler::Close()
999 {
1000   _totalSize = 0;
1001   _headersError = false;
1002 
1003   _inStream.Release();
1004   _segments.Clear();
1005   _sections.Clear();
1006   _namesData.Free();
1007   return S_OK;
1008 }
1009 
GetNumberOfItems(UInt32 * numItems)1010 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
1011 {
1012   *numItems = _segments.Size() + _sections.Size();
1013   return S_OK;
1014 }
1015 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)1016 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1017     Int32 testMode, IArchiveExtractCallback *extractCallback)
1018 {
1019   COM_TRY_BEGIN
1020   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1021   if (allFilesMode)
1022     numItems = _segments.Size() + _sections.Size();
1023   if (numItems == 0)
1024     return S_OK;
1025   UInt64 totalSize = 0;
1026   UInt32 i;
1027   for (i = 0; i < numItems; i++)
1028   {
1029     UInt32 index = allFilesMode ? i : indices[i];
1030     totalSize += (index < _segments.Size()) ?
1031         _segments[index].Size :
1032         _sections[index - _segments.Size()].GetSize();
1033   }
1034   extractCallback->SetTotal(totalSize);
1035 
1036   UInt64 currentTotalSize = 0;
1037   UInt64 currentItemSize;
1038 
1039   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
1040   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
1041 
1042   CLocalProgress *lps = new CLocalProgress;
1043   CMyComPtr<ICompressProgressInfo> progress = lps;
1044   lps->Init(extractCallback, false);
1045 
1046   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
1047   CMyComPtr<ISequentialInStream> inStream(streamSpec);
1048   streamSpec->SetStream(_inStream);
1049 
1050   for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
1051   {
1052     lps->InSize = lps->OutSize = currentTotalSize;
1053     RINOK(lps->SetCur());
1054     Int32 askMode = testMode ?
1055         NExtract::NAskMode::kTest :
1056         NExtract::NAskMode::kExtract;
1057     UInt32 index = allFilesMode ? i : indices[i];
1058     UInt64 offset;
1059     if (index < _segments.Size())
1060     {
1061       const CSegment &item = _segments[index];
1062       currentItemSize = item.Size;
1063       offset = item.Offset;
1064     }
1065     else
1066     {
1067       const CSection &item = _sections[index - _segments.Size()];
1068       currentItemSize = item.GetSize();
1069       offset = item.Offset;
1070     }
1071 
1072     CMyComPtr<ISequentialOutStream> outStream;
1073     RINOK(extractCallback->GetStream(index, &outStream, askMode));
1074     if (!testMode && !outStream)
1075       continue;
1076 
1077     RINOK(extractCallback->PrepareOperation(askMode));
1078     RINOK(_inStream->Seek(offset, STREAM_SEEK_SET, NULL));
1079     streamSpec->Init(currentItemSize);
1080     RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
1081     outStream.Release();
1082     RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == currentItemSize ?
1083         NExtract::NOperationResult::kOK:
1084         NExtract::NOperationResult::kDataError));
1085   }
1086   return S_OK;
1087   COM_TRY_END
1088 }
1089 
AllowTail(Int32 allowTail)1090 STDMETHODIMP CHandler::AllowTail(Int32 allowTail)
1091 {
1092   _allowTail = IntToBool(allowTail);
1093   return S_OK;
1094 }
1095 
1096 static const Byte k_Signature[] = { 0x7F, 'E', 'L', 'F' };
1097 
1098 REGISTER_ARC_I(
1099   "ELF", "elf", 0, 0xDE,
1100   k_Signature,
1101   0,
1102   NArcInfoFlags::kPreArc,
1103   NULL)
1104 
1105 }}
1106