Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / ElfHandler.cpp
1 // ElfHandler.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../C/CpuArch.h"\r
6 \r
7 #include "Common/Buffer.h"\r
8 #include "Common/ComTry.h"\r
9 #include "Common/IntToString.h"\r
10 \r
11 #include "Windows/PropVariantUtils.h"\r
12 \r
13 #include "../Common/LimitedStreams.h"\r
14 #include "../Common/ProgressUtils.h"\r
15 #include "../Common/RegisterArc.h"\r
16 #include "../Common/StreamUtils.h"\r
17 \r
18 #include "../Compress/CopyCoder.h"\r
19 \r
20 static UInt16 Get16(const Byte *p, int be) { if (be) return GetBe16(p); return GetUi16(p); }\r
21 static UInt32 Get32(const Byte *p, int be) { if (be) return GetBe32(p); return GetUi32(p); }\r
22 static UInt64 Get64(const Byte *p, int be) { if (be) return GetBe64(p); return GetUi64(p); }\r
23 \r
24 using namespace NWindows;\r
25 \r
26 namespace NArchive {\r
27 namespace NElf {\r
28 \r
29 #define ELF_CLASS_32 1\r
30 #define ELF_CLASS_64 2\r
31 \r
32 #define ELF_DATA_2LSB 1\r
33 #define ELF_DATA_2MSB 2\r
34 \r
35 #define NUM_SCAN_SECTIONS_MAX (1 << 6)\r
36 \r
37 struct CHeader\r
38 {\r
39   bool Mode64;\r
40   bool Be;\r
41   Byte Os;\r
42   Byte AbiVer;\r
43 \r
44   UInt16 Type;\r
45   UInt16 Machine;\r
46   // UInt32 Version;\r
47 \r
48   // UInt64 EntryVa;\r
49   UInt64 ProgOffset;\r
50   UInt64 SectOffset;\r
51   UInt32 Flags;\r
52   UInt16 ElfHeaderSize;\r
53   UInt16 SegmentEntrySize;\r
54   UInt16 NumSegments;\r
55   UInt16 SectEntrySize;\r
56   UInt16 NumSections;\r
57   // UInt16 SectNameStringTableIndex;\r
58 \r
59   bool Parse(const Byte *buf);\r
60 \r
61   bool CheckSegmentEntrySize() const\r
62   {\r
63     return (Mode64 && SegmentEntrySize == 0x38) || (!Mode64 && SegmentEntrySize == 0x20);\r
64   };\r
65 \r
66   UInt64 GetHeadersSize() const\r
67     { return ElfHeaderSize +\r
68       (UInt64)SegmentEntrySize * NumSegments +\r
69       (UInt64)SectEntrySize * NumSections; }\r
70     \r
71 };\r
72 \r
73 bool CHeader::Parse(const Byte *p)\r
74 {\r
75   switch(p[4])\r
76   {\r
77     case ELF_CLASS_32: Mode64 = false; break;\r
78     case ELF_CLASS_64: Mode64 = true; break;\r
79     default: return false;\r
80   }\r
81   bool be;\r
82   switch(p[5])\r
83   {\r
84     case ELF_DATA_2LSB: be = false; break;\r
85     case ELF_DATA_2MSB: be = true; break;\r
86     default: return false;\r
87   }\r
88   Be = be;\r
89   if (p[6] != 1) // Version\r
90     return false;\r
91   Os = p[7];\r
92   AbiVer = p[8];\r
93   for (int i = 9; i < 16; i++)\r
94     if (p[i] != 0)\r
95       return false;\r
96 \r
97   Type = Get16(p + 0x10, be);\r
98   Machine = Get16(p + 0x12, be);\r
99   if (Get32(p + 0x14, be) != 1) // Version\r
100     return false;\r
101 \r
102   if (Mode64)\r
103   {\r
104     // EntryVa = Get64(p + 0x18, be);\r
105     ProgOffset = Get64(p + 0x20, be);\r
106     SectOffset = Get64(p + 0x28, be);\r
107     p += 0x30;\r
108   }\r
109   else\r
110   {\r
111     // EntryVa = Get32(p + 0x18, be);\r
112     ProgOffset = Get32(p + 0x1C, be);\r
113     SectOffset = Get32(p + 0x20, be);\r
114     p += 0x24;\r
115   }\r
116 \r
117   Flags = Get32(p + 0, be);\r
118   ElfHeaderSize = Get16(p + 4, be);\r
119   SegmentEntrySize = Get16(p + 6, be);\r
120   NumSegments = Get16(p + 8, be);\r
121   SectEntrySize = Get16(p + 10, be);\r
122   NumSections = Get16(p + 12, be);\r
123   // SectNameStringTableIndex = Get16(p + 14, be);\r
124   return CheckSegmentEntrySize();\r
125 }\r
126 \r
127 struct CSegment\r
128 {\r
129   UInt32 Type;\r
130   UInt32 Flags;\r
131   UInt64 Offset;\r
132   UInt64 Va;\r
133   // UInt64 Pa;\r
134   UInt64 PSize;\r
135   UInt64 VSize;\r
136   // UInt64 Align;\r
137 \r
138   void UpdateTotalSize(UInt64 &totalSize)\r
139   {\r
140     UInt64 t = Offset + PSize;\r
141     if (t > totalSize)\r
142       totalSize = t;\r
143   }\r
144   void Parse(const Byte *p, bool mode64, bool be);\r
145 };\r
146 \r
147 void CSegment::Parse(const Byte *p, bool mode64, bool be)\r
148 {\r
149   Type = Get32(p, be);\r
150   if (mode64)\r
151   {\r
152     Flags = Get32(p + 4, be);\r
153     Offset = Get64(p + 8, be);\r
154     Va = Get64(p + 0x10, be);\r
155     // Pa = Get64(p + 0x18, be);\r
156     PSize = Get64(p + 0x20, be);\r
157     VSize = Get64(p + 0x28, be);\r
158     // Align = Get64(p + 0x30, be);\r
159   }\r
160   else\r
161   {\r
162     Offset = Get32(p + 4, be);\r
163     Va = Get32(p + 8, be);\r
164     // Pa = Get32(p + 12, be);\r
165     PSize = Get32(p + 16, be);\r
166     VSize = Get32(p + 20, be);\r
167     Flags = Get32(p + 24, be);\r
168     // Align = Get32(p + 28, be);\r
169   }\r
170 }\r
171 \r
172 static const CUInt32PCharPair g_MachinePairs[] =\r
173 {\r
174   { 0, "None" },\r
175   { 1, "AT&T WE 32100" },\r
176   { 2, "SPARC" },\r
177   { 3, "Intel 386" },\r
178   { 4, "Motorola 68000" },\r
179   { 5, "Motorola 88000" },\r
180   { 6, "Intel 486" },\r
181   { 7, "Intel i860" },\r
182   { 8, "MIPS" },\r
183   { 9, "IBM S/370" },\r
184   { 10, "MIPS RS3000 LE" },\r
185   { 11, "RS6000" },\r
186 \r
187   { 15, "PA-RISC" },\r
188   { 16, "nCUBE" },\r
189   { 17, "Fujitsu VPP500" },\r
190   { 18, "SPARC 32+" },\r
191   { 19, "Intel i960" },\r
192   { 20, "PowerPC" },\r
193   { 21, "PowerPC 64-bit" },\r
194   { 22, "IBM S/390" },\r
195 \r
196   { 36, "NEX v800" },\r
197   { 37, "Fujitsu FR20" },\r
198   { 38, "TRW RH-32" },\r
199   { 39, "Motorola RCE" },\r
200   { 40, "ARM" },\r
201   { 41, "Alpha" },\r
202   { 42, "Hitachi SH" },\r
203   { 43, "SPARC-V9" },\r
204   { 44, "Siemens Tricore" },\r
205   { 45, "ARC" },\r
206   { 46, "H8/300" },\r
207   { 47, "H8/300H" },\r
208   { 48, "H8S" },\r
209   { 49, "H8/500" },\r
210   { 50, "IA-64" },\r
211   { 51, "Stanford MIPS-X" },\r
212   { 52, "Motorola ColdFire" },\r
213   { 53, "M68HC12" },\r
214   { 54, "Fujitsu MMA" },\r
215   { 55, "Siemens PCP" },\r
216   { 56, "Sony nCPU" },\r
217   { 57, "Denso NDR1" },\r
218   { 58, "Motorola StarCore" },\r
219   { 59, "Toyota ME16" },\r
220   { 60, "ST100" },\r
221   { 61, "Advanced Logic TinyJ" },\r
222   { 62, "AMD64" },\r
223   { 63, "Sony DSP" },\r
224 \r
225   { 66, "Siemens FX66" },\r
226   { 67, "ST9+" },\r
227   { 68, "ST7" },\r
228   { 69, "MC68HC16" },\r
229   { 70, "MC68HC11" },\r
230   { 71, "MC68HC08" },\r
231   { 72, "MC68HC05" },\r
232   { 73, "Silicon Graphics SVx" },\r
233   { 74, "ST19" },\r
234   { 75, "Digital VAX" },\r
235   { 76, "Axis CRIS" },\r
236   { 77, "Infineon JAVELIN" },\r
237   { 78, "Element 14 FirePath" },\r
238   { 79, "LSI ZSP" },\r
239   { 80, "MMIX" },\r
240   { 81, "HUANY" },\r
241   { 82, "SiTera Prism" },\r
242   { 83, "Atmel AVR" },\r
243   { 84, "Fujitsu FR30" },\r
244   { 85, "Mitsubishi D10V" },\r
245   { 86, "Mitsubishi D30V" },\r
246   { 87, "NEC v850" },\r
247   { 88, "Mitsubishi M32R" },\r
248   { 89, "Matsushita MN10300" },\r
249   { 90, "Matsushita MN10200" },\r
250   { 91, "picoJava" },\r
251   { 92, "OpenRISC" },\r
252   { 93, "ARC Tangent-A5" },\r
253   { 94, "Tensilica Xtensa" },\r
254   { 0x9026, "Alpha" }\r
255 };\r
256 \r
257 static const CUInt32PCharPair g_AbiOS[] =\r
258 {\r
259   { 0, "None" },\r
260   { 1, "HP-UX" },\r
261   { 2, "NetBSD" },\r
262   { 3, "Linux" },\r
263 \r
264   { 6, "Solaris" },\r
265   { 7, "AIX" },\r
266   { 8, "IRIX" },\r
267   { 9, "FreeBSD" },\r
268   { 10, "TRU64" },\r
269   { 11, "Novell Modesto" },\r
270   { 12, "OpenBSD" },\r
271   { 13, "OpenVMS" },\r
272   { 14, "HP NSK" },\r
273   { 15, "AROS" },\r
274   { 97, "ARM" },\r
275   { 255, "Standalone" }\r
276 };\r
277 \r
278 static const CUInt32PCharPair g_SegmentFlags[] =\r
279 {\r
280   { 0, "Execute" },\r
281   { 1, "Write" },\r
282   { 2, "Read" }\r
283 };\r
284 \r
285 static const char *g_Types[] =\r
286 {\r
287   "None",\r
288   "Relocatable file",\r
289   "Executable file",\r
290   "Shared object file",\r
291   "Core file"\r
292 };\r
293 \r
294 static const char *g_SegnmentTypes[] =\r
295 {\r
296   "Unused",\r
297   "Loadable segment",\r
298   "Dynamic linking tables",\r
299   "Program interpreter path name",\r
300   "Note section",\r
301   "SHLIB",\r
302   "Program header table",\r
303   "TLS"\r
304 };\r
305 \r
306 class CHandler:\r
307   public IInArchive,\r
308   public CMyUnknownImp\r
309 {\r
310   CMyComPtr<IInStream> _inStream;\r
311   CObjectVector<CSegment> _sections;\r
312   UInt32 _peOffset;\r
313   CHeader _header;\r
314   UInt64 _totalSize;\r
315   HRESULT Open2(IInStream *stream);\r
316   bool Parse(const Byte *buf, UInt32 size);\r
317 public:\r
318   MY_UNKNOWN_IMP1(IInArchive)\r
319   INTERFACE_IInArchive(;)\r
320 };\r
321 \r
322 #define ELF_PT_PHDR 6\r
323 \r
324 bool CHandler::Parse(const Byte *buf, UInt32 size)\r
325 {\r
326   if (size < 64)\r
327     return false;\r
328   if (!_header.Parse(buf))\r
329     return false;\r
330   if (_header.ProgOffset > size ||\r
331       _header.ProgOffset + (UInt64)_header.SegmentEntrySize * _header.NumSegments > size ||\r
332       _header.NumSegments > NUM_SCAN_SECTIONS_MAX)\r
333     return false;\r
334   const Byte *p = buf + _header.ProgOffset;\r
335   _totalSize = _header.ProgOffset;\r
336   \r
337   for (int i = 0; i < _header.NumSegments; i++, p += _header.SegmentEntrySize)\r
338   {\r
339     CSegment sect;\r
340     sect.Parse(p, _header.Mode64, _header.Be);\r
341     sect.UpdateTotalSize(_totalSize);\r
342     if (sect.Type != ELF_PT_PHDR)\r
343       _sections.Add(sect);\r
344   }\r
345   UInt64 total2 = _header.SectOffset + (UInt64)_header.SectEntrySize * _header.NumSections;\r
346   if (total2 > _totalSize)\r
347     _totalSize = total2;\r
348   return true;\r
349 }\r
350 \r
351 STATPROPSTG kArcProps[] =\r
352 {\r
353   { NULL, kpidCpu, VT_BSTR},\r
354   { NULL, kpidBit64, VT_BOOL},\r
355   { NULL, kpidBigEndian, VT_BOOL},\r
356   { NULL, kpidHostOS, VT_BSTR},\r
357   { NULL, kpidCharacts, VT_BSTR},\r
358   { NULL, kpidPhySize, VT_UI8},\r
359   { NULL, kpidHeadersSize, VT_UI8}\r
360  };\r
361 \r
362 STATPROPSTG kProps[] =\r
363 {\r
364   { NULL, kpidPath, VT_BSTR},\r
365   { NULL, kpidSize, VT_UI8},\r
366   { NULL, kpidPackSize, VT_UI8},\r
367   { NULL, kpidType, VT_BSTR},\r
368   { NULL, kpidCharacts, VT_BSTR},\r
369   { NULL, kpidOffset, VT_UI8},\r
370   { NULL, kpidVa, VT_UI8}\r
371 };\r
372 \r
373 IMP_IInArchive_Props\r
374 IMP_IInArchive_ArcProps\r
375 \r
376 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)\r
377 {\r
378   COM_TRY_BEGIN\r
379   NCOM::CPropVariant prop;\r
380   switch(propID)\r
381   {\r
382     case kpidPhySize:  prop = _totalSize; break;\r
383     case kpidHeadersSize:  prop = _header.GetHeadersSize(); break;\r
384     case kpidBit64:  if (_header.Mode64) prop = _header.Mode64; break;\r
385     case kpidBigEndian:  if (_header.Be) prop = _header.Be; break;\r
386     case kpidCpu:  PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break;\r
387     case kpidHostOS:  PAIR_TO_PROP(g_AbiOS, _header.Os, prop); break;\r
388     case kpidCharacts:  TYPE_TO_PROP(g_Types, _header.Type, prop); break;\r
389   }\r
390   prop.Detach(value);\r
391   return S_OK;\r
392   COM_TRY_END\r
393 }\r
394 \r
395 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)\r
396 {\r
397   COM_TRY_BEGIN\r
398   NCOM::CPropVariant prop;\r
399   const CSegment &item = _sections[index];\r
400   switch(propID)\r
401   {\r
402     case kpidPath:\r
403     {\r
404       wchar_t sz[32];\r
405       ConvertUInt64ToString(index, sz);\r
406       prop = sz;\r
407       break;\r
408     }\r
409     case kpidSize:  prop = (UInt64)item.VSize; break;\r
410     case kpidPackSize:  prop = (UInt64)item.PSize; break;\r
411     case kpidOffset:  prop = item.Offset; break;\r
412     case kpidVa:  prop = item.Va; break;\r
413     case kpidType:  TYPE_TO_PROP(g_SegnmentTypes, item.Type, prop); break;\r
414     case kpidCharacts:  FLAGS_TO_PROP(g_SegmentFlags, item.Flags, prop); break;\r
415   }\r
416   prop.Detach(value);\r
417   return S_OK;\r
418   COM_TRY_END\r
419 }\r
420 \r
421 HRESULT CHandler::Open2(IInStream *stream)\r
422 {\r
423   const UInt32 kBufSize = 1 << 18;\r
424   const UInt32 kSigSize = 4;\r
425 \r
426   CByteBuffer buffer;\r
427   buffer.SetCapacity(kBufSize);\r
428   Byte *buf = buffer;\r
429 \r
430   size_t processed = kSigSize;\r
431   RINOK(ReadStream_FALSE(stream, buf, processed));\r
432   if (buf[0] != 0x7F || buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F')\r
433     return S_FALSE;\r
434   processed = kBufSize - kSigSize;\r
435   RINOK(ReadStream(stream, buf + kSigSize, &processed));\r
436   processed += kSigSize;\r
437   if (!Parse(buf, (UInt32)processed))\r
438     return S_FALSE;\r
439   UInt64 fileSize;\r
440   RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));\r
441   return (fileSize == _totalSize) ? S_OK : S_FALSE;\r
442 }\r
443 \r
444 STDMETHODIMP CHandler::Open(IInStream *inStream,\r
445     const UInt64 * /* maxCheckStartPosition */,\r
446     IArchiveOpenCallback * /* openArchiveCallback */)\r
447 {\r
448   COM_TRY_BEGIN\r
449   Close();\r
450   RINOK(Open2(inStream));\r
451   _inStream = inStream;\r
452   return S_OK;\r
453   COM_TRY_END\r
454 }\r
455 \r
456 STDMETHODIMP CHandler::Close()\r
457 {\r
458   _inStream.Release();\r
459   _sections.Clear();\r
460   return S_OK;\r
461 }\r
462 \r
463 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
464 {\r
465   *numItems = _sections.Size();\r
466   return S_OK;\r
467 }\r
468 \r
469 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
470     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
471 {\r
472   COM_TRY_BEGIN\r
473   bool allFilesMode = (numItems == (UInt32)-1);\r
474   if (allFilesMode)\r
475     numItems = _sections.Size();\r
476   if (numItems == 0)\r
477     return S_OK;\r
478   UInt64 totalSize = 0;\r
479   UInt32 i;\r
480   for (i = 0; i < numItems; i++)\r
481     totalSize += _sections[allFilesMode ? i : indices[i]].PSize;\r
482   extractCallback->SetTotal(totalSize);\r
483 \r
484   UInt64 currentTotalSize = 0;\r
485   UInt64 currentItemSize;\r
486   \r
487   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();\r
488   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;\r
489 \r
490   CLocalProgress *lps = new CLocalProgress;\r
491   CMyComPtr<ICompressProgressInfo> progress = lps;\r
492   lps->Init(extractCallback, false);\r
493 \r
494   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;\r
495   CMyComPtr<ISequentialInStream> inStream(streamSpec);\r
496   streamSpec->SetStream(_inStream);\r
497 \r
498   for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)\r
499   {\r
500     lps->InSize = lps->OutSize = currentTotalSize;\r
501     RINOK(lps->SetCur());\r
502     Int32 askMode = testMode ?\r
503         NExtract::NAskMode::kTest :\r
504         NExtract::NAskMode::kExtract;\r
505     UInt32 index = allFilesMode ? i : indices[i];\r
506     const CSegment &item = _sections[index];\r
507     currentItemSize = item.PSize;\r
508     \r
509     CMyComPtr<ISequentialOutStream> outStream;\r
510     RINOK(extractCallback->GetStream(index, &outStream, askMode));\r
511     if (!testMode && !outStream)\r
512       continue;\r
513       \r
514     RINOK(extractCallback->PrepareOperation(askMode));\r
515     RINOK(_inStream->Seek(item.Offset, STREAM_SEEK_SET, NULL));\r
516     streamSpec->Init(currentItemSize);\r
517     RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));\r
518     outStream.Release();\r
519     RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == currentItemSize ?\r
520         NExtract::NOperationResult::kOK:\r
521         NExtract::NOperationResult::kDataError));\r
522   }\r
523   return S_OK;\r
524   COM_TRY_END\r
525 }\r
526 \r
527 static IInArchive *CreateArc() { return new CHandler; }\r
528 \r
529 static CArcInfo g_ArcInfo =\r
530   { L"ELF", L"", 0, 0xDE, { 0 }, 0, false, CreateArc, 0 };\r
531 \r
532 REGISTER_ARC(Elf)\r
533 \r
534 }}\r