mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2026-02-05 05:13:36 +00:00
This is to fix build error on Windows. Bug: chromium:1294084 Change-Id: I8e6a2e46d53d6f5b02343b81cfaa078580a8326d Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/3438886 Reviewed-by: Joshua Peraza <jperaza@chromium.org>
409 lines
13 KiB
C++
409 lines
13 KiB
C++
// 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 <memory>
|
|
|
|
#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:
|
|
* http://uninformed.org/index.cgi?v=4&a=1&p=13
|
|
*
|
|
*/
|
|
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!
|
|
// 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];
|
|
};
|
|
|
|
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
|