static bool ReadPointer(const libunwindInfo* info, unw_word_t* addr, unw_word_t* valp) { #ifdef BIT64 uint64_t val64; if (ReadValue64(info, addr, &val64)) { *valp = val64; return true; } #else uint32_t val32; if (ReadValue32(info, addr, &val32)) { *valp = val32; return true; } #endif return false; }
void DisassemblerCore::Disassemble() { // Skip some header information that is already included in the validation result. Skip((2 * sizeof(Value32)) + (7 * sizeof(Value16))); mOutStream.Print("Version %" BOND_PRIu32 ".%" BOND_PRIu32 "\n", mValidationResult.mMajorVersion, mValidationResult.mMinorVersion); mOutStream.Print("Pointer size: %d bits\n", (mValidationResult.mPointerSize == POINTER_64BIT) ? 64 : 32); for (size_t i = 0; i < mValidationResult.mValue32Count; ++i) { mValue32Table[i] = ReadValue32(); } for (size_t i = 0; i < mValidationResult.mValue64Count; ++i) { mValue64Table[i] = ReadValue64(); } char *stringBytes = mStringBytes.data(); for (size_t i = 0; i < mValidationResult.mStringCount; ++i) { const size_t length = ReadValue16().mUShort; mStringTable[i] = StringView(stringBytes, length); mCboStream.Read(stringBytes, length); stringBytes += length; *stringBytes++ = '\0'; } QualifiedName *name = mQualifiedNameTable.data(); const char **element = mQualifiedNameElementTable.data(); for (size_t i = 0; i < mValidationResult.mQualifiedNameCount; ++i) { *name++ = QualifiedName(element); const size_t numElements = ReadValue16().mUShort; for (size_t j = 0; j < numElements; ++j) { const size_t elementIndex = ReadValue16().mUShort; *element++ = mStringTable[elementIndex].GetString(); } *element++ = nullptr; } DisassembleBlob(); }
static bool LookupTableEntry(const libunwindInfo* info, int32_t ip, unw_word_t tableAddr, size_t tableCount, table_entry* entry, bool* found) { size_t low, high, mid; unw_word_t addr; int32_t start_ip; *found = false; // do a binary search on table for (low = 0, high = tableCount; low < high;) { mid = (low + high) / 2; addr = tableAddr + (mid * sizeof(table_entry)); if (!ReadValue32(info, &addr, (uint32_t*)&start_ip)) { return false; } if (ip < start_ip) { high = mid; } else { low = mid + 1; } } if (high > 0) { addr = tableAddr + ((high - 1) * sizeof(table_entry)); // Assumes that the table_entry is two 32 bit values _ASSERTE(sizeof(*entry) == sizeof(uint64_t)); if (!ReadValue64(info, &addr, (uint64_t*)entry)) { return false; } *found = true; } return true; }
static bool ExtractProcInfoFromFde(const libunwindInfo* info, unw_word_t* addrp, unw_proc_info_t *pip, int need_unwind_info) { unw_word_t addr = *addrp, fdeEndAddr, cieOffsetAddr, cieAddr; uint32_t value32; uint64_t value64; if (!ReadValue32(info, &addr, &value32)) { return false; } if (value32 != 0xffffffff) { int32_t cieOffset = 0; // In some configurations, an FDE with a 0 length indicates the end of the FDE-table if (value32 == 0) { return false; } // the FDE is in the 32-bit DWARF format */ *addrp = fdeEndAddr = addr + value32; cieOffsetAddr = addr; if (!ReadValue32(info, &addr, (uint32_t*)&cieOffset)) { return false; } // Ignore CIEs (happens during linear search) if (cieOffset == 0) { return true; } // DWARF says that the CIE_pointer in the FDE is a .debug_frame-relative offset, // but the GCC-generated .eh_frame sections instead store a "pcrelative" offset, // which is just as fine as it's self-contained cieAddr = cieOffsetAddr - cieOffset; } else { int64_t cieOffset = 0; // the FDE is in the 64-bit DWARF format */ if (!ReadValue64(info, &addr, (uint64_t*)&value64)) { return false; } *addrp = fdeEndAddr = addr + value64; cieOffsetAddr = addr; if (!ReadValue64(info, &addr, (uint64_t*)&cieOffset)) { return false; } // Ignore CIEs (happens during linear search) if (cieOffset == 0) { return true; } // DWARF says that the CIE_pointer in the FDE is a .debug_frame-relative offset, // but the GCC-generated .eh_frame sections instead store a "pcrelative" offset, // which is just as fine as it's self-contained cieAddr = (unw_word_t)((uint64_t)cieOffsetAddr - cieOffset); } dwarf_cie_info_t dci; if (!ParseCie(info, cieAddr, &dci)) { return false; } unw_word_t ipStart, ipRange; if (!ReadEncodedPointer(info, &addr, dci.fde_encoding, UINTPTR_MAX, &ipStart)) { return false; } // IP-range has same encoding as FDE pointers, except that it's always an absolute value uint8_t ipRangeEncoding = dci.fde_encoding & DW_EH_PE_FORMAT_MASK; if (!ReadEncodedPointer(info, &addr, ipRangeEncoding, UINTPTR_MAX, &ipRange)) { return false; } pip->start_ip = ipStart; pip->end_ip = ipStart + ipRange; pip->handler = dci.handler; unw_word_t augmentationSize, augmentationEndAddr; if (dci.sized_augmentation) { if (!ReadULEB128(info, &addr, &augmentationSize)) { return false; } augmentationEndAddr = addr + augmentationSize; } // Read language specific data area address if (!ReadEncodedPointer(info, &addr, dci.lsda_encoding, pip->start_ip, &pip->lsda)) { return false; } // Now fill out the proc info if requested if (need_unwind_info) { if (dci.have_abi_marker) { if (!ReadValue16(info, &addr, &dci.abi)) { return false; } if (!ReadValue16(info, &addr, &dci.tag)) { return false; } } if (dci.sized_augmentation) { dci.fde_instr_start = augmentationEndAddr; } else { dci.fde_instr_start = addr; } dci.fde_instr_end = fdeEndAddr; pip->format = UNW_INFO_FORMAT_TABLE; pip->unwind_info_size = sizeof(dci); pip->unwind_info = malloc(sizeof(dci)); if (pip->unwind_info == nullptr) { return -UNW_ENOMEM; } memcpy(pip->unwind_info, &dci, sizeof(dci)); } return true; }
static bool ParseCie(const libunwindInfo* info, unw_word_t addr, dwarf_cie_info_t* dci) { uint8_t ch, version, fdeEncoding, handlerEncoding; unw_word_t cieLength, cieEndAddr; uint32_t value32; uint64_t value64; memset(dci, 0, sizeof (*dci)); // Pick appropriate default for FDE-encoding. DWARF spec says // start-IP (initial_location) and the code-size (address_range) are // "address-unit sized constants". The `R' augmentation can be used // to override this, but by default, we pick an address-sized unit // for fde_encoding. #if BIT64 fdeEncoding = DW_EH_PE_udata8; #else fdeEncoding = DW_EH_PE_udata4; #endif dci->lsda_encoding = DW_EH_PE_omit; dci->handler = 0; if (!ReadValue32(info, &addr, &value32)) { return false; } if (value32 != 0xffffffff) { // The CIE is in the 32-bit DWARF format uint32_t cieId; // DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 const uint32_t expectedId = 0; cieLength = value32; cieEndAddr = addr + cieLength; if (!ReadValue32(info, &addr, &cieId)) { return false; } if (cieId != expectedId) { ASSERT("ParseCie: unexpected cie id %x\n", cieId); return false; } } else { // The CIE is in the 64-bit DWARF format uint64_t cieId; // DWARF says CIE id should be 0xffffffffffffffff, but in .eh_frame, it's 0 const uint64_t expectedId = 0; if (!ReadValue64(info, &addr, &value64)) { return false; } cieLength = value64; cieEndAddr = addr + cieLength; if (!ReadValue64(info, &addr, &cieId)) { return false; } if (cieId != expectedId) { ASSERT("ParseCie: unexpected cie id %lx\n", cieId); return false; } } dci->cie_instr_end = cieEndAddr; if (!ReadValue8(info, &addr, &version)) { return false; } if (version != 1 && version != DWARF_CIE_VERSION) { ASSERT("ParseCie: invalid cie version %x\n", version); return false; } // Read the augmentation string uint8_t augmentationString[8]; memset(augmentationString, 0, sizeof(augmentationString)); for (int i = 0; i < sizeof(augmentationString); i++) { if (!ReadValue8(info, &addr, &ch)) { return false; } if (ch == 0) { break; } augmentationString[i] = ch; } // Read the code and data alignment if (!ReadULEB128(info, &addr, &dci->code_align)) { return false; } if (!ReadSLEB128(info, &addr, &dci->data_align)) { return false; } // Read the return-address column either as a u8 or as a uleb128 if (version == 1) { if (!ReadValue8(info, &addr, &ch)) { return false; } dci->ret_addr_column = ch; } else { if (!ReadULEB128(info, &addr, &dci->ret_addr_column)) { return false; } } // Parse the augmentation string for (int i = 0; i < sizeof(augmentationString); i++) { bool done = false; unw_word_t augmentationSize; switch (augmentationString[i]) { case '\0': done = true; break; case 'z': dci->sized_augmentation = 1; if (!ReadULEB128(info, &addr, &augmentationSize)) { return false; } break; case 'L': // read the LSDA pointer-encoding format if (!ReadValue8(info, &addr, &ch)) { return false; } dci->lsda_encoding = ch; break; case 'R': // read the FDE pointer-encoding format if (!ReadValue8(info, &addr, &fdeEncoding)) { return false; } break; case 'P': // read the personality-routine pointer-encoding format if (!ReadValue8(info, &addr, &handlerEncoding)) { return false; } if (!ReadEncodedPointer(info, &addr, handlerEncoding, UINTPTR_MAX, &dci->handler)) { return false; } break; case 'S': // This is a signal frame dci->signal_frame = 1; // Temporarily set it to one so dwarf_parse_fde() knows that // it should fetch the actual ABI/TAG pair from the FDE. dci->have_abi_marker = 1; break; default: if (dci->sized_augmentation) { // If we have the size of the augmentation body, we can skip // over the parts that we don't understand, so we're OK done = true; break; } ASSERT("ParseCie: unexpected argumentation string '%s'\n", augmentationString[i]); return false; } if (done) { break; } } dci->fde_encoding = fdeEncoding; dci->cie_instr_start = addr; return true; }
static bool ReadEncodedPointer(const libunwindInfo* info, unw_word_t* addr, unsigned char encoding, unw_word_t funcRel, unw_word_t* valp) { unw_word_t initialAddr = *addr; uint16_t value16; uint32_t value32; uint64_t value64; unw_word_t value; if (encoding == DW_EH_PE_omit) { *valp = 0; return true; } else if (encoding == DW_EH_PE_aligned) { int size = sizeof(unw_word_t); *addr = (initialAddr + size - 1) & -size; return ReadPointer(info, addr, valp); } switch (encoding & DW_EH_PE_FORMAT_MASK) { case DW_EH_PE_ptr: if (!ReadPointer(info, addr, &value)) { return false; } break; case DW_EH_PE_uleb128: if (!ReadULEB128(info, addr, &value)) { return false; } break; case DW_EH_PE_sleb128: if (!ReadSLEB128(info, addr, &value)) { return false; } break; case DW_EH_PE_udata2: if (!ReadValue16(info, addr, &value16)) { return false; } value = value16; break; case DW_EH_PE_udata4: if (!ReadValue32(info, addr, &value32)) { return false; } value = value32; break; case DW_EH_PE_udata8: if (!ReadValue64(info, addr, &value64)) { return false; } value = value64; break; case DW_EH_PE_sdata2: if (!ReadValue16(info, addr, &value16)) { return false; } value = (int16_t)value16; break; case DW_EH_PE_sdata4: if (!ReadValue32(info, addr, &value32)) { return false; } value = (int32_t)value32; break; case DW_EH_PE_sdata8: if (!ReadValue64(info, addr, &value64)) { return false; } value = (int64_t)value64; break; default: ASSERT("ReadEncodedPointer: invalid encoding format %x\n", encoding); return false; } // 0 is a special value and always absolute if (value == 0) { *valp = 0; return true; } switch (encoding & DW_EH_PE_APPL_MASK) { case DW_EH_PE_absptr: break; case DW_EH_PE_pcrel: value += initialAddr; break; case DW_EH_PE_funcrel: _ASSERTE(funcRel != UINTPTR_MAX); value += funcRel; break; case DW_EH_PE_textrel: case DW_EH_PE_datarel: default: ASSERT("ReadEncodedPointer: invalid application type %x\n", encoding); return false; } if (encoding & DW_EH_PE_indirect) { unw_word_t indirect_addr = value; if (!ReadPointer(info, &indirect_addr, &value)) { return false; } } *valp = value; return true; }