brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.4 KiB · 4b516b2 Raw
534 lines · cpp
1#include "PECallFrameInfo.h"2 3#include "ObjectFilePECOFF.h"4 5#include "Plugins/Process/Utility/lldb-x86-register-enums.h"6#include "lldb/Symbol/UnwindPlan.h"7#include "llvm/Support/Win64EH.h"8 9using namespace lldb;10using namespace lldb_private;11using namespace llvm::Win64EH;12 13template <typename T>14static const T *TypedRead(const DataExtractor &data_extractor, offset_t &offset,15                          offset_t size = sizeof(T)) {16  return static_cast<const T *>(data_extractor.GetData(&offset, size));17}18 19struct EHInstruction {20  enum class Type {21    PUSH_REGISTER,22    ALLOCATE,23    SET_FRAME_POINTER_REGISTER,24    SAVE_REGISTER25  };26 27  uint8_t offset;28  Type type;29  uint32_t reg;30  uint32_t frame_offset;31};32 33using EHProgram = std::vector<EHInstruction>;34 35class UnwindCodesIterator {36public:37  UnwindCodesIterator(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva);38 39  bool GetNext();40  bool IsError() const { return m_error; }41 42  const UnwindInfo *GetUnwindInfo() const { return m_unwind_info; }43  const UnwindCode *GetUnwindCode() const { return m_unwind_code; }44  bool IsChained() const { return m_chained; }45 46private:47  ObjectFilePECOFF &m_object_file;48 49  bool m_error = false;50 51  uint32_t m_unwind_info_rva;52  DataExtractor m_unwind_info_data;53  const UnwindInfo *m_unwind_info = nullptr;54 55  DataExtractor m_unwind_code_data;56  offset_t m_unwind_code_offset;57  const UnwindCode *m_unwind_code = nullptr;58 59  bool m_chained = false;60};61 62UnwindCodesIterator::UnwindCodesIterator(ObjectFilePECOFF &object_file,63                                         uint32_t unwind_info_rva)64    : m_object_file(object_file),65      m_unwind_info_rva(unwind_info_rva), m_unwind_code_offset{} {}66 67bool UnwindCodesIterator::GetNext() {68  static constexpr int UNWIND_INFO_SIZE = 4;69 70  m_error = false;71  m_unwind_code = nullptr;72  while (!m_unwind_code) {73    if (!m_unwind_info) {74      m_unwind_info_data =75          m_object_file.ReadImageDataByRVA(m_unwind_info_rva, UNWIND_INFO_SIZE);76 77      offset_t offset = 0;78      m_unwind_info =79          TypedRead<UnwindInfo>(m_unwind_info_data, offset, UNWIND_INFO_SIZE);80      if (!m_unwind_info) {81        m_error = true;82        break;83      }84 85      m_unwind_code_data = m_object_file.ReadImageDataByRVA(86          m_unwind_info_rva + UNWIND_INFO_SIZE,87          m_unwind_info->NumCodes * sizeof(UnwindCode));88      m_unwind_code_offset = 0;89    }90 91    if (m_unwind_code_offset < m_unwind_code_data.GetByteSize()) {92      m_unwind_code =93          TypedRead<UnwindCode>(m_unwind_code_data, m_unwind_code_offset);94      m_error = !m_unwind_code;95      break;96    }97 98    if (!(m_unwind_info->getFlags() & UNW_ChainInfo))99      break;100 101    uint32_t runtime_function_rva =102        m_unwind_info_rva + UNWIND_INFO_SIZE +103        ((m_unwind_info->NumCodes + 1) & ~1) * sizeof(UnwindCode);104    DataExtractor runtime_function_data = m_object_file.ReadImageDataByRVA(105        runtime_function_rva, sizeof(RuntimeFunction));106 107    offset_t offset = 0;108    const auto *runtime_function =109        TypedRead<RuntimeFunction>(runtime_function_data, offset);110    if (!runtime_function) {111      m_error = true;112      break;113    }114 115    m_unwind_info_rva = runtime_function->UnwindInfoOffset;116    m_unwind_info = nullptr;117    m_chained = true;118  }119 120  return !!m_unwind_code;121}122 123class EHProgramBuilder {124public:125  EHProgramBuilder(ObjectFilePECOFF &object_file, uint32_t unwind_info_rva);126 127  bool Build();128 129  const EHProgram &GetProgram() const { return m_program; }130 131private:132  static uint32_t ConvertMachineToLLDBRegister(uint8_t machine_reg);133  static uint32_t ConvertXMMToLLDBRegister(uint8_t xmm_reg);134 135  bool ProcessUnwindCode(UnwindCode code);136  void Finalize();137 138  bool ParseBigOrScaledFrameOffset(uint32_t &result, bool big, uint32_t scale);139  bool ParseBigFrameOffset(uint32_t &result);140  bool ParseFrameOffset(uint32_t &result);141 142  UnwindCodesIterator m_iterator;143  EHProgram m_program;144};145 146EHProgramBuilder::EHProgramBuilder(ObjectFilePECOFF &object_file,147                                   uint32_t unwind_info_rva)148    : m_iterator(object_file, unwind_info_rva) {}149 150bool EHProgramBuilder::Build() {151  while (m_iterator.GetNext())152    if (!ProcessUnwindCode(*m_iterator.GetUnwindCode()))153      return false;154 155  if (m_iterator.IsError())156    return false;157 158  Finalize();159 160  return true;161}162 163uint32_t EHProgramBuilder::ConvertMachineToLLDBRegister(uint8_t machine_reg) {164  static uint32_t machine_to_lldb_register[] = {165      lldb_rax_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64, lldb_rbx_x86_64,166      lldb_rsp_x86_64, lldb_rbp_x86_64, lldb_rsi_x86_64, lldb_rdi_x86_64,167      lldb_r8_x86_64,  lldb_r9_x86_64,  lldb_r10_x86_64, lldb_r11_x86_64,168      lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64};169 170  if (machine_reg >= std::size(machine_to_lldb_register))171    return LLDB_INVALID_REGNUM;172 173  return machine_to_lldb_register[machine_reg];174}175 176uint32_t EHProgramBuilder::ConvertXMMToLLDBRegister(uint8_t xmm_reg) {177  static uint32_t xmm_to_lldb_register[] = {178      lldb_xmm0_x86_64,  lldb_xmm1_x86_64,  lldb_xmm2_x86_64,179      lldb_xmm3_x86_64,  lldb_xmm4_x86_64,  lldb_xmm5_x86_64,180      lldb_xmm6_x86_64,  lldb_xmm7_x86_64,  lldb_xmm8_x86_64,181      lldb_xmm9_x86_64,  lldb_xmm10_x86_64, lldb_xmm11_x86_64,182      lldb_xmm12_x86_64, lldb_xmm13_x86_64, lldb_xmm14_x86_64,183      lldb_xmm15_x86_64};184 185  if (xmm_reg >= std::size(xmm_to_lldb_register))186    return LLDB_INVALID_REGNUM;187 188  return xmm_to_lldb_register[xmm_reg];189}190 191bool EHProgramBuilder::ProcessUnwindCode(UnwindCode code) {192  uint8_t o = m_iterator.IsChained() ? 0 : code.u.CodeOffset;193  uint8_t unwind_operation = code.getUnwindOp();194  uint8_t operation_info = code.getOpInfo();195 196  switch (unwind_operation) {197  case UOP_PushNonVol: {198    uint32_t r = ConvertMachineToLLDBRegister(operation_info);199    if (r == LLDB_INVALID_REGNUM)200      return false;201 202    m_program.emplace_back(203        EHInstruction{o, EHInstruction::Type::PUSH_REGISTER, r, 8});204 205    return true;206  }207  case UOP_AllocLarge: {208    uint32_t fo;209    if (!ParseBigOrScaledFrameOffset(fo, operation_info, 8))210      return false;211 212    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE,213                                         LLDB_INVALID_REGNUM, fo});214 215    return true;216  }217  case UOP_AllocSmall: {218    m_program.emplace_back(219        EHInstruction{o, EHInstruction::Type::ALLOCATE, LLDB_INVALID_REGNUM,220                      static_cast<uint32_t>(operation_info) * 8 + 8});221    return true;222  }223  case UOP_SetFPReg: {224    uint32_t fpr = LLDB_INVALID_REGNUM;225    if (m_iterator.GetUnwindInfo()->getFrameRegister())226      fpr = ConvertMachineToLLDBRegister(227          m_iterator.GetUnwindInfo()->getFrameRegister());228    if (fpr == LLDB_INVALID_REGNUM)229      return false;230 231    uint32_t fpro =232        static_cast<uint32_t>(m_iterator.GetUnwindInfo()->getFrameOffset()) *233        16;234 235    m_program.emplace_back(EHInstruction{236        o, EHInstruction::Type::SET_FRAME_POINTER_REGISTER, fpr, fpro});237 238    return true;239  }240  case UOP_SaveNonVol:241  case UOP_SaveNonVolBig: {242    uint32_t r = ConvertMachineToLLDBRegister(operation_info);243    if (r == LLDB_INVALID_REGNUM)244      return false;245 246    uint32_t fo;247    if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveNonVolBig,248                                     8))249      return false;250 251    m_program.emplace_back(252        EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo});253 254    return true;255  }256  case UOP_Epilog: {257    return m_iterator.GetNext();258  }259  case UOP_SpareCode: {260    // ReSharper disable once CppIdenticalOperandsInBinaryExpression261    return m_iterator.GetNext() && m_iterator.GetNext();262  }263  case UOP_SaveXMM128:264  case UOP_SaveXMM128Big: {265    uint32_t r = ConvertXMMToLLDBRegister(operation_info);266    if (r == LLDB_INVALID_REGNUM)267      return false;268 269    uint32_t fo;270    if (!ParseBigOrScaledFrameOffset(fo, unwind_operation == UOP_SaveXMM128Big,271                                     16))272      return false;273 274    m_program.emplace_back(275        EHInstruction{o, EHInstruction::Type::SAVE_REGISTER, r, fo});276 277    return true;278  }279  case UOP_PushMachFrame: {280    if (operation_info)281      m_program.emplace_back(EHInstruction{o, EHInstruction::Type::ALLOCATE,282                                           LLDB_INVALID_REGNUM, 8});283    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,284                                         lldb_rip_x86_64, 8});285    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,286                                         lldb_cs_x86_64, 8});287    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,288                                         lldb_rflags_x86_64, 8});289    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,290                                         lldb_rsp_x86_64, 8});291    m_program.emplace_back(EHInstruction{o, EHInstruction::Type::PUSH_REGISTER,292                                         lldb_ss_x86_64, 8});293 294    return true;295  }296  default:297    return false;298  }299}300 301void EHProgramBuilder::Finalize() {302  for (const EHInstruction &i : m_program)303    if (i.reg == lldb_rip_x86_64)304      return;305 306  m_program.emplace_back(307      EHInstruction{0, EHInstruction::Type::PUSH_REGISTER, lldb_rip_x86_64, 8});308}309 310bool EHProgramBuilder::ParseBigOrScaledFrameOffset(uint32_t &result, bool big,311                                                   uint32_t scale) {312  if (big) {313    if (!ParseBigFrameOffset(result))314      return false;315  } else {316    if (!ParseFrameOffset(result))317      return false;318 319    result *= scale;320  }321 322  return true;323}324 325bool EHProgramBuilder::ParseBigFrameOffset(uint32_t &result) {326  if (!m_iterator.GetNext())327    return false;328 329  result = m_iterator.GetUnwindCode()->FrameOffset;330 331  if (!m_iterator.GetNext())332    return false;333 334  result += static_cast<uint32_t>(m_iterator.GetUnwindCode()->FrameOffset)335            << 16;336 337  return true;338}339 340bool EHProgramBuilder::ParseFrameOffset(uint32_t &result) {341  if (!m_iterator.GetNext())342    return false;343 344  result = m_iterator.GetUnwindCode()->FrameOffset;345 346  return true;347}348 349class EHProgramRange {350public:351  EHProgramRange(EHProgram::const_iterator begin,352                 EHProgram::const_iterator end);353 354  UnwindPlan::Row BuildUnwindPlanRow() const;355 356private:357  int32_t GetCFAFrameOffset() const;358 359  EHProgram::const_iterator m_begin;360  EHProgram::const_iterator m_end;361};362 363EHProgramRange::EHProgramRange(EHProgram::const_iterator begin,364                               EHProgram::const_iterator end)365    : m_begin(begin), m_end(end) {}366 367UnwindPlan::Row EHProgramRange::BuildUnwindPlanRow() const {368  UnwindPlan::Row row;369 370  if (m_begin != m_end)371    row.SetOffset(m_begin->offset);372 373  int32_t cfa_frame_offset = GetCFAFrameOffset();374 375  bool frame_pointer_found = false;376  for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) {377    switch (it->type) {378    case EHInstruction::Type::SET_FRAME_POINTER_REGISTER:379      row.GetCFAValue().SetIsRegisterPlusOffset(it->reg, cfa_frame_offset -380                                                             it->frame_offset);381      frame_pointer_found = true;382      break;383    default:384      break;385    }386    if (frame_pointer_found)387      break;388  }389  if (!frame_pointer_found)390    row.GetCFAValue().SetIsRegisterPlusOffset(lldb_rsp_x86_64,391                                              cfa_frame_offset);392 393  int32_t rsp_frame_offset = 0;394  for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) {395    switch (it->type) {396    case EHInstruction::Type::PUSH_REGISTER:397      row.SetRegisterLocationToAtCFAPlusOffset(398          it->reg, rsp_frame_offset - cfa_frame_offset, false);399      rsp_frame_offset += it->frame_offset;400      break;401    case EHInstruction::Type::ALLOCATE:402      rsp_frame_offset += it->frame_offset;403      break;404    case EHInstruction::Type::SAVE_REGISTER:405      row.SetRegisterLocationToAtCFAPlusOffset(406          it->reg, it->frame_offset - cfa_frame_offset, false);407      break;408    default:409      break;410    }411  }412 413  row.SetRegisterLocationToIsCFAPlusOffset(lldb_rsp_x86_64, 0, false);414 415  return row;416}417 418int32_t EHProgramRange::GetCFAFrameOffset() const {419  int32_t result = 0;420 421  for (EHProgram::const_iterator it = m_begin; it != m_end; ++it) {422    switch (it->type) {423    case EHInstruction::Type::PUSH_REGISTER:424    case EHInstruction::Type::ALLOCATE:425      result += it->frame_offset;426      break;427    default:428      break;429    }430  }431 432  return result;433}434 435PECallFrameInfo::PECallFrameInfo(ObjectFilePECOFF &object_file,436                                 uint32_t exception_dir_rva,437                                 uint32_t exception_dir_size)438    : m_object_file(object_file),439      m_exception_dir(object_file.ReadImageDataByRVA(exception_dir_rva,440                                                      exception_dir_size)) {}441 442bool PECallFrameInfo::GetAddressRange(Address addr, AddressRange &range) {443  range.Clear();444 445  const RuntimeFunction *runtime_function =446      FindRuntimeFunctionIntersectsWithRange(AddressRange(addr, 1));447  if (!runtime_function)448    return false;449 450  range.GetBaseAddress() =451      m_object_file.GetAddress(runtime_function->StartAddress);452  range.SetByteSize(runtime_function->EndAddress -453                    runtime_function->StartAddress);454 455  return true;456}457 458std::unique_ptr<UnwindPlan> PECallFrameInfo::GetUnwindPlan(459    llvm::ArrayRef<lldb_private::AddressRange> ranges,460    const lldb_private::Address &addr) {461  // Only continuous functions are supported.462  if (ranges.size() != 1)463    return nullptr;464  const AddressRange &range = ranges[0];465 466  const RuntimeFunction *runtime_function =467      FindRuntimeFunctionIntersectsWithRange(range);468  if (!runtime_function)469    return nullptr;470 471  auto plan_up = std::make_unique<UnwindPlan>(eRegisterKindLLDB);472  plan_up->SetSourceName("PE EH info");473  plan_up->SetSourcedFromCompiler(eLazyBoolYes);474 475  EHProgramBuilder builder(m_object_file, runtime_function->UnwindInfoOffset);476  if (!builder.Build())477    return nullptr;478 479  std::vector<UnwindPlan::Row> rows;480 481  uint32_t last_offset = UINT32_MAX;482  for (auto it = builder.GetProgram().begin(); it != builder.GetProgram().end();483       ++it) {484    if (it->offset == last_offset)485      continue;486 487    EHProgramRange program_range =488        EHProgramRange(it, builder.GetProgram().end());489    rows.push_back(program_range.BuildUnwindPlanRow());490 491    last_offset = it->offset;492  }493 494  for (auto it = rows.rbegin(); it != rows.rend(); ++it)495    plan_up->AppendRow(std::move(*it));496 497  plan_up->SetPlanValidAddressRanges({AddressRange(498      m_object_file.GetAddress(runtime_function->StartAddress),499      runtime_function->EndAddress - runtime_function->StartAddress)});500  plan_up->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);501 502  return plan_up;503}504 505const RuntimeFunction *PECallFrameInfo::FindRuntimeFunctionIntersectsWithRange(506    const AddressRange &range) const {507  uint32_t rva = m_object_file.GetRVA(range.GetBaseAddress());508  addr_t size = range.GetByteSize();509 510  uint32_t begin = 0;511  uint32_t end = m_exception_dir.GetByteSize() / sizeof(RuntimeFunction);512  while (begin < end) {513    uint32_t curr = (begin + end) / 2;514 515    offset_t offset = curr * sizeof(RuntimeFunction);516    const auto *runtime_function =517        TypedRead<RuntimeFunction>(m_exception_dir, offset);518    if (!runtime_function)519      break;520 521    if (runtime_function->StartAddress < rva + size &&522        runtime_function->EndAddress > rva)523      return runtime_function;524 525    if (runtime_function->StartAddress >= rva + size)526      end = curr;527 528    if (runtime_function->EndAddress <= rva)529      begin = curr + 1;530  }531 532  return nullptr;533}534