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