VMProtect/core/dwarf.cc

1188 lines
35 KiB
C++

#include "../runtime/crypto.h"
#include "objects.h"
#include "files.h"
#include "dwarf.h"
/**
* EncodedData
*/
EncodedData::EncodedData(uint64_t address, OperandSize pointer_size)
: std::vector<uint8_t>(), address_(address), pointer_size_(pointer_size)
{
}
void EncodedData::Read(void *buf, size_t count, size_t *pos) const
{
if (*pos + count > size())
throw std::runtime_error("buffer index out of bounds");
memcpy(buf, data() + *pos, count);
*pos += count;
}
void EncodedData::ReadFromFile(IArchitecture &file, size_t size)
{
resize(size);
file.Read(data(), size);
}
uint8_t EncodedData::ReadByte(size_t *pos) const
{
uint8_t res;
Read(&res, sizeof(res), pos);
return res;
}
uint16_t EncodedData::ReadWord(size_t *pos) const
{
uint16_t res;
Read(&res, sizeof(res), pos);
return res;
}
uint32_t EncodedData::ReadDWord(size_t *pos) const
{
uint32_t res;
Read(&res, sizeof(res), pos);
return res;
}
uint64_t EncodedData::ReadQWord(size_t *pos) const
{
uint64_t res;
Read(&res, sizeof(res), pos);
return res;
}
uint64_t EncodedData::ReadUleb128(size_t *pos) const
{
uint64_t result = 0, slice;
int bit = 0;
uint8_t byte;
do {
byte = ReadByte(pos);
slice = byte & 0x7f;
if (bit > 63) {
throw std::runtime_error("uleb128 too big for uint64");
} else {
result |= (slice << bit);
bit += 7;
}
} while (byte & 0x80);
return result;
}
int64_t EncodedData::ReadSleb128(size_t *pos) const
{
int64_t result = 0;
int bit = 0;
uint8_t byte;
do {
byte = ReadByte(pos);
result |= (int64_t(byte & 0x7f) << bit);
bit += 7;
} while (byte & 0x80);
/* Sign extend negative numbers. */
if ((byte & 0x40) != 0)
result |= (-1LL) << bit; //-V610
return result;
}
std::string EncodedData::ReadString(size_t *pos) const
{
std::string res;
while (true) {
char c = ReadByte(pos);
if (c == '\0')
break;
res.push_back(c);
}
return res;
}
uint32_t EncodedData::ReadUnsigned(size_t *pos) const
{
static const uint32_t len_tab[0x10] = {
1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1, 5
};
uint8_t c = at(*pos) & 0xf;
size_t size = len_tab[c];
*pos += size;
uint32_t res = 0;
bool need_shift = true;
if (size > 4) {
size = 4;
need_shift = false;
}
memcpy(&res, data() + *pos - size, size);
if (need_shift)
res >>= len_tab[c];
return res;
}
uint64_t EncodedData::ReadEncoding(uint8_t encoding, size_t *pos) const
{
uint64_t base;
switch (encoding & 0x70) {
case DW_EH_PE_pcrel:
base = address_ + *pos;
break;
case DW_EH_PE_datarel:
base = address_;
break;
default:
base = 0;
break;
}
switch (encoding & 0x0f) {
case DW_EH_PE_absptr:
if (pointer_size_ == osDWord)
return base + static_cast<int32_t>(ReadDWord(pos));
if (pointer_size_ == osQWord)
return base + static_cast<int64_t>(ReadQWord(pos));
break;
case DW_EH_PE_uleb128:
return base + ReadUleb128(pos);
case DW_EH_PE_sleb128:
return base + ReadSleb128(pos);
case DW_EH_PE_udata2:
return base + ReadWord(pos);
case DW_EH_PE_sdata2:
return base + static_cast<int16_t>(ReadWord(pos));
case DW_EH_PE_udata4:
return base + ReadDWord(pos);
case DW_EH_PE_sdata4:
return base + static_cast<int32_t>(ReadDWord(pos));
case DW_EH_PE_udata8:
return base + ReadQWord(pos);
case DW_EH_PE_sdata8:
return base + static_cast<int64_t>(ReadQWord(pos));
}
throw std::runtime_error("Invalid encoding");
}
void EncodedData::WriteUleb128(uint64_t value)
{
uint8_t byte;
do {
byte = value & 0x7F;
value &= ~(uint64_t)0x7F;
if ( value != 0 )
byte |= 0x80;
push_back(byte);
value = value >> 7;
} while( byte >= 0x80 );
}
void EncodedData::WriteSleb128(int64_t value)
{
bool is_neg = ( value < 0 );
uint8_t byte;
bool more;
do {
byte = value & 0x7F;
value = value >> 7;
if (is_neg)
more = ((value != -1) || ((byte & 0x40) == 0));
else
more = ((value != 0) || ((byte & 0x40) != 0));
if (more)
byte |= 0x80;
push_back(byte);
}
while( more );
}
void EncodedData::WriteString(const std::string &str)
{
Write(str.c_str(), str.size() + 1);
}
void EncodedData::WriteByte(uint8_t value)
{
push_back(value);
}
void EncodedData::WriteWord(uint16_t value)
{
Write(&value, sizeof(value));
}
void EncodedData::WriteDWord(uint32_t value)
{
Write(&value, sizeof(value));
}
void EncodedData::WriteQWord(uint64_t value)
{
Write(&value, sizeof(value));
}
void EncodedData::Write(const void *buf, size_t count)
{
insert(end(), static_cast<const uint8_t *>(buf), static_cast<const uint8_t *>(buf) + count);
}
void EncodedData::WriteEncoding(uint8_t encoding, uint64_t value)
{
switch (encoding & 0x70) {
case DW_EH_PE_pcrel:
value -= address_ + size();
break;
case DW_EH_PE_datarel:
value -= address_;
break;
}
switch (encoding & 0x0f) {
case DW_EH_PE_absptr:
if (pointer_size_ == osDWord) {
WriteDWord(static_cast<uint32_t>(value));
return;
}
if (pointer_size_ == osQWord) {
WriteQWord(value);
return;
}
break;
case DW_EH_PE_uleb128:
WriteUleb128(value);
return;
case DW_EH_PE_sleb128:
WriteSleb128(value);
return;
case DW_EH_PE_udata2:
case DW_EH_PE_sdata2:
WriteWord(static_cast<uint16_t>(value));
return;
case DW_EH_PE_udata4:
case DW_EH_PE_sdata4:
WriteDWord(static_cast<uint32_t>(value));
return;
case DW_EH_PE_udata8:
case DW_EH_PE_sdata8:
WriteQWord(value);
return;
}
throw std::runtime_error("Invalid encoding");
}
size_t EncodedData::encoding_size(uint8_t encoding) const
{
switch (encoding & 0x0f) {
case DW_EH_PE_absptr:
return OperandSizeToValue(pointer_size_);
case DW_EH_PE_udata2:
case DW_EH_PE_sdata2:
return sizeof(uint16_t);
case DW_EH_PE_udata4:
case DW_EH_PE_sdata4:
return sizeof(uint32_t);
case DW_EH_PE_udata8:
case DW_EH_PE_sdata8:
return sizeof(uint64_t);
}
throw std::runtime_error("Invalid encoding");
}
/**
* CommonInformationEntry
*/
CommonInformationEntry::CommonInformationEntry(CommonInformationEntryList *owner, uint8_t version, const std::string &augmentation,
uint64_t code_alignment_factor, uint64_t data_alignment_factor, uint8_t return_address_register, uint8_t fde_encoding,
uint8_t lsda_encoding, uint8_t personality_encoding, uint64_t personality_routine, const std::vector<uint8_t> &initial_instructions)
: IObject(), owner_(owner), version_(version), augmentation_(augmentation), code_alignment_factor_(code_alignment_factor),
data_alignment_factor_(data_alignment_factor), return_address_register_(return_address_register), fde_encoding_(fde_encoding),
lsda_encoding_(lsda_encoding), personality_encoding_(personality_encoding), personality_routine_(personality_routine), initial_instructions_(initial_instructions)
{
}
CommonInformationEntry::CommonInformationEntry(CommonInformationEntryList *owner, const CommonInformationEntry &src)
: IObject(), owner_(owner)
{
version_ = src.version_;
code_alignment_factor_ = src.code_alignment_factor_;
data_alignment_factor_ = src.data_alignment_factor_;
return_address_register_ = src.return_address_register_;
augmentation_ = src.augmentation_;
fde_encoding_ = src.fde_encoding_;
lsda_encoding_ = src.lsda_encoding_;
personality_encoding_ = src.personality_encoding_;
personality_routine_ = src.personality_routine_;
initial_instructions_ = src.initial_instructions_;
}
CommonInformationEntry *CommonInformationEntry::Clone(CommonInformationEntryList *owner) const
{
CommonInformationEntry *entry = new CommonInformationEntry(owner, *this);
return entry;
}
CommonInformationEntry::~CommonInformationEntry()
{
if (owner_)
owner_->RemoveObject(this);
}
void CommonInformationEntry::Rebase(uint64_t delta_base)
{
if (personality_routine_)
personality_routine_ += delta_base;
}
/**
* CommonInformationEntryList
*/
CommonInformationEntryList::CommonInformationEntryList()
: ObjectList<CommonInformationEntry>()
{
}
CommonInformationEntryList::CommonInformationEntryList(const CommonInformationEntryList &src)
: ObjectList<CommonInformationEntry>()
{
for (size_t i = 0; i < src.count(); i++) {
AddObject(src.item(i)->Clone(this));
}
}
CommonInformationEntry *CommonInformationEntryList::Add(uint8_t version, const std::string &augmentation,
uint64_t code_alignment_factor, uint64_t data_alignment_factor, uint8_t return_address_register, uint8_t fde_encoding,
uint8_t lsda_encoding, uint8_t personality_encoding, uint64_t personality_routine, const std::vector<uint8_t> &call_frame_instructions)
{
CommonInformationEntry *entry = new CommonInformationEntry(this, version, augmentation, code_alignment_factor,
data_alignment_factor, return_address_register, fde_encoding, lsda_encoding, personality_encoding, personality_routine, call_frame_instructions);
AddObject(entry);
return entry;
}
CommonInformationEntryList *CommonInformationEntryList::Clone() const
{
CommonInformationEntryList *list = new CommonInformationEntryList(*this);
return list;
}
void CommonInformationEntryList::Rebase(uint64_t delta_base)
{
for (size_t i = 0; i < count(); i++) {
item(i)->Rebase(delta_base);
}
}
/**
* DwarfParser
*/
uint32_t DwarfParser::CreateCompactEncoding(IArchitecture &file, const std::vector<uint8_t> &fde_instructions, CommonInformationEntry *cie_, uint64_t start)
{
PrologInfo info = PrologInfo();
if (ParseInstructions(cie_->initial_instructions(), cie_, info) && ParseInstructions(fde_instructions, cie_, info))
return (file.cpu_address_size() == osDWord) ? CreateCompactEncodingForX32(file, start, info) : CreateCompactEncodingForX64(file, start, info);
return UNWIND_X86_MODE_DWARF;
}
bool DwarfParser::ParseInstructions(const std::vector<uint8_t> &instructions, CommonInformationEntry *cie_, PrologInfo &info)
{
EncodedData data;
data.Write(instructions.data(), instructions.size());
uint64_t reg, reg2, length;
uint64_t code_offset = 0;
int64_t offset;
PrologInfo initial_state = info;
for (size_t pos = 0; pos < data.size(); ) {
uint8_t opcode = data.ReadByte(&pos);
uint8_t operand;
switch (opcode) {
case DW_CFA_nop:
break;
case DW_CFA_set_loc:
code_offset = data.ReadEncoding(cie_->fde_encoding(), &pos);
break;
case DW_CFA_advance_loc1:
code_offset += data.ReadByte(&pos) * cie_->code_alignment_factor();
break;
case DW_CFA_advance_loc2:
code_offset += data.ReadWord(&pos) * cie_->code_alignment_factor();
break;
case DW_CFA_advance_loc4:
code_offset += data.ReadDWord(&pos) * cie_->code_alignment_factor();
break;
case DW_CFA_offset_extended:
reg = data.ReadUleb128(&pos);
offset = data.ReadUleb128(&pos) * cie_->data_alignment_factor();
if (reg >= kMaxRegisterNumber)
return false;
if (info.savedRegisters[reg].location != kRegisterUnused)
info.registerSavedMoreThanOnce = true;
info.savedRegisters[reg].location = kRegisterInCFA;
info.savedRegisters[reg].value = offset;
break;
case DW_CFA_restore_extended:
reg = data.ReadUleb128(&pos);
if (reg >= kMaxRegisterNumber)
return false;
info.savedRegisters[reg] = initial_state.savedRegisters[reg];
break;
case DW_CFA_undefined:
reg = data.ReadUleb128(&pos);
if (reg >= kMaxRegisterNumber)
return false;
info.savedRegisters[reg].location = kRegisterUnused;
break;
case DW_CFA_same_value:
reg = data.ReadUleb128(&pos);
if (reg >= kMaxRegisterNumber)
return false;
info.savedRegisters[reg].location = kRegisterUnused;
info.sameValueUsed = true;
break;
case DW_CFA_register:
reg = data.ReadUleb128(&pos);
reg2 = data.ReadUleb128(&pos);
if (reg >= kMaxRegisterNumber)
return false;
if (reg2 >= kMaxRegisterNumber)
return false;
info.savedRegisters[reg].location = kRegisterInRegister;
info.savedRegisters[reg].value = reg2;
info.registersInOtherRegisters = true;
break;
case DW_CFA_remember_state:
break;
case DW_CFA_restore_state:
break;
case DW_CFA_def_cfa:
reg = data.ReadUleb128(&pos);
offset = data.ReadUleb128(&pos);
if (reg >= kMaxRegisterNumber)
return false;
info.cfaRegister = static_cast<uint32_t>(reg);
info.cfaRegisterOffset = static_cast<int32_t>(offset);
if (offset > 0x80000000)
info.cfaOffsetWasNegative = true;
break;
case DW_CFA_def_cfa_register:
reg = data.ReadUleb128(&pos);
if (reg >= kMaxRegisterNumber)
return false;
info.cfaRegister = static_cast<uint32_t>(reg);
break;
case DW_CFA_def_cfa_offset:
info.cfaRegisterOffset = static_cast<int32_t>(data.ReadUleb128(&pos));
info.codeOffsetAtStackDecrement = static_cast<uint32_t>(code_offset);
break;
case DW_CFA_def_cfa_expression:
info.cfaRegister = 0;
info.cfaExpression = pos;
length = data.ReadUleb128(&pos);
pos += static_cast<size_t>(length);
break;
case DW_CFA_expression:
reg = data.ReadUleb128(&pos);
if (reg >= kMaxRegisterNumber)
return false;
info.savedRegisters[reg].location = kRegisterAtExpression;
info.savedRegisters[reg].value = pos;
length = data.ReadUleb128(&pos);
pos += static_cast<size_t>(length);
break;
case DW_CFA_offset_extended_sf:
reg = data.ReadUleb128(&pos);
offset = data.ReadSleb128(&pos) * cie_->data_alignment_factor();
if (reg >= kMaxRegisterNumber)
return false;
if (info.savedRegisters[reg].location != kRegisterUnused)
info.registerSavedMoreThanOnce = true;
info.savedRegisters[reg].location = kRegisterInCFA;
info.savedRegisters[reg].value = offset;
break;
case DW_CFA_def_cfa_sf:
reg = data.ReadUleb128(&pos);
offset = data.ReadSleb128(&pos) * cie_->data_alignment_factor();
if (reg >= kMaxRegisterNumber)
return false;
info.cfaRegister = static_cast<uint32_t>(reg);
info.cfaRegisterOffset = static_cast<int32_t>(offset);
break;
case DW_CFA_def_cfa_offset_sf:
info.cfaRegisterOffset = static_cast<int32_t>(data.ReadSleb128(&pos) * cie_->data_alignment_factor());
info.codeOffsetAtStackDecrement = static_cast<uint32_t>(code_offset);
break;
case DW_CFA_val_offset:
reg = data.ReadUleb128(&pos);
offset = data.ReadUleb128(&pos) * cie_->data_alignment_factor();
if (reg >= kMaxRegisterNumber)
return false;
info.savedRegisters[reg].location = kRegisterOffsetFromCFA;
info.savedRegisters[reg].value = offset;
break;
case DW_CFA_val_offset_sf:
reg = data.ReadUleb128(&pos);
offset = data.ReadSleb128(&pos) * cie_->data_alignment_factor();
if (reg >= kMaxRegisterNumber)
return false;
info.savedRegisters[reg].location = kRegisterOffsetFromCFA;
info.savedRegisters[reg].value = offset;
break;
case DW_CFA_val_expression:
reg = data.ReadUleb128(&pos);
if (reg >= kMaxRegisterNumber)
return false;
info.savedRegisters[reg].location = kRegisterIsExpression;
info.savedRegisters[reg].value = pos;
length = data.ReadUleb128(&pos);
pos += static_cast<size_t>(length);
break;
case DW_CFA_GNU_args_size:
offset = data.ReadUleb128(&pos);
info.spExtraArgSize = static_cast<uint32_t>(offset);
break;
case DW_CFA_GNU_negative_offset_extended:
reg = data.ReadUleb128(&pos);
if (reg >= kMaxRegisterNumber)
return false;
offset = data.ReadUleb128(&pos) * cie_->data_alignment_factor();
if (info.savedRegisters[reg].location != kRegisterUnused)
info.registerSavedMoreThanOnce = true;
info.savedRegisters[reg].location = kRegisterInCFA;
info.savedRegisters[reg].value = -offset;
break;
default:
operand = opcode & 0x3F;
switch ( opcode & 0xC0 ) {
case DW_CFA_offset:
reg = operand;
offset = data.ReadUleb128(&pos) * cie_->data_alignment_factor();
if (info.savedRegisters[reg].location != kRegisterUnused) {
/*
if ( (pcoffset == (pint_t)(-1))
&& (results->savedRegisters[reg].location == kRegisterInCFA)
&& (results->savedRegisters[reg].value == offset) )
results->registerSavedTwiceInCIE = reg;
else
results->registerSavedMoreThanOnce = true;
*/
}
info.savedRegisters[reg].location = kRegisterInCFA;
info.savedRegisters[reg].value = offset;
break;
case DW_CFA_advance_loc:
code_offset += operand * cie_->code_alignment_factor();
break;
case DW_CFA_restore:
reg = operand;
info.savedRegisters[reg] = initial_state.savedRegisters[reg];
break;
default:
return false;
}
}
}
return true;
}
uint32_t DwarfParser::GetRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool &failure)
{
if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 32) ) {
failure = true;
return 0;
}
unsigned int slotIndex = regOffsetFromBaseOffset/8;
switch ( reg ) {
case UNW_X86_64_RBX:
return UNWIND_X86_64_REG_RBX << (slotIndex*3);
case UNW_X86_64_R12:
return UNWIND_X86_64_REG_R12 << (slotIndex*3);
case UNW_X86_64_R13:
return UNWIND_X86_64_REG_R13 << (slotIndex*3);
case UNW_X86_64_R14:
return UNWIND_X86_64_REG_R14 << (slotIndex*3);
case UNW_X86_64_R15:
return UNWIND_X86_64_REG_R15 << (slotIndex*3);
}
// invalid register
failure = true;
return 0;
}
uint32_t DwarfParser::CreateCompactEncodingForX64(IArchitecture &file, uint64_t start, PrologInfo &prolog)
{
if (prolog.registerSavedTwiceInCIE == DW_X86_64_RET_ADDR)
return UNWIND_X86_64_MODE_DWARF;
// don't create compact unwind info for unsupported dwarf kinds
if (prolog.registerSavedMoreThanOnce)
return UNWIND_X86_64_MODE_DWARF;
if (prolog.cfaOffsetWasNegative)
return UNWIND_X86_64_MODE_DWARF;
if (prolog.spExtraArgSize != 0)
return UNWIND_X86_64_MODE_DWARF;
if (prolog.sameValueUsed)
return UNWIND_X86_64_MODE_DWARF;
// figure out which kind of frame this function uses
bool standardRBPframe = (
(prolog.cfaRegister == UNW_X86_64_RBP)
&& (prolog.cfaRegisterOffset == 16)
&& (prolog.savedRegisters[UNW_X86_64_RBP].location == kRegisterInCFA)
&& (prolog.savedRegisters[UNW_X86_64_RBP].value == -16) );
bool standardRSPframe = (prolog.cfaRegister == UNW_X86_64_RSP);
if (!standardRBPframe && !standardRSPframe)
return UNWIND_X86_64_MODE_DWARF;
// scan which registers are saved
int saveRegisterCount = 0;
bool rbxSaved = false;
bool r12Saved = false;
bool r13Saved = false;
bool r14Saved = false;
bool r15Saved = false;
bool rbpSaved = false;
for (int i=0; i < 64; ++i) {
if ( prolog.savedRegisters[i].location != kRegisterUnused ) {
if ( prolog.savedRegisters[i].location != kRegisterInCFA )
return UNWIND_X86_64_MODE_DWARF;
switch (i) {
case UNW_X86_64_RBX:
rbxSaved = true;
++saveRegisterCount;
break;
case UNW_X86_64_R12:
r12Saved = true;
++saveRegisterCount;
break;
case UNW_X86_64_R13:
r13Saved = true;
++saveRegisterCount;
break;
case UNW_X86_64_R14:
r14Saved = true;
++saveRegisterCount;
break;
case UNW_X86_64_R15:
r15Saved = true;
++saveRegisterCount;
break;
case UNW_X86_64_RBP:
rbpSaved = true;
++saveRegisterCount;
break;
case DW_X86_64_RET_ADDR:
break;
default:
return UNWIND_X86_64_MODE_DWARF;
}
}
}
const int64_t cfaOffsetRBX = prolog.savedRegisters[UNW_X86_64_RBX].value;
const int64_t cfaOffsetR12 = prolog.savedRegisters[UNW_X86_64_R12].value;
const int64_t cfaOffsetR13 = prolog.savedRegisters[UNW_X86_64_R13].value;
const int64_t cfaOffsetR14 = prolog.savedRegisters[UNW_X86_64_R14].value;
const int64_t cfaOffsetR15 = prolog.savedRegisters[UNW_X86_64_R15].value;
const int64_t cfaOffsetRBP = prolog.savedRegisters[UNW_X86_64_RBP].value;
// encode standard RBP frames
uint32_t encoding = 0;
if ( standardRBPframe ) {
// | |
// +--------------+ <- CFA
// | ret addr |
// +--------------+
// | rbp |
// +--------------+ <- rbp
// ~ ~
// +--------------+
// | saved reg3 |
// +--------------+ <- CFA - offset+16
// | saved reg2 |
// +--------------+ <- CFA - offset+8
// | saved reg1 |
// +--------------+ <- CFA - offset
// | |
// +--------------+
// | |
// <- rsp
//
encoding = UNWIND_X86_64_MODE_RBP_FRAME;
// find save location of farthest register from rbp
int64_t furthestCfaOffset = 0;
if ( rbxSaved & (cfaOffsetRBX < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetRBX;
if ( r12Saved & (cfaOffsetR12 < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetR12;
if ( r13Saved & (cfaOffsetR13 < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetR13;
if ( r14Saved & (cfaOffsetR14 < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetR14;
if ( r15Saved & (cfaOffsetR15 < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetR15;
if ( furthestCfaOffset == 0 ) {
// no registers saved, nothing more to encode
return encoding;
}
// add stack offset to encoding
int64_t rbpOffset = furthestCfaOffset + 16;
int64_t encodedOffset = rbpOffset/(-8);
if ( encodedOffset > 255 )
return UNWIND_X86_64_MODE_DWARF;
encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_64_RBP_FRAME_OFFSET));
// add register saved from each stack location
bool encodingFailure = false;
if ( rbxSaved )
encoding |= GetRBPEncodedRegister(UNW_X86_64_RBX, static_cast<int32_t>(cfaOffsetRBX - furthestCfaOffset), encodingFailure);
if ( r12Saved )
encoding |= GetRBPEncodedRegister(UNW_X86_64_R12, static_cast<int32_t>(cfaOffsetR12 - furthestCfaOffset), encodingFailure);
if ( r13Saved )
encoding |= GetRBPEncodedRegister(UNW_X86_64_R13, static_cast<int32_t>(cfaOffsetR13 - furthestCfaOffset), encodingFailure);
if ( r14Saved )
encoding |= GetRBPEncodedRegister(UNW_X86_64_R14, static_cast<int32_t>(cfaOffsetR14 - furthestCfaOffset), encodingFailure);
if ( r15Saved )
encoding |= GetRBPEncodedRegister(UNW_X86_64_R15, static_cast<int32_t>(cfaOffsetR15 - furthestCfaOffset), encodingFailure);
if ( encodingFailure )
return UNWIND_X86_64_MODE_DWARF;
return encoding;
}
else {
// | |
// +--------------+ <- CFA
// | ret addr |
// +--------------+
// | saved reg1 |
// +--------------+ <- CFA - 16
// | saved reg2 |
// +--------------+ <- CFA - 24
// | saved reg3 |
// +--------------+ <- CFA - 32
// | saved reg4 |
// +--------------+ <- CFA - 40
// | saved reg5 |
// +--------------+ <- CFA - 48
// | saved reg6 |
// +--------------+ <- CFA - 56
// | |
// <- esp
//
// for RSP based frames we need to encode stack size in unwind info
encoding = UNWIND_X86_64_MODE_STACK_IMMD;
uint64_t stackValue = prolog.cfaRegisterOffset / 8;
uint32_t stackAdjust = 0;
bool immedStackSize = true;
if ( stackValue > (UNWIND_X86_64_FRAMELESS_STACK_SIZE >> __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_SIZE)) ) {
// stack size is too big to fit as an immediate value, so encode offset of subq instruction in function
if (prolog.codeOffsetAtStackDecrement == 0)
return UNWIND_X86_64_MODE_DWARF;
uint64_t functionContentAdjustStackIns = start + prolog.codeOffsetAtStackDecrement - 4;
uint64_t pos = file.Tell();
if (!file.AddressSeek(functionContentAdjustStackIns))
return UNWIND_X86_64_MODE_DWARF;
uint32_t stackDecrementInCode = file.ReadDWord();
file.Seek(pos);
stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8;
stackValue = functionContentAdjustStackIns - start;
immedStackSize = false;
if ( stackAdjust > 7 )
return UNWIND_X86_64_MODE_DWARF;
encoding = UNWIND_X86_64_MODE_STACK_IND;
}
// validate that saved registers are all within 6 slots abutting return address
int registers[6];
for (int i=0; i < 6;++i)
registers[i] = 0;
if ( r15Saved ) {
if ( cfaOffsetR15 < -56 )
return UNWIND_X86_64_MODE_DWARF;
registers[(cfaOffsetR15+56)/8] = UNWIND_X86_64_REG_R15;
}
if ( r14Saved ) {
if ( cfaOffsetR14 < -56 )
return UNWIND_X86_64_MODE_DWARF;
registers[(cfaOffsetR14+56)/8] = UNWIND_X86_64_REG_R14;
}
if ( r13Saved ) {
if ( cfaOffsetR13 < -56 )
return UNWIND_X86_64_MODE_DWARF;
registers[(cfaOffsetR13+56)/8] = UNWIND_X86_64_REG_R13;
}
if ( r12Saved ) {
if ( cfaOffsetR12 < -56 )
return UNWIND_X86_64_MODE_DWARF;
registers[(cfaOffsetR12+56)/8] = UNWIND_X86_64_REG_R12;
}
if ( rbxSaved ) {
if ( cfaOffsetRBX < -56 )
return UNWIND_X86_64_MODE_DWARF;
registers[(cfaOffsetRBX+56)/8] = UNWIND_X86_64_REG_RBX;
}
if ( rbpSaved ) {
if ( cfaOffsetRBP < -56 )
return UNWIND_X86_64_MODE_DWARF;
registers[(cfaOffsetRBP+56)/8] = UNWIND_X86_64_REG_RBP;
}
// validate that saved registers are contiguous and abut return address on stack
for (int i=0; i < saveRegisterCount; ++i) {
if ( registers[5-i] == 0 ) {
return UNWIND_X86_64_MODE_DWARF;
}
}
// encode register permutation
// the 10-bits are encoded differently depending on the number of registers saved
int renumregs[6];
for (int i=6-saveRegisterCount; i < 6; ++i) {
int countless = 0;
for (int j=6-saveRegisterCount; j < i; ++j) {
if ( registers[j] < registers[i] )
++countless;
}
renumregs[i] = registers[i] - countless -1;
}
uint32_t permutationEncoding = 0;
switch ( saveRegisterCount ) {
case 6:
permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]);
break;
case 5:
permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]);
break;
case 4:
permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]);
break;
case 3:
permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]);
break;
case 2:
permutationEncoding |= (5*renumregs[4] + renumregs[5]);
break;
case 1:
permutationEncoding |= (renumregs[5]);
break;
}
encoding |= (stackValue << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_SIZE));
encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_ADJUST));
encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT));
encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION));
return encoding;
}
}
uint32_t DwarfParser::GetEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure)
{
if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 16) ) {
failure = true;
return 0;
}
unsigned int slotIndex = regOffsetFromBaseOffset/4;
switch ( reg ) {
case UNW_X86_EBX:
return UNWIND_X86_REG_EBX << (slotIndex*3);
case UNW_X86_ECX:
return UNWIND_X86_REG_ECX << (slotIndex*3);
case UNW_X86_EDX:
return UNWIND_X86_REG_EDX << (slotIndex*3);
case UNW_X86_EDI:
return UNWIND_X86_REG_EDI << (slotIndex*3);
case UNW_X86_ESI:
return UNWIND_X86_REG_ESI << (slotIndex*3);
}
// invalid register
failure = true;
return 0;
}
uint32_t DwarfParser::CreateCompactEncodingForX32(IArchitecture &file, uint64_t start, PrologInfo &prolog)
{
if ( prolog.registerSavedTwiceInCIE == DW_X86_RET_ADDR )
return UNWIND_X86_64_MODE_DWARF;
// don't create compact unwind info for unsupported dwarf kinds
if ( prolog.registerSavedMoreThanOnce )
return UNWIND_X86_MODE_DWARF;
if ( prolog.spExtraArgSize != 0 )
return UNWIND_X86_MODE_DWARF;
if ( prolog.sameValueUsed )
return UNWIND_X86_MODE_DWARF;
// figure out which kind of frame this function uses
bool standardEBPframe = (
(prolog.cfaRegister == UNW_X86_EBP)
&& (prolog.cfaRegisterOffset == 8)
&& (prolog.savedRegisters[UNW_X86_EBP].location == kRegisterInCFA)
&& (prolog.savedRegisters[UNW_X86_EBP].value == -8) );
bool standardESPframe = (prolog.cfaRegister == UNW_X86_ESP);
if ( !standardEBPframe && !standardESPframe ) {
// no compact encoding for this
return UNWIND_X86_MODE_DWARF;
}
// scan which registers are saved
int saveRegisterCount = 0;
bool ebxSaved = false;
bool ecxSaved = false;
bool edxSaved = false;
bool esiSaved = false;
bool ediSaved = false;
bool ebpSaved = false;
for (int i=0; i < 64; ++i) {
if ( prolog.savedRegisters[i].location != kRegisterUnused ) {
if ( prolog.savedRegisters[i].location != kRegisterInCFA )
return UNWIND_X86_MODE_DWARF;
switch (i) {
case UNW_X86_EBX:
ebxSaved = true;
++saveRegisterCount;
break;
case UNW_X86_ECX:
ecxSaved = true;
++saveRegisterCount;
break;
case UNW_X86_EDX:
edxSaved = true;
++saveRegisterCount;
break;
case UNW_X86_ESI:
esiSaved = true;
++saveRegisterCount;
break;
case UNW_X86_EDI:
ediSaved = true;
++saveRegisterCount;
break;
case UNW_X86_EBP:
ebpSaved = true;
++saveRegisterCount;
break;
case DW_X86_RET_ADDR:
break;
default:
return UNWIND_X86_MODE_DWARF;
}
}
}
const int32_t cfaOffsetEBX = static_cast<int32_t>(prolog.savedRegisters[UNW_X86_EBX].value);
const int32_t cfaOffsetECX = static_cast<int32_t>(prolog.savedRegisters[UNW_X86_ECX].value);
const int32_t cfaOffsetEDX = static_cast<int32_t>(prolog.savedRegisters[UNW_X86_EDX].value);
const int32_t cfaOffsetEDI = static_cast<int32_t>(prolog.savedRegisters[UNW_X86_EDI].value);
const int32_t cfaOffsetESI = static_cast<int32_t>(prolog.savedRegisters[UNW_X86_ESI].value);
const int32_t cfaOffsetEBP = static_cast<int32_t>(prolog.savedRegisters[UNW_X86_EBP].value);
// encode standard RBP frames
uint32_t encoding = 0;
if ( standardEBPframe ) {
// | |
// +--------------+ <- CFA
// | ret addr |
// +--------------+
// | ebp |
// +--------------+ <- ebp
// ~ ~
// +--------------+
// | saved reg3 |
// +--------------+ <- CFA - offset+8
// | saved reg2 |
// +--------------+ <- CFA - offset+e
// | saved reg1 |
// +--------------+ <- CFA - offset
// | |
// +--------------+
// | |
// <- esp
//
encoding = UNWIND_X86_MODE_EBP_FRAME;
// find save location of farthest register from ebp
int32_t furthestCfaOffset = 0;
if ( ebxSaved & (cfaOffsetEBX < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetEBX;
if ( ecxSaved & (cfaOffsetECX < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetECX;
if ( edxSaved & (cfaOffsetEDX < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetEDX;
if ( ediSaved & (cfaOffsetEDI < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetEDI;
if ( esiSaved & (cfaOffsetESI < furthestCfaOffset) )
furthestCfaOffset = cfaOffsetESI;
if ( furthestCfaOffset == 0 ) {
// no registers saved, nothing more to encode
return encoding;
}
// add stack offset to encoding
int ebpOffset = furthestCfaOffset + 8;
int encodedOffset = ebpOffset/(-4);
if ( encodedOffset > 255 )
return UNWIND_X86_MODE_DWARF;
encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_EBP_FRAME_OFFSET));
// add register saved from each stack location
bool encodingFailure = false;
if ( ebxSaved )
encoding |= GetEBPEncodedRegister(UNW_X86_EBX, cfaOffsetEBX - furthestCfaOffset, encodingFailure);
if ( ecxSaved )
encoding |= GetEBPEncodedRegister(UNW_X86_ECX, cfaOffsetECX - furthestCfaOffset, encodingFailure);
if ( edxSaved )
encoding |= GetEBPEncodedRegister(UNW_X86_EDX, cfaOffsetEDX - furthestCfaOffset, encodingFailure);
if ( ediSaved )
encoding |= GetEBPEncodedRegister(UNW_X86_EDI, cfaOffsetEDI - furthestCfaOffset, encodingFailure);
if ( esiSaved )
encoding |= GetEBPEncodedRegister(UNW_X86_ESI, cfaOffsetESI - furthestCfaOffset, encodingFailure);
if ( encodingFailure )
return UNWIND_X86_MODE_DWARF;
return encoding;
}
else {
// | |
// +--------------+ <- CFA
// | ret addr |
// +--------------+
// | saved reg1 |
// +--------------+ <- CFA - 8
// | saved reg2 |
// +--------------+ <- CFA - 12
// | saved reg3 |
// +--------------+ <- CFA - 16
// | saved reg4 |
// +--------------+ <- CFA - 20
// | saved reg5 |
// +--------------+ <- CFA - 24
// | saved reg6 |
// +--------------+ <- CFA - 28
// | |
// <- esp
//
// for ESP based frames we need to encode stack size in unwind info
encoding = UNWIND_X86_MODE_STACK_IMMD;
uint64_t stackValue = prolog.cfaRegisterOffset / 4;
uint32_t stackAdjust = 0;
bool immedStackSize = true;
if ( stackValue > (UNWIND_X86_FRAMELESS_STACK_SIZE >> __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_SIZE)) ) {
// stack size is too big to fit as an immediate value, so encode offset of subq instruction in function
uint64_t functionContentAdjustStackIns = start + prolog.codeOffsetAtStackDecrement - 4;
uint64_t pos = file.Tell();
if (!file.AddressSeek(functionContentAdjustStackIns))
return UNWIND_X86_64_MODE_DWARF;
uint32_t stackDecrementInCode = file.ReadDWord();
file.Seek(pos);
stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4;
stackValue = functionContentAdjustStackIns - start;
immedStackSize = false;
if ( stackAdjust > 7 )
return UNWIND_X86_MODE_DWARF;
encoding = UNWIND_X86_MODE_STACK_IND;
}
// validate that saved registers are all within 6 slots abutting return address
int registers[6];
for (int i=0; i < 6;++i)
registers[i] = 0;
if ( ebxSaved ) {
if ( cfaOffsetEBX < -28 )
return UNWIND_X86_MODE_DWARF;
registers[(cfaOffsetEBX+28)/4] = UNWIND_X86_REG_EBX;
}
if ( ecxSaved ) {
if ( cfaOffsetECX < -28 )
return UNWIND_X86_MODE_DWARF;
registers[(cfaOffsetECX+28)/4] = UNWIND_X86_REG_ECX;
}
if ( edxSaved ) {
if ( cfaOffsetEDX < -28 )
return UNWIND_X86_MODE_DWARF;
registers[(cfaOffsetEDX+28)/4] = UNWIND_X86_REG_EDX;
}
if ( ediSaved ) {
if ( cfaOffsetEDI < -28 )
return UNWIND_X86_MODE_DWARF;
registers[(cfaOffsetEDI+28)/4] = UNWIND_X86_REG_EDI;
}
if ( esiSaved ) {
if ( cfaOffsetESI < -28 )
return UNWIND_X86_MODE_DWARF;
registers[(cfaOffsetESI+28)/4] = UNWIND_X86_REG_ESI;
}
if ( ebpSaved ) {
if ( cfaOffsetEBP < -28 )
return UNWIND_X86_MODE_DWARF;
registers[(cfaOffsetEBP+28)/4] = UNWIND_X86_REG_EBP;
}
// validate that saved registers are contiguous and abut return address on stack
for (int i=0; i < saveRegisterCount; ++i) {
if ( registers[5-i] == 0 )
return UNWIND_X86_MODE_DWARF;
}
// encode register permutation
// the 10-bits are encoded differently depending on the number of registers saved
int renumregs[6];
for (int i=6-saveRegisterCount; i < 6; ++i) {
int countless = 0;
for (int j=6-saveRegisterCount; j < i; ++j) {
if ( registers[j] < registers[i] )
++countless;
}
renumregs[i] = registers[i] - countless -1;
}
uint32_t permutationEncoding = 0;
switch ( saveRegisterCount ) {
case 6:
permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]);
break;
case 5:
permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]);
break;
case 4:
permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]);
break;
case 3:
permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]);
break;
case 2:
permutationEncoding |= (5*renumregs[4] + renumregs[5]);
break;
case 1:
permutationEncoding |= (renumregs[5]);
break;
}
encoding |= (stackValue << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_SIZE));
encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_ADJUST));
encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_COUNT));
encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION));
return encoding;
}
}