#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;
UInt64 ProgOffset;
UInt64 SectOffset;
UInt32 Flags;
UInt16 ElfHeaderSize;
UInt16 SegmentEntrySize;
UInt16 NumSegments;
UInt16 SectEntrySize;
UInt16 NumSections;
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)
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)
return
false
;
if
(Mode64)
{
ProgOffset = Get64(p + 0x20, be);
SectOffset = Get64(p + 0x28, be);
p += 0x30;
}
else
{
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);
return
CheckSegmentEntrySize();
}
struct
CSegment
{
UInt32 Type;
UInt32 Flags;
UInt64 Offset;
UInt64 Va;
UInt64 PSize;
UInt64 VSize;
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);
PSize = Get64(p + 0x20, be);
VSize = Get64(p + 0x28, be);
}
else
{
Offset = Get32(p + 4, be);
Va = Get32(p + 8, be);
PSize = Get32(p + 16, be);
VSize = Get32(p + 20, be);
Flags = Get32(p + 24, 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 *
,
IArchiveOpenCallback *
)
{
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)
}}