diff options
author | jmpoep <OriginalEntryPoint@qq.com> | 2023-12-07 16:51:07 +0800 |
---|---|---|
committer | jmpoep <OriginalEntryPoint@qq.com> | 2023-12-07 16:51:07 +0800 |
commit | 28008a746a31abb7909dd86cb0cd413ac8943b0b (patch) | |
tree | a30b74b8cad548048c3c1551d652828ab76fa9bd /core/dwarf.cc | |
download | vmprotect-3.5.1-master.tar vmprotect-3.5.1-master.tar.gz vmprotect-3.5.1-master.tar.bz2 vmprotect-3.5.1-master.zip |
Diffstat (limited to 'core/dwarf.cc')
-rw-r--r-- | core/dwarf.cc | 1188 |
1 files changed, 1188 insertions, 0 deletions
diff --git a/core/dwarf.cc b/core/dwarf.cc new file mode 100644 index 0000000..6420601 --- /dev/null +++ b/core/dwarf.cc @@ -0,0 +1,1188 @@ +#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; + } +}
\ No newline at end of file |