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