diff options
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Archive/ElfHandler.cpp')
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/ElfHandler.cpp | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/ElfHandler.cpp b/src/libs/7zip/win/CPP/7zip/Archive/ElfHandler.cpp new file mode 100644 index 000000000..c4ad78e9e --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/ElfHandler.cpp @@ -0,0 +1,534 @@ +// ElfHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" + +#include "Windows/PropVariantUtils.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +static UInt16 Get16(const Byte *p, int be) { if (be) return GetBe16(p); return GetUi16(p); } +static UInt32 Get32(const Byte *p, int be) { if (be) return GetBe32(p); return GetUi32(p); } +static UInt64 Get64(const Byte *p, int be) { if (be) return GetBe64(p); return GetUi64(p); } + +using namespace NWindows; + +namespace NArchive { +namespace NElf { + +#define ELF_CLASS_32 1 +#define ELF_CLASS_64 2 + +#define ELF_DATA_2LSB 1 +#define ELF_DATA_2MSB 2 + +#define NUM_SCAN_SECTIONS_MAX (1 << 6) + +struct CHeader +{ + bool Mode64; + bool Be; + Byte Os; + Byte AbiVer; + + UInt16 Type; + UInt16 Machine; + // UInt32 Version; + + // UInt64 EntryVa; + UInt64 ProgOffset; + UInt64 SectOffset; + UInt32 Flags; + UInt16 ElfHeaderSize; + UInt16 SegmentEntrySize; + UInt16 NumSegments; + UInt16 SectEntrySize; + UInt16 NumSections; + // UInt16 SectNameStringTableIndex; + + bool Parse(const Byte *buf); + + bool CheckSegmentEntrySize() const + { + return (Mode64 && SegmentEntrySize == 0x38) || (!Mode64 && SegmentEntrySize == 0x20); + }; + + UInt64 GetHeadersSize() const + { return ElfHeaderSize + + (UInt64)SegmentEntrySize * NumSegments + + (UInt64)SectEntrySize * NumSections; } + +}; + +bool CHeader::Parse(const Byte *p) +{ + switch(p[4]) + { + case ELF_CLASS_32: Mode64 = false; break; + case ELF_CLASS_64: Mode64 = true; break; + default: return false; + } + bool be; + switch(p[5]) + { + case ELF_DATA_2LSB: be = false; break; + case ELF_DATA_2MSB: be = true; break; + default: return false; + } + Be = be; + if (p[6] != 1) // Version + return false; + Os = p[7]; + AbiVer = p[8]; + for (int i = 9; i < 16; i++) + if (p[i] != 0) + return false; + + Type = Get16(p + 0x10, be); + Machine = Get16(p + 0x12, be); + if (Get32(p + 0x14, be) != 1) // Version + return false; + + if (Mode64) + { + // EntryVa = Get64(p + 0x18, be); + ProgOffset = Get64(p + 0x20, be); + SectOffset = Get64(p + 0x28, be); + p += 0x30; + } + else + { + // EntryVa = Get32(p + 0x18, be); + ProgOffset = Get32(p + 0x1C, be); + SectOffset = Get32(p + 0x20, be); + p += 0x24; + } + + Flags = Get32(p + 0, be); + ElfHeaderSize = Get16(p + 4, be); + SegmentEntrySize = Get16(p + 6, be); + NumSegments = Get16(p + 8, be); + SectEntrySize = Get16(p + 10, be); + NumSections = Get16(p + 12, be); + // SectNameStringTableIndex = Get16(p + 14, be); + return CheckSegmentEntrySize(); +} + +struct CSegment +{ + UInt32 Type; + UInt32 Flags; + UInt64 Offset; + UInt64 Va; + // UInt64 Pa; + UInt64 PSize; + UInt64 VSize; + // UInt64 Align; + + void UpdateTotalSize(UInt64 &totalSize) + { + UInt64 t = Offset + PSize; + if (t > totalSize) + totalSize = t; + } + void Parse(const Byte *p, bool mode64, bool be); +}; + +void CSegment::Parse(const Byte *p, bool mode64, bool be) +{ + Type = Get32(p, be); + if (mode64) + { + Flags = Get32(p + 4, be); + Offset = Get64(p + 8, be); + Va = Get64(p + 0x10, be); + // Pa = Get64(p + 0x18, be); + PSize = Get64(p + 0x20, be); + VSize = Get64(p + 0x28, be); + // Align = Get64(p + 0x30, be); + } + else + { + Offset = Get32(p + 4, be); + Va = Get32(p + 8, be); + // Pa = Get32(p + 12, be); + PSize = Get32(p + 16, be); + VSize = Get32(p + 20, be); + Flags = Get32(p + 24, be); + // Align = Get32(p + 28, be); + } +} + +static const CUInt32PCharPair g_MachinePairs[] = +{ + { 0, "None" }, + { 1, "AT&T WE 32100" }, + { 2, "SPARC" }, + { 3, "Intel 386" }, + { 4, "Motorola 68000" }, + { 5, "Motorola 88000" }, + { 6, "Intel 486" }, + { 7, "Intel i860" }, + { 8, "MIPS" }, + { 9, "IBM S/370" }, + { 10, "MIPS RS3000 LE" }, + { 11, "RS6000" }, + + { 15, "PA-RISC" }, + { 16, "nCUBE" }, + { 17, "Fujitsu VPP500" }, + { 18, "SPARC 32+" }, + { 19, "Intel i960" }, + { 20, "PowerPC" }, + { 21, "PowerPC 64-bit" }, + { 22, "IBM S/390" }, + + { 36, "NEX v800" }, + { 37, "Fujitsu FR20" }, + { 38, "TRW RH-32" }, + { 39, "Motorola RCE" }, + { 40, "ARM" }, + { 41, "Alpha" }, + { 42, "Hitachi SH" }, + { 43, "SPARC-V9" }, + { 44, "Siemens Tricore" }, + { 45, "ARC" }, + { 46, "H8/300" }, + { 47, "H8/300H" }, + { 48, "H8S" }, + { 49, "H8/500" }, + { 50, "IA-64" }, + { 51, "Stanford MIPS-X" }, + { 52, "Motorola ColdFire" }, + { 53, "M68HC12" }, + { 54, "Fujitsu MMA" }, + { 55, "Siemens PCP" }, + { 56, "Sony nCPU" }, + { 57, "Denso NDR1" }, + { 58, "Motorola StarCore" }, + { 59, "Toyota ME16" }, + { 60, "ST100" }, + { 61, "Advanced Logic TinyJ" }, + { 62, "AMD64" }, + { 63, "Sony DSP" }, + + { 66, "Siemens FX66" }, + { 67, "ST9+" }, + { 68, "ST7" }, + { 69, "MC68HC16" }, + { 70, "MC68HC11" }, + { 71, "MC68HC08" }, + { 72, "MC68HC05" }, + { 73, "Silicon Graphics SVx" }, + { 74, "ST19" }, + { 75, "Digital VAX" }, + { 76, "Axis CRIS" }, + { 77, "Infineon JAVELIN" }, + { 78, "Element 14 FirePath" }, + { 79, "LSI ZSP" }, + { 80, "MMIX" }, + { 81, "HUANY" }, + { 82, "SiTera Prism" }, + { 83, "Atmel AVR" }, + { 84, "Fujitsu FR30" }, + { 85, "Mitsubishi D10V" }, + { 86, "Mitsubishi D30V" }, + { 87, "NEC v850" }, + { 88, "Mitsubishi M32R" }, + { 89, "Matsushita MN10300" }, + { 90, "Matsushita MN10200" }, + { 91, "picoJava" }, + { 92, "OpenRISC" }, + { 93, "ARC Tangent-A5" }, + { 94, "Tensilica Xtensa" }, + { 0x9026, "Alpha" } +}; + +static const CUInt32PCharPair g_AbiOS[] = +{ + { 0, "None" }, + { 1, "HP-UX" }, + { 2, "NetBSD" }, + { 3, "Linux" }, + + { 6, "Solaris" }, + { 7, "AIX" }, + { 8, "IRIX" }, + { 9, "FreeBSD" }, + { 10, "TRU64" }, + { 11, "Novell Modesto" }, + { 12, "OpenBSD" }, + { 13, "OpenVMS" }, + { 14, "HP NSK" }, + { 15, "AROS" }, + { 97, "ARM" }, + { 255, "Standalone" } +}; + +static const CUInt32PCharPair g_SegmentFlags[] = +{ + { 0, "Execute" }, + { 1, "Write" }, + { 2, "Read" } +}; + +static const char *g_Types[] = +{ + "None", + "Relocatable file", + "Executable file", + "Shared object file", + "Core file" +}; + +static const char *g_SegnmentTypes[] = +{ + "Unused", + "Loadable segment", + "Dynamic linking tables", + "Program interpreter path name", + "Note section", + "SHLIB", + "Program header table", + "TLS" +}; + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _inStream; + CObjectVector<CSegment> _sections; + UInt32 _peOffset; + CHeader _header; + UInt64 _totalSize; + HRESULT Open2(IInStream *stream); + bool Parse(const Byte *buf, UInt32 size); +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +#define ELF_PT_PHDR 6 + +bool CHandler::Parse(const Byte *buf, UInt32 size) +{ + if (size < 64) + return false; + if (!_header.Parse(buf)) + return false; + if (_header.ProgOffset > size || + _header.ProgOffset + (UInt64)_header.SegmentEntrySize * _header.NumSegments > size || + _header.NumSegments > NUM_SCAN_SECTIONS_MAX) + return false; + const Byte *p = buf + _header.ProgOffset; + _totalSize = _header.ProgOffset; + + for (int i = 0; i < _header.NumSegments; i++, p += _header.SegmentEntrySize) + { + CSegment sect; + sect.Parse(p, _header.Mode64, _header.Be); + sect.UpdateTotalSize(_totalSize); + if (sect.Type != ELF_PT_PHDR) + _sections.Add(sect); + } + UInt64 total2 = _header.SectOffset + (UInt64)_header.SectEntrySize * _header.NumSections; + if (total2 > _totalSize) + _totalSize = total2; + return true; +} + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidCpu, VT_BSTR}, + { NULL, kpidBit64, VT_BOOL}, + { NULL, kpidBigEndian, VT_BOOL}, + { NULL, kpidHostOS, VT_BSTR}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidHeadersSize, VT_UI8} + }; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidType, VT_BSTR}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidOffset, VT_UI8}, + { NULL, kpidVa, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: prop = _totalSize; break; + case kpidHeadersSize: prop = _header.GetHeadersSize(); break; + case kpidBit64: if (_header.Mode64) prop = _header.Mode64; break; + case kpidBigEndian: if (_header.Be) prop = _header.Be; break; + case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break; + case kpidHostOS: PAIR_TO_PROP(g_AbiOS, _header.Os, prop); break; + case kpidCharacts: TYPE_TO_PROP(g_Types, _header.Type, prop); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + const CSegment &item = _sections[index]; + switch(propID) + { + case kpidPath: + { + wchar_t sz[32]; + ConvertUInt64ToString(index, sz); + prop = sz; + break; + } + case kpidSize: prop = (UInt64)item.VSize; break; + case kpidPackSize: prop = (UInt64)item.PSize; break; + case kpidOffset: prop = item.Offset; break; + case kpidVa: prop = item.Va; break; + case kpidType: TYPE_TO_PROP(g_SegnmentTypes, item.Type, prop); break; + case kpidCharacts: FLAGS_TO_PROP(g_SegmentFlags, item.Flags, prop); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +HRESULT CHandler::Open2(IInStream *stream) +{ + const UInt32 kBufSize = 1 << 18; + const UInt32 kSigSize = 4; + + CByteBuffer buffer; + buffer.SetCapacity(kBufSize); + Byte *buf = buffer; + + size_t processed = kSigSize; + RINOK(ReadStream_FALSE(stream, buf, processed)); + if (buf[0] != 0x7F || buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') + return S_FALSE; + processed = kBufSize - kSigSize; + RINOK(ReadStream(stream, buf + kSigSize, &processed)); + processed += kSigSize; + if (!Parse(buf, (UInt32)processed)) + return S_FALSE; + UInt64 fileSize; + RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize)); + return (fileSize == _totalSize) ? S_OK : S_FALSE; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + RINOK(Open2(inStream)); + _inStream = inStream; + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _inStream.Release(); + _sections.Clear(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _sections.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _sections.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _sections[allFilesMode ? i : indices[i]].PSize; + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + UInt64 currentItemSize; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_inStream); + + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CSegment &item = _sections[index]; + currentItemSize = item.PSize; + + CMyComPtr<ISequentialOutStream> outStream; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(_inStream->Seek(item.Offset, STREAM_SEEK_SET, NULL)); + streamSpec->Init(currentItemSize); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + outStream.Release(); + RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == currentItemSize ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"ELF", L"", 0, 0xDE, { 0 }, 0, false, CreateArc, 0 }; + +REGISTER_ARC(Elf) + +}} |