Add support for Win64 stack unwind data as STACK CFI

This is a copy of https://breakpad.appspot.com/1264002/ where code review took place.

See https://bugzilla.mozilla.org/show_bug.cgi?id=548035 and https://code.google.com/p/chromium/issues/detail?id=115922

Credit to Mikhail I. Izmestev <izmmishao5@gmail.com> for original patch in https://breakpad.appspot.com/345002/

BUG=572
TEST=Run dump_syms_unittest after compiling dump_syms_regtest.cc with x64 toolchain
R=mark@chromium.org

Review URL: https://breakpad.appspot.com/1274002

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1290 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
wfh@chromium.org 2014-03-24 12:12:17 +00:00
parent b764582a53
commit 213a0698cb
6 changed files with 4769 additions and 1 deletions

View File

@ -48,6 +48,53 @@
#define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union. #define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union.
#endif // UNDNAME_NO_ECSU #endif // UNDNAME_NO_ECSU
/*
* Not defined in WinNT.h for some reason. Definitions taken from:
* http://uninformed.org/index.cgi?v=4&a=1&p=13
*
*/
typedef unsigned char UBYTE;
#define UNW_FLAG_EHANDLER 0x01
#define UNW_FLAG_UHANDLER 0x02
#define UNW_FLAG_CHAININFO 0x04
union UnwindCode {
struct {
UBYTE offset_in_prolog;
UBYTE unwind_operation_code : 4;
UBYTE operation_info : 4;
};
USHORT frame_offset;
};
enum UnwindOperationCodes {
UWOP_PUSH_NONVOL = 0, /* info == register number */
UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
//XXX: these are missing from MSDN!
// See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm
UWOP_SAVE_XMM,
UWOP_SAVE_XMM_FAR,
UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */
UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
};
// See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
// Note: some fields removed as we don't use them.
struct UnwindInfo {
UBYTE version : 3;
UBYTE flags : 5;
UBYTE size_of_prolog;
UBYTE count_of_codes;
UBYTE frame_register : 4;
UBYTE frame_offset : 4;
UnwindCode unwind_code[1];
};
namespace google_breakpad { namespace google_breakpad {
namespace { namespace {
@ -383,7 +430,7 @@ bool PDBSourceLineWriter::PrintFunctions() {
return true; return true;
} }
bool PDBSourceLineWriter::PrintFrameData() { bool PDBSourceLineWriter::PrintFrameDataUsingPDB() {
// It would be nice if it were possible to output frame data alongside the // It would be nice if it were possible to output frame data alongside the
// associated function, as is done with line numbers, but the DIA API // associated function, as is done with line numbers, but the DIA API
// doesn't make it possible to get the frame data in that way. // doesn't make it possible to get the frame data in that way.
@ -538,6 +585,149 @@ bool PDBSourceLineWriter::PrintFrameData() {
return true; 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;
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++) {
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));
unwind_rva = chained_func->UnwindInfoAddress;
}
UnwindInfo *unwind_info = static_cast<UnwindInfo *>(
ImageRvaToVa(img->FileHeader,
img->MappedAddress,
unwind_rva,
&img->LastRvaSection));
DWORD stack_size = 8; // minimal stack size is 8 for RIP
DWORD rip_offset = 8;
do {
for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) {
UnwindCode *unwind_code = &unwind_info->unwind_code[c];
switch (unwind_code->unwind_operation_code) {
case UWOP_PUSH_NONVOL: {
stack_size += 8;
break;
}
case UWOP_ALLOC_LARGE: {
if (unwind_code->operation_info == 0) {
c++;
if (c < unwind_info->count_of_codes)
stack_size += (unwind_code + 1)->frame_offset * 8;
} else {
c += 2;
if (c < unwind_info->count_of_codes)
stack_size += (unwind_code + 1)->frame_offset |
((unwind_code + 2)->frame_offset << 16);
}
break;
}
case UWOP_ALLOC_SMALL: {
stack_size += unwind_code->operation_info * 8 + 8;
break;
}
case UWOP_SET_FPREG:
case UWOP_SAVE_XMM:
case UWOP_SAVE_XMM_FAR:
break;
case UWOP_SAVE_NONVOL:
case UWOP_SAVE_XMM128: {
c++; //skip slot with offset
break;
}
case UWOP_SAVE_NONVOL_FAR:
case UWOP_SAVE_XMM128_FAR: {
c += 2; //skip 2 slots with offset
break;
}
case UWOP_PUSH_MACHFRAME: {
if (unwind_code->operation_info) {
stack_size += 88;
} else {
stack_size += 80;
}
rip_offset += 80;
break;
}
}
}
if (unwind_info->flags & UNW_FLAG_CHAININFO) {
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));
} else {
unwind_info = NULL;
}
} while (unwind_info);
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",
funcs[i].BeginAddress, stack_size);
}
return true;
}
bool PDBSourceLineWriter::PrintFrameData() {
PDBModuleInfo info;
if (GetModuleInfo(&info) && info.cpu == L"x86_64") {
return PrintFrameDataUsingEXE();
} else {
return PrintFrameDataUsingPDB();
}
return false;
}
bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) { bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
BOOL is_code; BOOL is_code;
if (FAILED(symbol->get_code(&is_code))) { if (FAILED(symbol->get_code(&is_code))) {

View File

@ -147,6 +147,16 @@ class PDBSourceLineWriter {
// Returns true on success. // Returns true on success.
bool PrintSourceFiles(); bool PrintSourceFiles();
// Outputs all of the frame information necessary to construct stack
// backtraces in the absence of frame pointers. For x86 data stored in
// .pdb files. Returns true on success.
bool PrintFrameDataUsingPDB();
// Outputs all of the frame information necessary to construct stack
// backtraces in the absence of frame pointers. For x64 data stored in
// .exe, .dll files. Returns true on success.
bool PrintFrameDataUsingEXE();
// Outputs all of the frame information necessary to construct stack // Outputs all of the frame information necessary to construct stack
// backtraces in the absence of frame pointers. Returns true on success. // backtraces in the absence of frame pointers. Returns true on success.
bool PrintFrameData(); bool PrintFrameData();

View File

@ -52,6 +52,13 @@
'<(DEPTH)/client/windows/unittests/testing.gyp:gtest', '<(DEPTH)/client/windows/unittests/testing.gyp:gtest',
'dump_syms', 'dump_syms',
], ],
'msvs_settings': {
'VCLinkerTool': {
'AdditionalDependencies': [
'shell32.lib',
],
},
},
}, },
], ],
} }

View File

@ -57,6 +57,8 @@ const wchar_t* kRootNames[] = {
L"omap_stretched", L"omap_stretched",
// A PDB file with OMAP data for an image that has been basic block reordered. // A PDB file with OMAP data for an image that has been basic block reordered.
L"omap_reorder_bbs", L"omap_reorder_bbs",
// A 64bit PDB file with no OMAP data.
L"dump_syms_regtest64",
}; };
void TrimLastComponent(const std::wstring& path, void TrimLastComponent(const std::wstring& path,

Binary file not shown.

File diff suppressed because it is too large Load Diff