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:
parent
b764582a53
commit
213a0698cb
@ -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))) {
|
||||||
|
@ -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();
|
||||||
|
@ -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',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
BIN
src/tools/windows/dump_syms/testdata/dump_syms_regtest64.pdb
vendored
Normal file
BIN
src/tools/windows/dump_syms/testdata/dump_syms_regtest64.pdb
vendored
Normal file
Binary file not shown.
4559
src/tools/windows/dump_syms/testdata/dump_syms_regtest64.sym
vendored
Normal file
4559
src/tools/windows/dump_syms/testdata/dump_syms_regtest64.sym
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user