// 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 _inStream; CObjectVector _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 copyCoder = copyCoderSpec; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; CMyComPtr 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 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) }}