[Windows] Use information from the PE32 exceptions directory to construct unwind plans

This patch adds an implementation of unwinding using PE EH info. It allows to
get almost ideal call stacks on 64-bit Windows systems (except some epilogue
cases, but I believe that they can be fixed with unwind plan disassembly
augmentation in the future).

To achieve the goal the CallFrameInfo abstraction was made. It is based on the
DWARFCallFrameInfo class interface with a few changes to make it less
DWARF-specific.

To implement the new interface for PECOFF object files the class PECallFrameInfo
was written. It uses the next helper classes:

- UnwindCodesIterator helps to iterate through UnwindCode structures (and
  processes chained infos transparently);
- EHProgramBuilder with the use of UnwindCodesIterator constructs EHProgram;
- EHProgram is, by fact, a vector of EHInstructions. It creates an abstraction
  over the low-level unwind codes and simplifies work with them. It contains
  only the information that is relevant to unwinding in the unified form. Also
  the required unwind codes are read from the object file only once with it;
- EHProgramRange allows to take a range of EHProgram and to build an unwind row
  for it.

So, PECallFrameInfo builds the EHProgram with EHProgramBuilder, takes the ranges
corresponding to every offset in prologue and builds the rows of the resulted
unwind plan. The resulted plan covers the whole range of the function except the
epilogue.

Reviewers: jasonmolenda, asmith, amccarth, clayborg, JDevlieghere, stella.stamenova, labath, espindola

Reviewed By: jasonmolenda

Subscribers: leonid.mashinskiy, emaste, mgorny, aprantl, arichardson, MaskRay, lldb-commits, llvm-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D67347

llvm-svn: 374528
This commit is contained in:
Aleksandr Urakov
2019-10-11 09:03:29 +00:00
parent b46dd6e92a
commit 30c2441a32
19 changed files with 1149 additions and 11 deletions

View File

@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "ObjectFilePECOFF.h"
#include "PECallFrameInfo.h"
#include "WindowsMiniDump.h"
#include "lldb/Core/FileSpecList.h"
@@ -518,7 +519,26 @@ bool ObjectFilePECOFF::ParseCOFFOptionalHeader(lldb::offset_t *offset_ptr) {
return success;
}
uint32_t ObjectFilePECOFF::GetRVA(const Address &addr) const {
return addr.GetFileAddress() - m_image_base;
}
Address ObjectFilePECOFF::GetAddress(uint32_t rva) {
SectionList *sect_list = GetSectionList();
if (!sect_list)
return Address(GetFileAddress(rva));
return Address(GetFileAddress(rva), sect_list);
}
lldb::addr_t ObjectFilePECOFF::GetFileAddress(uint32_t rva) const {
return m_image_base + rva;
}
DataExtractor ObjectFilePECOFF::ReadImageData(uint32_t offset, size_t size) {
if (!size)
return {};
if (m_file) {
// A bit of a hack, but we intend to write to this buffer, so we can't
// mmap it.
@@ -541,6 +561,15 @@ DataExtractor ObjectFilePECOFF::ReadImageData(uint32_t offset, size_t size) {
return data;
}
DataExtractor ObjectFilePECOFF::ReadImageDataByRVA(uint32_t rva, size_t size) {
if (m_file) {
Address addr = GetAddress(rva);
rva = addr.GetSection()->GetFileOffset() + addr.GetOffset();
}
return ReadImageData(rva, size);
}
// ParseSectionHeaders
bool ObjectFilePECOFF::ParseSectionHeaders(
uint32_t section_header_data_offset) {
@@ -678,14 +707,8 @@ Symtab *ObjectFilePECOFF::GetSymtab() {
uint32_t data_start =
m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr;
uint32_t address_rva = data_start;
if (m_file) {
Address address(m_coff_header_opt.image_base + data_start, sect_list);
address_rva =
address.GetSection()->GetFileOffset() + address.GetOffset();
}
DataExtractor symtab_data =
ReadImageData(address_rva, m_coff_header_opt.data_dirs[0].vmsize);
DataExtractor symtab_data = ReadImageDataByRVA(
data_start, m_coff_header_opt.data_dirs[0].vmsize);
lldb::offset_t offset = 0;
// Read export_table header
@@ -740,6 +763,19 @@ Symtab *ObjectFilePECOFF::GetSymtab() {
return m_symtab_up.get();
}
std::unique_ptr<CallFrameInfo> ObjectFilePECOFF::CreateCallFrameInfo() {
if (coff_data_dir_exception_table >= m_coff_header_opt.data_dirs.size())
return {};
data_directory data_dir_exception =
m_coff_header_opt.data_dirs[coff_data_dir_exception_table];
if (!data_dir_exception.vmaddr)
return {};
return std::make_unique<PECallFrameInfo>(*this, data_dir_exception.vmaddr,
data_dir_exception.vmsize);
}
bool ObjectFilePECOFF::IsStripped() {
// TODO: determine this for COFF
return false;