📄 rar3vm.cpp
字号:
// Rar3Vm.cpp
// According to unRAR license, this code may not be used to develop
// a program that creates RAR archives
/*
Note:
Due to performance considerations Rar VM may set Flags C incorrectly
for some operands (SHL x, 0, ... ).
Check implementation of concrete VM command
to see if it sets flags right.
*/
#include "StdAfx.h"
#include "Rar3Vm.h"
extern "C"
{
#include "../../../../C/Alloc.h"
#include "../../../../C/7zCrc.h"
}
namespace NCompress {
namespace NRar3 {
UInt32 CMemBitDecoder::ReadBits(int numBits)
{
UInt32 res = 0;
for (;;)
{
Byte b = _bitPos < _bitSize ? _data[_bitPos >> 3] : 0;
int avail = (int)(8 - (_bitPos & 7));
if (numBits <= avail)
{
_bitPos += numBits;
return res | (b >> (avail - numBits)) & ((1 << numBits) - 1);
}
numBits -= avail;
res |= (UInt32)(b & ((1 << avail) - 1)) << numBits;
_bitPos += avail;
}
}
UInt32 CMemBitDecoder::ReadBit() { return ReadBits(1); }
namespace NVm {
static const UInt32 kStackRegIndex = kNumRegs - 1;
static const UInt32 FLAG_C = 1;
static const UInt32 FLAG_Z = 2;
static const UInt32 FLAG_S = 0x80000000;
static const Byte CF_OP0 = 0;
static const Byte CF_OP1 = 1;
static const Byte CF_OP2 = 2;
static const Byte CF_OPMASK = 3;
static const Byte CF_BYTEMODE = 4;
static const Byte CF_JUMP = 8;
static const Byte CF_PROC = 16;
static const Byte CF_USEFLAGS = 32;
static const Byte CF_CHFLAGS = 64;
static Byte kCmdFlags[]=
{
/* CMD_MOV */ CF_OP2 | CF_BYTEMODE,
/* CMD_CMP */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_ADD */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_SUB */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_JZ */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
/* CMD_JNZ */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
/* CMD_INC */ CF_OP1 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_DEC */ CF_OP1 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_JMP */ CF_OP1 | CF_JUMP,
/* CMD_XOR */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_AND */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_OR */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_TEST */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_JS */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
/* CMD_JNS */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
/* CMD_JB */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
/* CMD_JBE */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
/* CMD_JA */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
/* CMD_JAE */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
/* CMD_PUSH */ CF_OP1,
/* CMD_POP */ CF_OP1,
/* CMD_CALL */ CF_OP1 | CF_PROC,
/* CMD_RET */ CF_OP0 | CF_PROC,
/* CMD_NOT */ CF_OP1 | CF_BYTEMODE,
/* CMD_SHL */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_SHR */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_SAR */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_NEG */ CF_OP1 | CF_BYTEMODE | CF_CHFLAGS,
/* CMD_PUSHA */ CF_OP0,
/* CMD_POPA */ CF_OP0,
/* CMD_PUSHF */ CF_OP0 | CF_USEFLAGS,
/* CMD_POPF */ CF_OP0 | CF_CHFLAGS,
/* CMD_MOVZX */ CF_OP2,
/* CMD_MOVSX */ CF_OP2,
/* CMD_XCHG */ CF_OP2 | CF_BYTEMODE,
/* CMD_MUL */ CF_OP2 | CF_BYTEMODE,
/* CMD_DIV */ CF_OP2 | CF_BYTEMODE,
/* CMD_ADC */ CF_OP2 | CF_BYTEMODE | CF_USEFLAGS | CF_CHFLAGS ,
/* CMD_SBB */ CF_OP2 | CF_BYTEMODE | CF_USEFLAGS | CF_CHFLAGS ,
/* CMD_PRINT */ CF_OP0
};
CVm::CVm(): Mem(NULL) {}
bool CVm::Create()
{
if (Mem == NULL)
Mem = (Byte *)::MyAlloc(kSpaceSize + 4);
return (Mem != NULL);
}
CVm::~CVm()
{
::MyFree(Mem);
}
// CVm::Execute can change CProgram object: it clears progarm if VM returns error.
bool CVm::Execute(CProgram *prg, const CProgramInitState *initState,
CBlockRef &outBlockRef, CRecordVector<Byte> &outGlobalData)
{
memcpy(R, initState->InitR, sizeof(initState->InitR));
R[kStackRegIndex] = kSpaceSize;
R[kNumRegs] = 0;
Flags = 0;
UInt32 globalSize = MyMin((UInt32)initState->GlobalData.Size(), kGlobalSize);
if (globalSize != 0)
memcpy(Mem + kGlobalOffset, &initState->GlobalData[0], globalSize);
UInt32 staticSize = MyMin((UInt32)prg->StaticData.Size(), kGlobalSize - globalSize);
if (staticSize != 0)
memcpy(Mem + kGlobalOffset + globalSize, &prg->StaticData[0], staticSize);
bool res = true;
#ifdef RARVM_STANDARD_FILTERS
if (prg->StandardFilterIndex >= 0)
ExecuteStandardFilter(prg->StandardFilterIndex);
else
#endif
{
res = ExecuteCode(prg);
if (!res)
prg->Commands[0].OpCode = CMD_RET;
}
UInt32 newBlockPos = GetFixedGlobalValue32(NGlobalOffset::kBlockPos) & kSpaceMask;
UInt32 newBlockSize = GetFixedGlobalValue32(NGlobalOffset::kBlockSize) & kSpaceMask;
if (newBlockPos + newBlockSize >= kSpaceSize)
newBlockPos = newBlockSize = 0;
outBlockRef.Offset = newBlockPos;
outBlockRef.Size = newBlockSize;
outGlobalData.Clear();
UInt32 dataSize = GetFixedGlobalValue32(NGlobalOffset::kGlobalMemOutSize);
dataSize = MyMin(dataSize, kGlobalSize - kFixedGlobalSize);
if (dataSize != 0)
{
dataSize += kFixedGlobalSize;
outGlobalData.Reserve(dataSize);
for (UInt32 i = 0; i < dataSize; i++)
outGlobalData.Add(Mem[kGlobalOffset + i]);
}
return res;
}
#define SET_IP(IP) \
if ((IP) >= numCommands) return true; \
if (--maxOpCount <= 0) return false; \
cmd = commands + (IP);
#define GET_FLAG_S_B(res) (((res) & 0x80) ? FLAG_S : 0)
#define SET_IP_OP1 { UInt32 val = GetOperand32(&cmd->Op1); SET_IP(val); }
#define FLAGS_UPDATE_SZ Flags = res == 0 ? FLAG_Z : res & FLAG_S
#define FLAGS_UPDATE_SZ_B Flags = (res & 0xFF) == 0 ? FLAG_Z : GET_FLAG_S_B(res)
UInt32 CVm::GetOperand32(const COperand *op) const
{
switch(op->Type)
{
case OP_TYPE_REG: return R[op->Data];
case OP_TYPE_REGMEM: return GetValue32(&Mem[(op->Base + R[op->Data]) & kSpaceMask]);
default: return op->Data;
}
}
void CVm::SetOperand32(const COperand *op, UInt32 val)
{
switch(op->Type)
{
case OP_TYPE_REG: R[op->Data] = val; return;
case OP_TYPE_REGMEM: SetValue32(&Mem[(op->Base + R[op->Data]) & kSpaceMask], val); return;
}
}
Byte CVm::GetOperand8(const COperand *op) const
{
switch(op->Type)
{
case OP_TYPE_REG: return (Byte)R[op->Data];
case OP_TYPE_REGMEM: return Mem[(op->Base + R[op->Data]) & kSpaceMask];;
default: return (Byte)op->Data;
}
}
void CVm::SetOperand8(const COperand *op, Byte val)
{
switch(op->Type)
{
case OP_TYPE_REG: R[op->Data] = (R[op->Data] & 0xFFFFFF00) | val; return;
case OP_TYPE_REGMEM: Mem[(op->Base + R[op->Data]) & kSpaceMask] = val; return;
}
}
UInt32 CVm::GetOperand(bool byteMode, const COperand *op) const
{
if (byteMode)
return GetOperand8(op);
return GetOperand32(op);
}
void CVm::SetOperand(bool byteMode, const COperand *op, UInt32 val)
{
if (byteMode)
SetOperand8(op, (Byte)(val & 0xFF));
else
SetOperand32(op, val);
}
bool CVm::ExecuteCode(const CProgram *prg)
{
Int32 maxOpCount = 25000000;
const CCommand *commands = &prg->Commands[0];
const CCommand *cmd = commands;
UInt32 numCommands = prg->Commands.Size();
for (;;)
{
switch(cmd->OpCode)
{
#ifndef RARVM_NO_VM
case CMD_MOV:
SetOperand32(&cmd->Op1, GetOperand32(&cmd->Op2));
break;
case CMD_MOVB:
SetOperand8(&cmd->Op1, GetOperand8(&cmd->Op2));
break;
case CMD_CMP:
{
UInt32 v1 = GetOperand32(&cmd->Op1);
UInt32 res = v1 - GetOperand32(&cmd->Op2);
Flags = res == 0 ? FLAG_Z : (res > v1) | (res & FLAG_S);
}
break;
case CMD_CMPB:
{
Byte v1 = GetOperand8(&cmd->Op1);
Byte res = v1 - GetOperand8(&cmd->Op2);
res &= 0xFF;
Flags = res == 0 ? FLAG_Z : (res > v1) | GET_FLAG_S_B(res);
}
break;
case CMD_ADD:
{
UInt32 v1 = GetOperand32(&cmd->Op1);
UInt32 res = v1 + GetOperand32(&cmd->Op2);
SetOperand32(&cmd->Op1, res);
Flags = (res < v1) | (res == 0 ? FLAG_Z : (res & FLAG_S));
}
break;
case CMD_ADDB:
{
Byte v1 = GetOperand8(&cmd->Op1);
Byte res = v1 + GetOperand8(&cmd->Op2);
res &= 0xFF;
SetOperand8(&cmd->Op1, (Byte)res);
Flags = (res < v1) | (res == 0 ? FLAG_Z : GET_FLAG_S_B(res));
}
break;
case CMD_ADC:
{
UInt32 v1 = GetOperand(cmd->ByteMode, &cmd->Op1);
UInt32 FC = (Flags & FLAG_C);
UInt32 res = v1 + GetOperand(cmd->ByteMode, &cmd->Op2) + FC;
if (cmd->ByteMode)
res &= 0xFF;
SetOperand(cmd->ByteMode, &cmd->Op1, res);
Flags = (res < v1 || res == v1 && FC) | (res == 0 ? FLAG_Z : (res & FLAG_S));
}
break;
case CMD_SUB:
{
UInt32 v1 = GetOperand32(&cmd->Op1);
UInt32 res = v1 - GetOperand32(&cmd->Op2);
SetOperand32(&cmd->Op1, res);
Flags = res == 0 ? FLAG_Z : (res > v1) | (res & FLAG_S);
}
break;
case CMD_SUBB:
{
UInt32 v1 = GetOperand8(&cmd->Op1);
UInt32 res = v1 - GetOperand8(&cmd->Op2);
SetOperand8(&cmd->Op1, (Byte)res);
Flags = res == 0 ? FLAG_Z : (res > v1) | (res & FLAG_S);
}
break;
case CMD_SBB:
{
UInt32 v1 = GetOperand(cmd->ByteMode, &cmd->Op1);
UInt32 FC = (Flags & FLAG_C);
UInt32 res = v1 - GetOperand(cmd->ByteMode, &cmd->Op2) - FC;
// Flags = res == 0 ? FLAG_Z : (res > v1 || res == v1 && FC) | (res & FLAG_S);
if (cmd->ByteMode)
res &= 0xFF;
SetOperand(cmd->ByteMode, &cmd->Op1, res);
Flags = (res > v1 || res == v1 && FC) | (res == 0 ? FLAG_Z : (res & FLAG_S));
}
break;
case CMD_INC:
{
UInt32 res = GetOperand32(&cmd->Op1) + 1;
SetOperand32(&cmd->Op1, res);
FLAGS_UPDATE_SZ;
}
break;
case CMD_INCB:
{
Byte res = GetOperand8(&cmd->Op1) + 1;
SetOperand8(&cmd->Op1, res);;
FLAGS_UPDATE_SZ_B;
}
break;
case CMD_DEC:
{
UInt32 res = GetOperand32(&cmd->Op1) - 1;
SetOperand32(&cmd->Op1, res);
FLAGS_UPDATE_SZ;
}
break;
case CMD_DECB:
{
Byte res = GetOperand8(&cmd->Op1) - 1;
SetOperand8(&cmd->Op1, res);;
FLAGS_UPDATE_SZ_B;
}
break;
case CMD_XOR:
{
UInt32 res = GetOperand32(&cmd->Op1) ^ GetOperand32(&cmd->Op2);
SetOperand32(&cmd->Op1, res);
FLAGS_UPDATE_SZ;
}
break;
case CMD_XORB:
{
Byte res = GetOperand8(&cmd->Op1) ^ GetOperand8(&cmd->Op2);
SetOperand8(&cmd->Op1, res);
FLAGS_UPDATE_SZ_B;
}
break;
case CMD_AND:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -