Source code
Revision control
Copy as Markdown
Other Tools
// Copyright (c) 2019, Google Inc.↩
// All rights reserved.↩
//↩
// Redistribution and use in source and binary forms, with or without↩
// modification, are permitted provided that the following conditions are↩
// met:↩
//↩
// * Redistributions of source code must retain the above copyright↩
// notice, this list of conditions and the following disclaimer.↩
// * Redistributions in binary form must reproduce the above↩
// copyright notice, this list of conditions and the following disclaimer↩
// in the documentation and/or other materials provided with the↩
// distribution.↩
// * Neither the name of Google Inc. nor the names of its↩
// contributors may be used to endorse or promote products derived from↩
// this software without specific prior written permission.↩
//↩
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS↩
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT↩
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR↩
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT↩
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,↩
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT↩
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,↩
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY↩
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT↩
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE↩
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.↩
↩
#include "pe_util.h"↩
↩
#include <windows.h>↩
#include <winnt.h>↩
#include <atlbase.h>↩
#include <ImageHlp.h>↩
↩
#include <functional>↩
↩
#include "common/windows/string_utils-inl.h"↩
#include "common/windows/guid_string.h"↩
↩
namespace {↩
↩
/*↩
* Not defined in WinNT.h for some reason. Definitions taken from:↩
*↩
*/↩
typedef unsigned char UBYTE;↩
↩
#if !defined(_WIN64)↩
#define UNW_FLAG_EHANDLER 0x01↩
#define UNW_FLAG_UHANDLER 0x02↩
#define UNW_FLAG_CHAININFO 0x04↩
#endif↩
↩
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!↩
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 */↩
};↩
↩
// 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];↩
};↩
↩
struct CV_INFO_PDB70 {↩
ULONG cv_signature;↩
GUID signature;↩
ULONG age;↩
CHAR pdb_filename[ANYSIZE_ARRAY];↩
};↩
↩
#define CV_SIGNATURE_RSDS 'SDSR'↩
↩
// A helper class to scope a PLOADED_IMAGE.↩
class AutoImage {↩
public:↩
explicit AutoImage(PLOADED_IMAGE img) : img_(img) {}↩
~AutoImage() {↩
if (img_)↩
ImageUnload(img_);↩
}↩
↩
operator PLOADED_IMAGE() { return img_; }↩
PLOADED_IMAGE operator->() { return img_; }↩
↩
private:↩
PLOADED_IMAGE img_;↩
};↩
} // namespace↩
↩
namespace google_breakpad {↩
↩
using std::unique_ptr;↩
using google_breakpad::GUIDString;↩
↩
bool ReadModuleInfo(const wstring & pe_file, PDBModuleInfo * info) {↩
// Convert wchar to native charset because ImageLoad only takes↩
// a PSTR as input.↩
string img_file;↩
if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {↩
fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",↩
pe_file.c_str());↩
return false;↩
}↩
↩
AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));↩
if (!img) {↩
fprintf(stderr, "Failed to load %s\n", img_file.c_str());↩
return false;↩
}↩
↩
info->cpu = FileHeaderMachineToCpuString(↩
img->FileHeader->FileHeader.Machine);↩
↩
PIMAGE_OPTIONAL_HEADER64 optional_header =↩
&(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;↩
↩
// Search debug directories for a guid signature & age↩
DWORD debug_rva = optional_header->↩
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;↩
DWORD debug_size = optional_header->↩
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;↩
PIMAGE_DEBUG_DIRECTORY debug_directories =↩
static_cast<PIMAGE_DEBUG_DIRECTORY>(↩
ImageRvaToVa(img->FileHeader,↩
img->MappedAddress,↩
debug_rva,↩
&img->LastRvaSection));↩
↩
for (DWORD i = 0; i < debug_size / sizeof(*debug_directories); i++) {↩
if (debug_directories[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW ||↩
debug_directories[i].SizeOfData < sizeof(CV_INFO_PDB70)) {↩
continue;↩
}↩
↩
struct CV_INFO_PDB70* cv_info = static_cast<CV_INFO_PDB70*>(ImageRvaToVa(↩
img->FileHeader,↩
img->MappedAddress,↩
debug_directories[i].AddressOfRawData,↩
&img->LastRvaSection));↩
if (cv_info->cv_signature != CV_SIGNATURE_RSDS) {↩
continue;↩
}↩
↩
info->debug_identifier = GenerateDebugIdentifier(cv_info->age,↩
cv_info->signature);↩
↩
// This code assumes that the pdb_filename is stored as ASCII without↩
// multibyte characters, but it's not clear if that's true.↩
size_t debug_file_length = strnlen_s(cv_info->pdb_filename, MAX_PATH);↩
if (debug_file_length < 0 || debug_file_length >= MAX_PATH) {↩
fprintf(stderr, "PE debug directory is corrupt.\n");↩
return false;↩
}↩
std::string debug_file(cv_info->pdb_filename, debug_file_length);↩
if (!WindowsStringUtils::safe_mbstowcs(debug_file, &info->debug_file)) {↩
fprintf(stderr, "PDB filename '%s' contains unrecognized characters.\n",↩
debug_file.c_str());↩
return false;↩
}↩
info->debug_file = WindowsStringUtils::GetBaseName(info->debug_file);↩
↩
return true;↩
}↩
↩
fprintf(stderr, "Image is missing debug information.\n");↩
return false;↩
}↩
↩
bool ReadPEInfo(const wstring & pe_file, PEModuleInfo * info) {↩
// Convert wchar to native charset because ImageLoad only takes↩
// a PSTR as input.↩
string img_file;↩
if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {↩
fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",↩
pe_file.c_str());↩
return false;↩
}↩
↩
AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));↩
if (!img) {↩
fprintf(stderr, "Failed to open PE file: %S\n", pe_file.c_str());↩
return false;↩
}↩
↩
info->code_file = WindowsStringUtils::GetBaseName(pe_file);↩
↩
// The date and time that the file was created by the linker.↩
DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp;↩
// The size of the file in bytes, including all headers.↩
DWORD SizeOfImage = 0;↩
PIMAGE_OPTIONAL_HEADER64 opt =↩
&((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader;↩
if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {↩
// 64-bit PE file.↩
SizeOfImage = opt->SizeOfImage;↩
}↩
else {↩
// 32-bit PE file.↩
SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage;↩
}↩
wchar_t code_identifier[32];↩
swprintf(code_identifier,↩
sizeof(code_identifier) / sizeof(code_identifier[0]),↩
L"%08X%X", TimeDateStamp, SizeOfImage);↩
info->code_identifier = code_identifier;↩
↩
return true;↩
}↩
↩
bool PrintPEFrameData(const wstring & pe_file, FILE * out_file)↩
{↩
// Convert wchar to native charset because ImageLoad only takes↩
// a PSTR as input.↩
string img_file;↩
if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {↩
fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",↩
pe_file.c_str());↩
return false;↩
}↩
↩
AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));↩
if (!img) {↩
fprintf(stderr, "Failed to load %s\n", img_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(out_file, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n",↩
funcs[i].BeginAddress,↩
funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset);↩
fprintf(out_file, "STACK CFI %lx .cfa: $rsp %lu +\n",↩
funcs[i].BeginAddress, stack_size);↩
}↩
↩
return true;↩
}↩
↩
wstring GenerateDebugIdentifier(DWORD age, GUID signature)↩
{↩
// Use the same format that the MS symbol server uses in filesystem↩
// hierarchies.↩
wchar_t age_string[9];↩
swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]),↩
L"%x", age);↩
↩
// remove when VC++7.1 is no longer supported↩
age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0';↩
↩
wstring debug_identifier = GUIDString::GUIDToSymbolServerWString(&signature);↩
debug_identifier.append(age_string);↩
↩
return debug_identifier;↩
}↩
↩
wstring GenerateDebugIdentifier(DWORD age, DWORD signature)↩
{↩
// Use the same format that the MS symbol server uses in filesystem↩
// hierarchies.↩
wchar_t identifier_string[17];↩
swprintf(identifier_string,↩
sizeof(identifier_string) / sizeof(identifier_string[0]),↩
L"%08X%x", signature, age);↩
↩
// remove when VC++7.1 is no longer supported↩
identifier_string[sizeof(identifier_string) /↩
sizeof(identifier_string[0]) - 1] = L'\0';↩
↩
return wstring(identifier_string);↩
}↩
↩
} // namespace google_breakpad↩