Index: src/common/windows/pdb_source_line_writer.cc |
diff --git a/src/common/windows/pdb_source_line_writer.cc b/src/common/windows/pdb_source_line_writer.cc |
index f0edb721c07e86bd9658a984be55e6bf23749dbe..522ec1a125c42792107b20e8072d704c3989187f 100644 |
--- a/src/common/windows/pdb_source_line_writer.cc |
+++ b/src/common/windows/pdb_source_line_writer.cc |
@@ -37,6 +37,7 @@ |
#include <ImageHlp.h> |
#include <stdio.h> |
+#include <cassert> |
#include <limits> |
#include <set> |
@@ -686,61 +687,32 @@ bool PDBSourceLineWriter::PrintFrameDataUsingPDB() { |
return true; |
} |
-bool PDBSourceLineWriter::PrintFrameDataUsingEXE() { |
- if (code_file_.empty() && !FindPEFile()) { |
- fprintf(stderr, "Couldn't locate EXE or DLL file.\n"); |
- return false; |
- } |
- |
- // Convert wchar to native charset because ImageLoad only takes |
- // a PSTR as input. |
- string code_file; |
- if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) { |
- return false; |
- } |
- |
- AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL)); |
- if (!img) { |
- fprintf(stderr, "Failed to load %s\n", code_file.c_str()); |
- return false; |
- } |
- PIMAGE_OPTIONAL_HEADER64 optional_header = |
- &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader; |
- if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { |
- fprintf(stderr, "Not a PE32+ image\n"); |
- return false; |
- } |
- |
- // Read Exception Directory |
- DWORD exception_rva = optional_header-> |
- DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; |
- DWORD exception_size = optional_header-> |
- DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size; |
+// Prints CFI from xdata/pdata |
+// |
+// Performing the translation of RVA to a real pointer is very different for a |
+// XDATA/PDATA streams read from a PDB, and XDATA/PDATA found in the loaded |
+// image of an executable, so we specialize this function for each case with |
+// RvaToVaTraits |
+// |
+template <typename RvaToVaTraits> |
+static bool PrintCFIFromXData(FILE *output, PVOID pdata, size_t pdata_size, |
+ const typename RvaToVaTraits &t) { |
PIMAGE_RUNTIME_FUNCTION_ENTRY funcs = |
- static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( |
- ImageRvaToVa(img->FileHeader, |
- img->MappedAddress, |
- exception_rva, |
- &img->LastRvaSection)); |
- for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) { |
+ static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(pdata); |
+ |
+ for (size_t i = 0; i < pdata_size / sizeof(*funcs) ; i++) { |
DWORD unwind_rva = funcs[i].UnwindInfoAddress; |
// handle chaining |
while (unwind_rva & 0x1) { |
unwind_rva ^= 0x1; |
PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func = |
static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( |
- ImageRvaToVa(img->FileHeader, |
- img->MappedAddress, |
- unwind_rva, |
- &img->LastRvaSection)); |
+ t.PDataRvaToVa(unwind_rva)); |
unwind_rva = chained_func->UnwindInfoAddress; |
} |
UnwindInfo *unwind_info = static_cast<UnwindInfo *>( |
- ImageRvaToVa(img->FileHeader, |
- img->MappedAddress, |
- unwind_rva, |
- &img->LastRvaSection)); |
+ t.XDataRvaToVa(unwind_rva)); |
DWORD stack_size = 8; // minimal stack size is 8 for RIP |
DWORD rip_offset = 8; |
@@ -795,33 +767,162 @@ bool PDBSourceLineWriter::PrintFrameDataUsingEXE() { |
} |
} |
if (unwind_info->flags & UNW_FLAG_CHAININFO) { |
+ // unwind_code[] is followed by an IMAGE_RUNTIME_FUNCTION_ENTRY |
PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func = |
reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( |
(unwind_info->unwind_code + |
((unwind_info->count_of_codes + 1) & ~1))); |
unwind_info = static_cast<UnwindInfo *>( |
- ImageRvaToVa(img->FileHeader, |
- img->MappedAddress, |
- chained_func->UnwindInfoAddress, |
- &img->LastRvaSection)); |
+ t.XDataRvaToVa(chained_func->UnwindInfoAddress)); |
} else { |
unwind_info = NULL; |
} |
} while (unwind_info); |
- fprintf(output_, "STACK CFI INIT %x %x .cfa: $rsp .ra: .cfa %d - ^\n", |
+ fprintf(output, "STACK CFI INIT %x %x .cfa: $rsp .ra: .cfa %d - ^\n", |
funcs[i].BeginAddress, |
funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset); |
- fprintf(output_, "STACK CFI %x .cfa: $rsp %d +\n", |
+ fprintf(output, "STACK CFI %x .cfa: $rsp %d +\n", |
funcs[i].BeginAddress, stack_size); |
} |
return true; |
} |
+// In a LOADED_IMAGE, we can simply use ImageRvaToVa() to translate RVAs |
+class ImageRvaToVaTraits { |
+ public: |
+ ImageRvaToVaTraits(PLOADED_IMAGE img) |
+ : img_(img) |
+ { |
+ }; |
+ |
+ PVOID XDataRvaToVa(DWORD rva) const { |
+ return ImageRvaToVa(img_->FileHeader, |
+ img_->MappedAddress, |
+ rva, |
+ &img_->LastRvaSection); |
+ }; |
+ |
+ PVOID PDataRvaToVa(DWORD rva) const { |
+ return ImageRvaToVa(img_->FileHeader, |
+ img_->MappedAddress, |
+ rva, |
+ &img_->LastRvaSection); |
+ }; |
+ |
+ private: |
+ PLOADED_IMAGE img_; |
+}; |
+ |
+bool PDBSourceLineWriter::PrintFrameDataUsingEXE() { |
+ if (code_file_.empty() && !FindPEFile()) { |
+ fprintf(stderr, "Couldn't locate EXE or DLL file.\n"); |
+ return false; |
+ } |
+ |
+ // Convert wchar to native charset because ImageLoad only takes |
+ // a PSTR as input. |
+ string code_file; |
+ if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) { |
+ return false; |
+ } |
+ |
+ AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL)); |
+ if (!img) { |
+ fprintf(stderr, "Failed to load %s\n", code_file.c_str()); |
+ return false; |
+ } |
+ PIMAGE_OPTIONAL_HEADER64 optional_header = |
+ &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader; |
+ if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { |
+ fprintf(stderr, "Not a PE32+ image\n"); |
+ return false; |
+ } |
+ |
+ // Read Exception Directory |
+ DWORD exception_rva = optional_header-> |
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; |
+ DWORD exception_size = optional_header-> |
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size; |
+ |
+ PVOID pdata = static_cast<PVOID>( |
+ ImageRvaToVa(img->FileHeader, |
+ img->MappedAddress, |
+ exception_rva, |
+ &img->LastRvaSection)); |
+ |
+ ImageRvaToVaTraits t(img); |
+ |
+ return PrintCFIFromXData(output_, pdata, exception_size, t); |
+} |
+ |
+// We have the XDATA/PDATA streams from the PDB, along with their base RVAs |
+// provided by IDiaImageData. It is assumed that all the RVAs we access lie |
+// within the range of RVAs covered by the stream. |
+class PdbRvaToVaTraits { |
+ public: |
+ PdbRvaToVaTraits(PVOID pdata, size_t pdata_size, DWORD pdata_base_rva, |
+ PVOID xdata, size_t xdata_size, DWORD xdata_base_rva) |
+ : pdata_(pdata), pdata_size_(pdata_size), pdata_base_rva_(pdata_base_rva), |
+ xdata_(xdata), xdata_size_(xdata_size), xdata_base_rva_(xdata_base_rva) |
+ { |
+ }; |
+ |
+ PVOID XDataRvaToVa(DWORD rva) const { |
+ assert(rva >= xdata_base_rva_); |
+ assert(rva < xdata_base_rva_ + xdata_size_); |
+ |
+ return rva - xdata_base_rva_ + static_cast<PBYTE>(xdata_); |
+ }; |
+ |
+ PVOID PDataRvaToVa(DWORD rva) const { |
+ assert(rva >= pdata_base_rva_); |
+ assert(rva < pdata_base_rva_ + pdata_size_); |
+ |
+ return rva - pdata_base_rva_ + static_cast<PBYTE>(pdata_); |
+ }; |
+ |
+ private: |
+ PVOID pdata_; |
+ size_t pdata_size_; |
+ DWORD pdata_base_rva_; |
+ PVOID xdata_; |
+ size_t xdata_size_; |
+ DWORD xdata_base_rva_; |
+}; |
+ |
+bool PDBSourceLineWriter::PrintFrameDataUsingPDBXData() { |
+ static const wchar_t kPdataStreamName[] = L"PDATA"; |
+ static const wchar_t kXdataStreamName[] = L"XDATA"; |
+ |
+ std::vector<BYTE> pdata; |
+ std::vector<BYTE> xdata; |
+ DWORD pdata_base_rva; |
+ DWORD xdata_base_rva; |
+ |
+ if (FindAndLoadDebugStream(kPdataStreamName, session_, &pdata, |
+ &pdata_base_rva) && pdata_base_rva && |
+ FindAndLoadDebugStream(kXdataStreamName, session_, &xdata, |
+ &xdata_base_rva) && xdata_base_rva) { |
+ |
+ PdbRvaToVaTraits t(&pdata[0], pdata.size(), pdata_base_rva, |
+ &xdata[0], xdata.size(), xdata_base_rva); |
+ |
+ return PrintCFIFromXData(output_, &pdata[0], pdata.size(), t); |
+ } |
+ |
+ return false; |
+} |
+ |
bool PDBSourceLineWriter::PrintFrameData() { |
PDBModuleInfo info; |
if (GetModuleInfo(&info) && info.cpu == L"x86_64") { |
+ // Print CFI using .pdata/.xdata streams in PDB |
+ if (PrintFrameDataUsingPDBXData()) |
+ return true; |
+ // If they are not present, fall-back to reading the .pdata/.xdata from the |
+ // corresponding PE/COFF executable, if we can find it. |
return PrintFrameDataUsingEXE(); |
} else { |
return PrintFrameDataUsingPDB(); |