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