mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-13 17:43:59 +02:00
Update breakpad to make it work with MinGW
This commit is contained in:
87
thirdparty/breakpad/common/windows/common_windows.gyp
vendored
Normal file
87
thirdparty/breakpad/common/windows/common_windows.gyp
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
# Copyright 2013 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.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../client/windows/build/common.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'dia_sdk',
|
||||
'type': 'none',
|
||||
'all_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'<(DEPTH)',
|
||||
'"$(VSInstallDir)\DIA SDK\include"',
|
||||
],
|
||||
'msvs_settings': {
|
||||
'VCLinkerTool': {
|
||||
'AdditionalDependencies': [
|
||||
'$(VSInstallDir)\DIA SDK\lib\diaguids.lib',
|
||||
'imagehlp.lib',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'target_name': 'common_windows_lib',
|
||||
'type': 'static_library',
|
||||
'sources': [
|
||||
'dia_util.cc',
|
||||
'dia_util.h',
|
||||
'guid_string.cc',
|
||||
'guid_string.h',
|
||||
'http_upload.cc',
|
||||
'http_upload.h',
|
||||
'omap.cc',
|
||||
'omap.h',
|
||||
'omap_internal.h',
|
||||
'pdb_source_line_writer.cc',
|
||||
'pdb_source_line_writer.h',
|
||||
'string_utils.cc',
|
||||
'string_utils-inl.h',
|
||||
],
|
||||
'dependencies': [
|
||||
'dia_sdk',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'common_windows_unittests',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'omap_unittest.cc',
|
||||
],
|
||||
'dependencies': [
|
||||
'<(DEPTH)/client/windows/unittests/testing.gyp:gmock',
|
||||
'<(DEPTH)/client/windows/unittests/testing.gyp:gtest',
|
||||
'common_windows_lib',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
92
thirdparty/breakpad/common/windows/dia_util.cc
vendored
Normal file
92
thirdparty/breakpad/common/windows/dia_util.cc
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2013 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 "common/windows/dia_util.h"
|
||||
|
||||
#include <atlbase.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool FindDebugStream(const wchar_t* name,
|
||||
IDiaSession* session,
|
||||
IDiaEnumDebugStreamData** debug_stream) {
|
||||
CComPtr<IDiaEnumDebugStreams> enum_debug_streams;
|
||||
if (FAILED(session->getEnumDebugStreams(&enum_debug_streams))) {
|
||||
fprintf(stderr, "IDiaSession::getEnumDebugStreams failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CComPtr<IDiaEnumDebugStreamData> temp_debug_stream;
|
||||
ULONG fetched = 0;
|
||||
while (SUCCEEDED(enum_debug_streams->Next(1, &temp_debug_stream, &fetched)) &&
|
||||
fetched == 1) {
|
||||
CComBSTR stream_name;
|
||||
if (FAILED(temp_debug_stream->get_name(&stream_name))) {
|
||||
fprintf(stderr, "IDiaEnumDebugStreamData::get_name failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Found the stream?
|
||||
if (wcsncmp((LPWSTR)stream_name, name, stream_name.Length()) == 0) {
|
||||
*debug_stream = temp_debug_stream.Detach();
|
||||
return true;
|
||||
}
|
||||
|
||||
temp_debug_stream.Release();
|
||||
}
|
||||
|
||||
// No table was found.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindTable(REFIID iid, IDiaSession* session, void** table) {
|
||||
// Get the table enumerator.
|
||||
CComPtr<IDiaEnumTables> enum_tables;
|
||||
if (FAILED(session->getEnumTables(&enum_tables))) {
|
||||
fprintf(stderr, "IDiaSession::getEnumTables failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate through the tables.
|
||||
CComPtr<IDiaTable> temp_table;
|
||||
ULONG fetched = 0;
|
||||
while (SUCCEEDED(enum_tables->Next(1, &temp_table, &fetched)) &&
|
||||
fetched == 1) {
|
||||
void* temp = NULL;
|
||||
if (SUCCEEDED(temp_table->QueryInterface(iid, &temp))) {
|
||||
*table = temp;
|
||||
return true;
|
||||
}
|
||||
temp_table.Release();
|
||||
}
|
||||
|
||||
// The table was not found.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
59
thirdparty/breakpad/common/windows/dia_util.h
vendored
Normal file
59
thirdparty/breakpad/common/windows/dia_util.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Utilities for loading debug streams and tables from a PDB file.
|
||||
|
||||
#include <windows.h>
|
||||
#include <dia2.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Find the debug stream of the given |name| in the given |session|. Returns
|
||||
// true on success, false on error of if the stream does not exist. On success
|
||||
// the stream will be returned via |debug_stream|.
|
||||
bool FindDebugStream(const wchar_t* name,
|
||||
IDiaSession* session,
|
||||
IDiaEnumDebugStreamData** debug_stream);
|
||||
|
||||
// Finds the first table implementing the COM interface with ID |iid| in the
|
||||
// given |session|. Returns true on success, false on error or if no such
|
||||
// table is found. On success the table will be returned via |table|.
|
||||
bool FindTable(REFIID iid, IDiaSession* session, void** table);
|
||||
|
||||
// A templated version of FindTable. Finds the first table implementing type
|
||||
// |InterfaceType| in the given |session|. Returns true on success, false on
|
||||
// error or if no such table is found. On success the table will be returned via
|
||||
// |table|.
|
||||
template<typename InterfaceType>
|
||||
bool FindTable(IDiaSession* session, InterfaceType** table) {
|
||||
return FindTable(__uuidof(InterfaceType),
|
||||
session,
|
||||
reinterpret_cast<void**>(table));
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@@ -30,9 +30,12 @@
|
||||
#include <assert.h>
|
||||
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning( disable : 4530 )
|
||||
#pragma warning(disable:4530)
|
||||
|
||||
#include <fstream>
|
||||
#ifdef __MINGW32__
|
||||
#include <ext/stdio_filebuf.h>
|
||||
#endif
|
||||
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
|
||||
@@ -152,18 +155,18 @@ bool HTTPUpload::SendRequest(const wstring &url,
|
||||
if (!InternetSetOption(request.get(),
|
||||
INTERNET_OPTION_SEND_TIMEOUT,
|
||||
timeout,
|
||||
sizeof(timeout))) {
|
||||
sizeof(*timeout))) {
|
||||
fwprintf(stderr, L"Could not unset send timeout, continuing...\n");
|
||||
}
|
||||
|
||||
if (!InternetSetOption(request.get(),
|
||||
INTERNET_OPTION_RECEIVE_TIMEOUT,
|
||||
timeout,
|
||||
sizeof(timeout))) {
|
||||
sizeof(*timeout))) {
|
||||
fwprintf(stderr, L"Could not unset receive timeout, continuing...\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!HttpSendRequest(request.get(), NULL, 0,
|
||||
const_cast<char *>(request_body.data()),
|
||||
static_cast<DWORD>(request_body.size()))) {
|
||||
@@ -215,8 +218,7 @@ bool HTTPUpload::ReadResponse(HINTERNET request, wstring *response) {
|
||||
BOOL return_code;
|
||||
|
||||
while (((return_code = InternetQueryDataAvailable(request, &bytes_available,
|
||||
0, 0)) != 0) && bytes_available > 0) {
|
||||
|
||||
0, 0)) != 0) && bytes_available > 0) {
|
||||
vector<char> response_buffer(bytes_available);
|
||||
DWORD size_read;
|
||||
|
||||
@@ -323,28 +325,44 @@ bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> ¶meters,
|
||||
// static
|
||||
bool HTTPUpload::GetFileContents(const wstring &filename,
|
||||
vector<char> *contents) {
|
||||
bool rv = false;
|
||||
// The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
|
||||
// wchar_t* filename, so use _wfopen directly in that case. For VC8 and
|
||||
// later, _wfopen has been deprecated in favor of _wfopen_s, which does
|
||||
// not exist in earlier versions, so let the ifstream open the file itself.
|
||||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
// later, _wfopen has been deprecated in favor of _wfopen_s, which does not
|
||||
// exist in earlier versions, so let the ifstream open the file itself. GCC
|
||||
// doesn't support using a wide-character file name, so use the stdio_filebuf
|
||||
// extension.
|
||||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
ifstream file;
|
||||
file.open(filename.c_str(), ios::binary);
|
||||
#else // _MSC_VER >= 1400
|
||||
if (!file.is_open()) return false;
|
||||
#elif defined(_MSC_VER)
|
||||
ifstream file(_wfopen(filename.c_str(), L"rb"));
|
||||
#endif // _MSC_VER >= 1400
|
||||
if (file.is_open()) {
|
||||
if (!file.is_open()) return false;
|
||||
#else
|
||||
FILE *f = _wfopen(filename.c_str(), L"rb");
|
||||
if (!f) return false;
|
||||
__gnu_cxx::stdio_filebuf<char> filebuf(f, std::ios::in);
|
||||
std::istream file(&filebuf);
|
||||
#endif
|
||||
|
||||
{
|
||||
file.seekg(0, ios::end);
|
||||
std::streamoff length = file.tellg();
|
||||
contents->resize(length);
|
||||
if (length != 0) {
|
||||
file.seekg(0, ios::beg);
|
||||
file.read(&((*contents)[0]), length);
|
||||
// Check for loss of data when converting lenght from std::streamoff into
|
||||
// std::vector<char>::size_type
|
||||
std::vector<char>::size_type vector_size =
|
||||
static_cast<std::vector<char>::size_type>(length);
|
||||
if (static_cast<std::streamoff>(vector_size) == length) {
|
||||
contents->resize(vector_size);
|
||||
if (length != 0) {
|
||||
file.seekg(0, ios::beg);
|
||||
file.read(&((*contents)[0]), length);
|
||||
}
|
||||
rv = true;
|
||||
}
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return rv;
|
||||
}
|
||||
|
||||
// static
|
||||
|
@@ -38,8 +38,8 @@
|
||||
// Disable exception handler warnings.
|
||||
#pragma warning( disable : 4530 )
|
||||
|
||||
#include <Windows.h>
|
||||
#include <WinInet.h>
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -80,7 +80,7 @@ class HTTPUpload {
|
||||
// this merely checks (via the return value) that we were successfully
|
||||
// able to retrieve exactly as many bytes of content in the response as
|
||||
// were specified in the Content-Length header.
|
||||
static bool HTTPUpload::ReadResponse(HINTERNET request, wstring* response);
|
||||
static bool ReadResponse(HINTERNET request, wstring* response);
|
||||
|
||||
// Generates a new multipart boundary for a POST request
|
||||
static wstring GenerateMultipartBoundary();
|
||||
|
694
thirdparty/breakpad/common/windows/omap.cc
vendored
Normal file
694
thirdparty/breakpad/common/windows/omap.cc
vendored
Normal file
@@ -0,0 +1,694 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// This contains a suite of tools for transforming symbol information when
|
||||
// when that information has been extracted from a PDB containing OMAP
|
||||
// information.
|
||||
|
||||
// OMAP information is a lightweight description of a mapping between two
|
||||
// address spaces. It consists of two streams, each of them a vector 2-tuples.
|
||||
// The OMAPTO stream contains tuples of the form
|
||||
//
|
||||
// (RVA in transformed image, RVA in original image)
|
||||
//
|
||||
// while the OMAPFROM stream contains tuples of the form
|
||||
//
|
||||
// (RVA in original image, RVA in transformed image)
|
||||
//
|
||||
// The entries in each vector are sorted by the first value of the tuple, and
|
||||
// the lengths associated with a mapping are implicit as the distance between
|
||||
// two successive addresses in the vector.
|
||||
|
||||
// Consider a trivial 10-byte function described by the following symbol:
|
||||
//
|
||||
// Function: RVA 0x00001000, length 10, "foo"
|
||||
//
|
||||
// Now consider the same function, but with 5-bytes of instrumentation injected
|
||||
// at offset 5. The OMAP streams describing this would look like:
|
||||
//
|
||||
// OMAPTO : [ [0x00001000, 0x00001000],
|
||||
// [0x00001005, 0xFFFFFFFF],
|
||||
// [0x0000100a, 0x00001005] ]
|
||||
// OMAPFROM: [ [0x00001000, 0x00001000],
|
||||
// [0x00001005, 0x0000100a] ]
|
||||
//
|
||||
// In this case the injected code has been marked as not originating in the
|
||||
// source image, and thus it will have no symbol information at all. However,
|
||||
// the injected code may also be associated with an original address range;
|
||||
// for example, when prepending instrumentation to a basic block the
|
||||
// instrumentation can be labelled as originating from the same source BB such
|
||||
// that symbol resolution will still find the appropriate source code line
|
||||
// number. In this case the OMAP stream would look like:
|
||||
//
|
||||
// OMAPTO : [ [0x00001000, 0x00001000],
|
||||
// [0x00001005, 0x00001005],
|
||||
// [0x0000100a, 0x00001005] ]
|
||||
// OMAPFROM: [ [0x00001000, 0x00001000],
|
||||
// [0x00001005, 0x0000100a] ]
|
||||
//
|
||||
// Suppose we asked DIA to lookup the symbol at location 0x0000100a of the
|
||||
// instrumented image. It would first run this through the OMAPTO table and
|
||||
// translate that address to 0x00001005. It would then lookup the symbol
|
||||
// at that address and return the symbol for the function "foo". This is the
|
||||
// correct result.
|
||||
//
|
||||
// However, if we query DIA for the length and address of the symbol it will
|
||||
// tell us that it has length 10 and is at RVA 0x00001000. The location is
|
||||
// correct, but the length doesn't take into account the 5-bytes of injected
|
||||
// code. Symbol resolution works (starting from an instrumented address,
|
||||
// mapping to an original address, and looking up a symbol), but the symbol
|
||||
// metadata is incorrect.
|
||||
//
|
||||
// If we dump the symbols using DIA they will have their addresses
|
||||
// appropriately transformed and reflect positions in the instrumented image.
|
||||
// However, if we try to do a lookup using those symbols resolution can fail.
|
||||
// For example, the address 0x0000100a will not map to the symbol for "foo",
|
||||
// because DIA tells us it is at location 0x00001000 (correct) with length
|
||||
// 10 (incorrect). The problem is one of order of operations: in this case
|
||||
// we're attempting symbol resolution by looking up an instrumented address
|
||||
// in the table of translated symbols.
|
||||
//
|
||||
// One way to handle this is to dump the OMAP information as part of the
|
||||
// breakpad symbols. This requires the rest of the toolchain to be aware of
|
||||
// OMAP information and to use it when present prior to performing lookup. The
|
||||
// other option is to properly transform the symbols (updating length as well as
|
||||
// position) so that resolution will work as expected for translated addresses.
|
||||
// This is transparent to the rest of the toolchain.
|
||||
|
||||
#include "common/windows/omap.h"
|
||||
|
||||
#include <atlbase.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <set>
|
||||
|
||||
#include "common/windows/dia_util.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
namespace {
|
||||
|
||||
static const wchar_t kOmapToDebugStreamName[] = L"OMAPTO";
|
||||
static const wchar_t kOmapFromDebugStreamName[] = L"OMAPFROM";
|
||||
|
||||
// Dependending on where this is used in breakpad we sometimes get min/max from
|
||||
// windef, and other times from algorithm. To get around this we simply
|
||||
// define our own min/max functions.
|
||||
template<typename T>
|
||||
const T& Min(const T& t1, const T& t2) { return t1 < t2 ? t1 : t2; }
|
||||
template<typename T>
|
||||
const T& Max(const T& t1, const T& t2) { return t1 > t2 ? t1 : t2; }
|
||||
|
||||
// It makes things more readable to have two different OMAP types. We cast
|
||||
// normal OMAPs into these. They must be the same size as the OMAP structure
|
||||
// for this to work, hence the static asserts.
|
||||
struct OmapOrigToTran {
|
||||
DWORD rva_original;
|
||||
DWORD rva_transformed;
|
||||
};
|
||||
struct OmapTranToOrig {
|
||||
DWORD rva_transformed;
|
||||
DWORD rva_original;
|
||||
};
|
||||
static_assert(sizeof(OmapOrigToTran) == sizeof(OMAP),
|
||||
"OmapOrigToTran must have same size as OMAP.");
|
||||
static_assert(sizeof(OmapTranToOrig) == sizeof(OMAP),
|
||||
"OmapTranToOrig must have same size as OMAP.");
|
||||
typedef std::vector<OmapOrigToTran> OmapFromTable;
|
||||
typedef std::vector<OmapTranToOrig> OmapToTable;
|
||||
|
||||
// Used for sorting and searching through a Mapping.
|
||||
bool MappedRangeOriginalLess(const MappedRange& lhs, const MappedRange& rhs) {
|
||||
if (lhs.rva_original < rhs.rva_original)
|
||||
return true;
|
||||
if (lhs.rva_original > rhs.rva_original)
|
||||
return false;
|
||||
return lhs.length < rhs.length;
|
||||
}
|
||||
bool MappedRangeMappedLess(const MappedRange& lhs, const MappedRange& rhs) {
|
||||
if (lhs.rva_transformed < rhs.rva_transformed)
|
||||
return true;
|
||||
if (lhs.rva_transformed > rhs.rva_transformed)
|
||||
return false;
|
||||
return lhs.length < rhs.length;
|
||||
}
|
||||
|
||||
// Used for searching through the EndpointIndexMap.
|
||||
bool EndpointIndexLess(const EndpointIndex& ei1, const EndpointIndex& ei2) {
|
||||
return ei1.endpoint < ei2.endpoint;
|
||||
}
|
||||
|
||||
// Finds the debug stream with the given |name| in the given |session|, and
|
||||
// populates |table| with its contents. Casts the data directly into OMAP
|
||||
// structs.
|
||||
bool FindAndLoadOmapTable(const wchar_t* name,
|
||||
IDiaSession* session,
|
||||
OmapTable* table) {
|
||||
assert(name != NULL);
|
||||
assert(session != NULL);
|
||||
assert(table != NULL);
|
||||
|
||||
CComPtr<IDiaEnumDebugStreamData> stream;
|
||||
if (!FindDebugStream(name, session, &stream))
|
||||
return false;
|
||||
assert(stream.p != NULL);
|
||||
|
||||
LONG count = 0;
|
||||
if (FAILED(stream->get_Count(&count))) {
|
||||
fprintf(stderr, "IDiaEnumDebugStreamData::get_Count failed for stream "
|
||||
"\"%ws\"\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the length of the stream in bytes.
|
||||
DWORD bytes_read = 0;
|
||||
ULONG count_read = 0;
|
||||
if (FAILED(stream->Next(count, 0, &bytes_read, NULL, &count_read))) {
|
||||
fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
|
||||
"length of stream \"%ws\"\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure it's consistent with the OMAP data type.
|
||||
DWORD bytes_expected = count * sizeof(OmapTable::value_type);
|
||||
if (count * sizeof(OmapTable::value_type) != bytes_read) {
|
||||
fprintf(stderr, "DIA debug stream \"%ws\" has an unexpected length", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the table.
|
||||
table->resize(count);
|
||||
bytes_read = 0;
|
||||
count_read = 0;
|
||||
if (FAILED(stream->Next(count, bytes_expected, &bytes_read,
|
||||
reinterpret_cast<BYTE*>(&table->at(0)),
|
||||
&count_read))) {
|
||||
fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
|
||||
"data from stream \"%ws\"\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This determines the original image length by looking through the segment
|
||||
// table.
|
||||
bool GetOriginalImageLength(IDiaSession* session, DWORD* image_length) {
|
||||
assert(session != NULL);
|
||||
assert(image_length != NULL);
|
||||
|
||||
CComPtr<IDiaEnumSegments> enum_segments;
|
||||
if (!FindTable(session, &enum_segments))
|
||||
return false;
|
||||
assert(enum_segments.p != NULL);
|
||||
|
||||
DWORD temp_image_length = 0;
|
||||
CComPtr<IDiaSegment> segment;
|
||||
ULONG fetched = 0;
|
||||
while (SUCCEEDED(enum_segments->Next(1, &segment, &fetched)) &&
|
||||
fetched == 1) {
|
||||
assert(segment.p != NULL);
|
||||
|
||||
DWORD rva = 0;
|
||||
DWORD length = 0;
|
||||
DWORD frame = 0;
|
||||
if (FAILED(segment->get_relativeVirtualAddress(&rva)) ||
|
||||
FAILED(segment->get_length(&length)) ||
|
||||
FAILED(segment->get_frame(&frame))) {
|
||||
fprintf(stderr, "Failed to get basic properties for IDiaSegment\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frame > 0) {
|
||||
DWORD segment_end = rva + length;
|
||||
if (segment_end > temp_image_length)
|
||||
temp_image_length = segment_end;
|
||||
}
|
||||
segment.Release();
|
||||
}
|
||||
|
||||
*image_length = temp_image_length;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detects regions of the original image that have been removed in the
|
||||
// transformed image, and sets the 'removed' property on all mapped ranges
|
||||
// immediately preceding a gap. The mapped ranges must be sorted by
|
||||
// 'rva_original'.
|
||||
void FillInRemovedLengths(Mapping* mapping) {
|
||||
assert(mapping != NULL);
|
||||
|
||||
// Find and fill gaps. We do this with two sweeps. We first sweep forward
|
||||
// looking for gaps. When we identify a gap we then sweep forward with a
|
||||
// second scan and set the 'removed' property for any intervals that
|
||||
// immediately precede the gap.
|
||||
//
|
||||
// Gaps are typically between two successive intervals, but not always:
|
||||
//
|
||||
// Range 1: ---------------
|
||||
// Range 2: -------
|
||||
// Range 3: -------------
|
||||
// Gap : ******
|
||||
//
|
||||
// In the above example the gap is between range 1 and range 3. A forward
|
||||
// sweep finds the gap, and a second forward sweep identifies that range 1
|
||||
// immediately precedes the gap and sets its 'removed' property.
|
||||
|
||||
size_t fill = 0;
|
||||
DWORD rva_front = 0;
|
||||
for (size_t find = 0; find < mapping->size(); ++find) {
|
||||
#ifndef NDEBUG
|
||||
// We expect the mapped ranges to be sorted by 'rva_original'.
|
||||
if (find > 0) {
|
||||
assert(mapping->at(find - 1).rva_original <=
|
||||
mapping->at(find).rva_original);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rva_front < mapping->at(find).rva_original) {
|
||||
// We've found a gap. Fill it in by setting the 'removed' property for
|
||||
// any affected intervals.
|
||||
DWORD removed = mapping->at(find).rva_original - rva_front;
|
||||
for (; fill < find; ++fill) {
|
||||
if (mapping->at(fill).rva_original + mapping->at(fill).length !=
|
||||
rva_front) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This interval ends right where the gap starts. It needs to have its
|
||||
// 'removed' information filled in.
|
||||
mapping->at(fill).removed = removed;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the front that indicates the covered portion of the image.
|
||||
rva_front = mapping->at(find).rva_original + mapping->at(find).length;
|
||||
}
|
||||
}
|
||||
|
||||
// Builds a unified view of the mapping between the original and transformed
|
||||
// image space by merging OMAPTO and OMAPFROM data.
|
||||
void BuildMapping(const OmapData& omap_data, Mapping* mapping) {
|
||||
assert(mapping != NULL);
|
||||
|
||||
mapping->clear();
|
||||
|
||||
if (omap_data.omap_from.empty() || omap_data.omap_to.empty())
|
||||
return;
|
||||
|
||||
// The names 'omap_to' and 'omap_from' are awfully confusing, so we make
|
||||
// ourselves more explicit here. This cast is only safe because the underlying
|
||||
// types have the exact same size.
|
||||
const OmapToTable& tran2orig =
|
||||
reinterpret_cast<const OmapToTable&>(omap_data.omap_to);
|
||||
const OmapFromTable& orig2tran = reinterpret_cast<const OmapFromTable&>(
|
||||
omap_data.omap_from);
|
||||
|
||||
// Handle the range of data at the beginning of the image. This is not usually
|
||||
// specified by the OMAP data.
|
||||
if (tran2orig[0].rva_transformed > 0 && orig2tran[0].rva_original > 0) {
|
||||
DWORD header_transformed = tran2orig[0].rva_transformed;
|
||||
DWORD header_original = orig2tran[0].rva_original;
|
||||
DWORD header = Min(header_transformed, header_original);
|
||||
|
||||
MappedRange mr = {};
|
||||
mr.length = header;
|
||||
mr.injected = header_transformed - header;
|
||||
mr.removed = header_original - header;
|
||||
mapping->push_back(mr);
|
||||
}
|
||||
|
||||
// Convert the implied lengths to explicit lengths, and infer which content
|
||||
// has been injected into the transformed image. Injected content is inferred
|
||||
// as regions of the transformed address space that does not map back to
|
||||
// known valid content in the original image.
|
||||
for (size_t i = 0; i < tran2orig.size(); ++i) {
|
||||
const OmapTranToOrig& o1 = tran2orig[i];
|
||||
|
||||
// This maps to content that is outside the original image, thus it
|
||||
// describes injected content. We can skip this entry.
|
||||
if (o1.rva_original >= omap_data.length_original)
|
||||
continue;
|
||||
|
||||
// Calculate the length of the current OMAP entry. This is implicit as the
|
||||
// distance between successive |rva| values, capped at the end of the
|
||||
// original image.
|
||||
DWORD length = 0;
|
||||
if (i + 1 < tran2orig.size()) {
|
||||
const OmapTranToOrig& o2 = tran2orig[i + 1];
|
||||
|
||||
// We expect the table to be sorted by rva_transformed.
|
||||
assert(o1.rva_transformed <= o2.rva_transformed);
|
||||
|
||||
length = o2.rva_transformed - o1.rva_transformed;
|
||||
if (o1.rva_original + length > omap_data.length_original) {
|
||||
length = omap_data.length_original - o1.rva_original;
|
||||
}
|
||||
} else {
|
||||
length = omap_data.length_original - o1.rva_original;
|
||||
}
|
||||
|
||||
// Zero-length entries don't describe anything and can be ignored.
|
||||
if (length == 0)
|
||||
continue;
|
||||
|
||||
// Any gaps in the transformed address-space are due to injected content.
|
||||
if (!mapping->empty()) {
|
||||
MappedRange& prev_mr = mapping->back();
|
||||
prev_mr.injected += o1.rva_transformed -
|
||||
(prev_mr.rva_transformed + prev_mr.length);
|
||||
}
|
||||
|
||||
MappedRange mr = {};
|
||||
mr.rva_original = o1.rva_original;
|
||||
mr.rva_transformed = o1.rva_transformed;
|
||||
mr.length = length;
|
||||
mapping->push_back(mr);
|
||||
}
|
||||
|
||||
// Sort based on the original image addresses.
|
||||
std::sort(mapping->begin(), mapping->end(), MappedRangeOriginalLess);
|
||||
|
||||
// Fill in the 'removed' lengths by looking for gaps in the coverage of the
|
||||
// original address space.
|
||||
FillInRemovedLengths(mapping);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void BuildEndpointIndexMap(ImageMap* image_map) {
|
||||
assert(image_map != NULL);
|
||||
|
||||
if (image_map->mapping.size() == 0)
|
||||
return;
|
||||
|
||||
const Mapping& mapping = image_map->mapping;
|
||||
EndpointIndexMap& eim = image_map->endpoint_index_map;
|
||||
|
||||
// Get the unique set of interval endpoints.
|
||||
std::set<DWORD> endpoints;
|
||||
for (size_t i = 0; i < mapping.size(); ++i) {
|
||||
endpoints.insert(mapping[i].rva_original);
|
||||
endpoints.insert(mapping[i].rva_original +
|
||||
mapping[i].length +
|
||||
mapping[i].removed);
|
||||
}
|
||||
|
||||
// Use the endpoints to initialize the secondary search structure for the
|
||||
// mapping.
|
||||
eim.resize(endpoints.size());
|
||||
std::set<DWORD>::const_iterator it = endpoints.begin();
|
||||
for (size_t i = 0; it != endpoints.end(); ++it, ++i) {
|
||||
eim[i].endpoint = *it;
|
||||
eim[i].index = mapping.size();
|
||||
}
|
||||
|
||||
// For each endpoint we want the smallest index of any interval containing
|
||||
// it. We iterate over the intervals and update the indices associated with
|
||||
// each interval endpoint contained in the current interval. In the general
|
||||
// case of an arbitrary set of intervals this is O(n^2), but the structure of
|
||||
// OMAP data makes this O(n).
|
||||
for (size_t i = 0; i < mapping.size(); ++i) {
|
||||
EndpointIndex ei1 = { mapping[i].rva_original, 0 };
|
||||
EndpointIndexMap::iterator it1 = std::lower_bound(
|
||||
eim.begin(), eim.end(), ei1, EndpointIndexLess);
|
||||
|
||||
EndpointIndex ei2 = { mapping[i].rva_original + mapping[i].length +
|
||||
mapping[i].removed, 0 };
|
||||
EndpointIndexMap::iterator it2 = std::lower_bound(
|
||||
eim.begin(), eim.end(), ei2, EndpointIndexLess);
|
||||
|
||||
for (; it1 != it2; ++it1)
|
||||
it1->index = Min(i, it1->index);
|
||||
}
|
||||
}
|
||||
|
||||
// Clips the given mapped range.
|
||||
void ClipMappedRangeOriginal(const AddressRange& clip_range,
|
||||
MappedRange* mapped_range) {
|
||||
assert(mapped_range != NULL);
|
||||
|
||||
// The clipping range is entirely outside of the mapped range.
|
||||
if (clip_range.end() <= mapped_range->rva_original ||
|
||||
mapped_range->rva_original + mapped_range->length +
|
||||
mapped_range->removed <= clip_range.rva) {
|
||||
mapped_range->length = 0;
|
||||
mapped_range->injected = 0;
|
||||
mapped_range->removed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Clip the left side.
|
||||
if (mapped_range->rva_original < clip_range.rva) {
|
||||
DWORD clip_left = clip_range.rva - mapped_range->rva_original;
|
||||
mapped_range->rva_original += clip_left;
|
||||
mapped_range->rva_transformed += clip_left;
|
||||
|
||||
if (clip_left > mapped_range->length) {
|
||||
// The left clipping boundary entirely erases the content section of the
|
||||
// range.
|
||||
DWORD trim = clip_left - mapped_range->length;
|
||||
mapped_range->length = 0;
|
||||
mapped_range->injected -= Min(trim, mapped_range->injected);
|
||||
// We know that trim <= mapped_range->remove.
|
||||
mapped_range->removed -= trim;
|
||||
} else {
|
||||
// The left clipping boundary removes some, but not all, of the content.
|
||||
// As such it leaves the removed/injected component intact.
|
||||
mapped_range->length -= clip_left;
|
||||
}
|
||||
}
|
||||
|
||||
// Clip the right side.
|
||||
DWORD end_original = mapped_range->rva_original + mapped_range->length;
|
||||
if (clip_range.end() < end_original) {
|
||||
// The right clipping boundary lands in the 'content' section of the range,
|
||||
// entirely clearing the injected/removed portion.
|
||||
DWORD clip_right = end_original - clip_range.end();
|
||||
mapped_range->length -= clip_right;
|
||||
mapped_range->injected = 0;
|
||||
mapped_range->removed = 0;
|
||||
return;
|
||||
} else {
|
||||
// The right clipping boundary is outside of the content, but may affect
|
||||
// the removed/injected portion of the range.
|
||||
DWORD end_removed = end_original + mapped_range->removed;
|
||||
if (clip_range.end() < end_removed)
|
||||
mapped_range->removed = clip_range.end() - end_original;
|
||||
|
||||
DWORD end_injected = end_original + mapped_range->injected;
|
||||
if (clip_range.end() < end_injected)
|
||||
mapped_range->injected = clip_range.end() - end_original;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int AddressRange::Compare(const AddressRange& rhs) const {
|
||||
if (end() <= rhs.rva)
|
||||
return -1;
|
||||
if (rhs.end() <= rva)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GetOmapDataAndDisableTranslation(IDiaSession* session,
|
||||
OmapData* omap_data) {
|
||||
assert(session != NULL);
|
||||
assert(omap_data != NULL);
|
||||
|
||||
CComPtr<IDiaAddressMap> address_map;
|
||||
if (FAILED(session->QueryInterface(&address_map))) {
|
||||
fprintf(stderr, "IDiaSession::QueryInterface(IDiaAddressMap) failed\n");
|
||||
return false;
|
||||
}
|
||||
assert(address_map.p != NULL);
|
||||
|
||||
BOOL omap_enabled = FALSE;
|
||||
if (FAILED(address_map->get_addressMapEnabled(&omap_enabled))) {
|
||||
fprintf(stderr, "IDiaAddressMap::get_addressMapEnabled failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!omap_enabled) {
|
||||
// We indicate the non-presence of OMAP data by returning empty tables.
|
||||
omap_data->omap_from.clear();
|
||||
omap_data->omap_to.clear();
|
||||
omap_data->length_original = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// OMAP data is present. Disable translation.
|
||||
if (FAILED(address_map->put_addressMapEnabled(FALSE))) {
|
||||
fprintf(stderr, "IDiaAddressMap::put_addressMapEnabled failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the OMAP streams.
|
||||
if (!FindAndLoadOmapTable(kOmapFromDebugStreamName,
|
||||
session,
|
||||
&omap_data->omap_from)) {
|
||||
return false;
|
||||
}
|
||||
if (!FindAndLoadOmapTable(kOmapToDebugStreamName,
|
||||
session,
|
||||
&omap_data->omap_to)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the lengths of the address spaces.
|
||||
if (!GetOriginalImageLength(session, &omap_data->length_original))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BuildImageMap(const OmapData& omap_data, ImageMap* image_map) {
|
||||
assert(image_map != NULL);
|
||||
|
||||
BuildMapping(omap_data, &image_map->mapping);
|
||||
BuildEndpointIndexMap(image_map);
|
||||
}
|
||||
|
||||
void MapAddressRange(const ImageMap& image_map,
|
||||
const AddressRange& original_range,
|
||||
AddressRangeVector* mapped_ranges) {
|
||||
assert(mapped_ranges != NULL);
|
||||
|
||||
const Mapping& map = image_map.mapping;
|
||||
|
||||
// Handle the trivial case of an empty image_map. This means that there is
|
||||
// no transformation to be applied, and we can simply return the original
|
||||
// range.
|
||||
if (map.empty()) {
|
||||
mapped_ranges->push_back(original_range);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get a query of length 0 we need to handle it by using a non-zero
|
||||
// query length.
|
||||
AddressRange query_range(original_range);
|
||||
if (query_range.length == 0)
|
||||
query_range.length = 1;
|
||||
|
||||
// Find the range of intervals that can potentially intersect our query range.
|
||||
size_t imin = 0;
|
||||
size_t imax = 0;
|
||||
{
|
||||
// The index of the earliest possible range that can affect is us done by
|
||||
// searching through the secondary indexing structure.
|
||||
const EndpointIndexMap& eim = image_map.endpoint_index_map;
|
||||
EndpointIndex q1 = { query_range.rva, 0 };
|
||||
EndpointIndexMap::const_iterator it1 = std::lower_bound(
|
||||
eim.begin(), eim.end(), q1, EndpointIndexLess);
|
||||
if (it1 == eim.end()) {
|
||||
imin = map.size();
|
||||
} else {
|
||||
// Backup to find the interval that contains our query point.
|
||||
if (it1 != eim.begin() && query_range.rva < it1->endpoint)
|
||||
--it1;
|
||||
imin = it1->index;
|
||||
}
|
||||
|
||||
// The first range that can't possibly intersect us is found by searching
|
||||
// through the image map directly as it is already sorted by interval start
|
||||
// point.
|
||||
MappedRange q2 = { query_range.end(), 0 };
|
||||
Mapping::const_iterator it2 = std::lower_bound(
|
||||
map.begin(), map.end(), q2, MappedRangeOriginalLess);
|
||||
imax = it2 - map.begin();
|
||||
}
|
||||
|
||||
// Find all intervals that intersect the query range.
|
||||
Mapping temp_map;
|
||||
for (size_t i = imin; i < imax; ++i) {
|
||||
MappedRange mr = map[i];
|
||||
ClipMappedRangeOriginal(query_range, &mr);
|
||||
if (mr.length + mr.injected > 0)
|
||||
temp_map.push_back(mr);
|
||||
}
|
||||
|
||||
// If there are no intersecting ranges then the query range has been removed
|
||||
// from the image in question.
|
||||
if (temp_map.empty())
|
||||
return;
|
||||
|
||||
// Sort based on transformed addresses.
|
||||
std::sort(temp_map.begin(), temp_map.end(), MappedRangeMappedLess);
|
||||
|
||||
// Zero-length queries can't actually be merged. We simply output the set of
|
||||
// unique RVAs that correspond to the query RVA.
|
||||
if (original_range.length == 0) {
|
||||
mapped_ranges->push_back(AddressRange(temp_map[0].rva_transformed, 0));
|
||||
for (size_t i = 1; i < temp_map.size(); ++i) {
|
||||
if (temp_map[i].rva_transformed > mapped_ranges->back().rva)
|
||||
mapped_ranges->push_back(AddressRange(temp_map[i].rva_transformed, 0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Merge any ranges that are consecutive in the mapped image. We merge over
|
||||
// injected content if it makes ranges contiguous, but we ignore any injected
|
||||
// content at the tail end of a range. This allows us to detect symbols that
|
||||
// have been lengthened by injecting content in the middle. However, it
|
||||
// misses the case where content has been injected at the head or the tail.
|
||||
// The problem is that it doesn't know whether to attribute it to the
|
||||
// preceding or following symbol. It is up to the author of the transform to
|
||||
// output explicit OMAP info in these cases to ensure full coverage of the
|
||||
// transformed address space.
|
||||
DWORD rva_begin = temp_map[0].rva_transformed;
|
||||
DWORD rva_cur_content = rva_begin + temp_map[0].length;
|
||||
DWORD rva_cur_injected = rva_cur_content + temp_map[0].injected;
|
||||
for (size_t i = 1; i < temp_map.size(); ++i) {
|
||||
if (rva_cur_injected < temp_map[i].rva_transformed) {
|
||||
// This marks the end of a continuous range in the image. Output the
|
||||
// current range and start a new one.
|
||||
if (rva_begin < rva_cur_content) {
|
||||
mapped_ranges->push_back(
|
||||
AddressRange(rva_begin, rva_cur_content - rva_begin));
|
||||
}
|
||||
rva_begin = temp_map[i].rva_transformed;
|
||||
}
|
||||
|
||||
rva_cur_content = temp_map[i].rva_transformed + temp_map[i].length;
|
||||
rva_cur_injected = rva_cur_content + temp_map[i].injected;
|
||||
}
|
||||
|
||||
// Output the range in progress.
|
||||
if (rva_begin < rva_cur_content) {
|
||||
mapped_ranges->push_back(
|
||||
AddressRange(rva_begin, rva_cur_content - rva_begin));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
72
thirdparty/breakpad/common/windows/omap.h
vendored
Normal file
72
thirdparty/breakpad/common/windows/omap.h
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Provides an API for mapping symbols through OMAP information, if a PDB file
|
||||
// is augmented with it. This allows breakpad to work with addresses in
|
||||
// transformed images by transforming the symbols themselves, rather than
|
||||
// transforming addresses prior to querying symbols (the way it is typically
|
||||
// done by Windows-native tools, including the DIA).
|
||||
|
||||
#ifndef COMMON_WINDOWS_OMAP_H__
|
||||
#define COMMON_WINDOWS_OMAP_H__
|
||||
|
||||
#include "common/windows/omap_internal.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// If the given session contains OMAP data this extracts it, populating
|
||||
// |omap_data|, and then disabling automatic translation for the session.
|
||||
// OMAP data is present in the PDB if |omap_data| is not empty. This returns
|
||||
// true on success, false otherwise.
|
||||
bool GetOmapDataAndDisableTranslation(IDiaSession* dia_session,
|
||||
OmapData* omap_data);
|
||||
|
||||
// Given raw OMAP data builds an ImageMap. This can be used to query individual
|
||||
// image ranges using MapAddressRange.
|
||||
// |omap_data|| is the OMAP data extracted from the PDB.
|
||||
// |image_map| will be populated with a description of the image mapping. If
|
||||
// |omap_data| is empty then this will also be empty.
|
||||
void BuildImageMap(const OmapData& omap_data, ImageMap* image_map);
|
||||
|
||||
// Given an address range in the original image space determines how exactly it
|
||||
// has been tranformed.
|
||||
// |omap_data| is the OMAP data extracted from the PDB, which must not be
|
||||
// empty.
|
||||
// |original_range| is the address range in the original image being queried.
|
||||
// |mapped_ranges| will be populated with a full description of the mapping.
|
||||
// They may be disjoint in the transformed image so a vector is needed to
|
||||
// fully represent the mapping. This will be appended to if it is not
|
||||
// empty. If |omap_data| is empty then |mapped_ranges| will simply be
|
||||
// populated with a copy of |original_range| (the identity transform).
|
||||
void MapAddressRange(const ImageMap& image_map,
|
||||
const AddressRange& original_range,
|
||||
AddressRangeVector* mapped_ranges);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_WINDOWS_OMAP_H__
|
137
thirdparty/breakpad/common/windows/omap_internal.h
vendored
Normal file
137
thirdparty/breakpad/common/windows/omap_internal.h
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Declares internal implementation details for functionality in omap.h and
|
||||
// omap.cc.
|
||||
|
||||
#ifndef COMMON_WINDOWS_OMAP_INTERNAL_H__
|
||||
#define COMMON_WINDOWS_OMAP_INTERNAL_H__
|
||||
|
||||
#include <windows.h>
|
||||
#include <dia2.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// The OMAP struct is defined by debughlp.h, which doesn't play nicely with
|
||||
// imagehlp.h. We simply redefine it.
|
||||
struct OMAP {
|
||||
DWORD rva;
|
||||
DWORD rvaTo;
|
||||
};
|
||||
static_assert(sizeof(OMAP) == 8, "Wrong size for OMAP structure.");
|
||||
typedef std::vector<OMAP> OmapTable;
|
||||
|
||||
// This contains the OMAP data extracted from an image.
|
||||
struct OmapData {
|
||||
// The table of OMAP entries describing the transformation from the
|
||||
// original image to the transformed image.
|
||||
OmapTable omap_from;
|
||||
// The table of OMAP entries describing the transformation from the
|
||||
// instrumented image to the original image.
|
||||
OmapTable omap_to;
|
||||
// The length of the original untransformed image.
|
||||
DWORD length_original;
|
||||
|
||||
OmapData() : length_original(0) { }
|
||||
};
|
||||
|
||||
// This represents a range of addresses in an image.
|
||||
struct AddressRange {
|
||||
DWORD rva;
|
||||
DWORD length;
|
||||
|
||||
AddressRange() : rva(0), length(0) { }
|
||||
AddressRange(DWORD rva, DWORD length) : rva(rva), length(length) { }
|
||||
|
||||
// Returns the end address of this range.
|
||||
DWORD end() const { return rva + length; }
|
||||
|
||||
// Addreses only compare as less-than or greater-than if they are not
|
||||
// overlapping. Otherwise, they compare equal.
|
||||
int Compare(const AddressRange& rhs) const;
|
||||
bool operator<(const AddressRange& rhs) const { return Compare(rhs) == -1; }
|
||||
bool operator>(const AddressRange& rhs) const { return Compare(rhs) == 1; }
|
||||
|
||||
// Equality operators compare exact values.
|
||||
bool operator==(const AddressRange& rhs) const {
|
||||
return rva == rhs.rva && length == rhs.length;
|
||||
}
|
||||
bool operator!=(const AddressRange& rhs) const { return !((*this) == rhs); }
|
||||
};
|
||||
|
||||
typedef std::vector<AddressRange> AddressRangeVector;
|
||||
|
||||
// This represents an address range in an original image, and its corresponding
|
||||
// range in the transformed image.
|
||||
struct MappedRange {
|
||||
// An address in the original image.
|
||||
DWORD rva_original;
|
||||
// The corresponding addresses in the transformed image.
|
||||
DWORD rva_transformed;
|
||||
// The length of the address range.
|
||||
DWORD length;
|
||||
// It is possible for code to be injected into a transformed image, for which
|
||||
// there is no corresponding code in the original image. If this range of
|
||||
// transformed image is immediately followed by such injected code we maintain
|
||||
// a record of its length here.
|
||||
DWORD injected;
|
||||
// It is possible for code to be removed from the original image. This happens
|
||||
// for things like padding between blocks. There is no actual content lost,
|
||||
// but the spacing between items may be lost. This keeps track of any removed
|
||||
// content immediately following the |original| range.
|
||||
DWORD removed;
|
||||
};
|
||||
// A vector of mapped ranges is used as a more useful representation of
|
||||
// OMAP data.
|
||||
typedef std::vector<MappedRange> Mapping;
|
||||
|
||||
// Used as a secondary search structure accompanying a Mapping.
|
||||
struct EndpointIndex {
|
||||
DWORD endpoint;
|
||||
size_t index;
|
||||
};
|
||||
typedef std::vector<EndpointIndex> EndpointIndexMap;
|
||||
|
||||
// An ImageMap is vector of mapped ranges, plus a secondary index into it for
|
||||
// doing interval searches. (An interval tree would also work, but is overkill
|
||||
// because we don't need insertion and deletion.)
|
||||
struct ImageMap {
|
||||
// This is a description of the mapping between original and transformed
|
||||
// image, sorted by addresses in the original image.
|
||||
Mapping mapping;
|
||||
// For all interval endpoints in |mapping| this stores the minimum index of
|
||||
// an interval in |mapping| that contains the endpoint. Useful for doing
|
||||
// interval intersection queries.
|
||||
EndpointIndexMap endpoint_index_map;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_WINDOWS_OMAP_INTERNAL_H__
|
330
thirdparty/breakpad/common/windows/omap_unittest.cc
vendored
Normal file
330
thirdparty/breakpad/common/windows/omap_unittest.cc
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Unittests for OMAP related functions.
|
||||
|
||||
#include "common/windows/omap.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Equality operators for ContainerEq. These must be outside of the anonymous
|
||||
// namespace in order for them to be found.
|
||||
bool operator==(const MappedRange& mr1, const MappedRange& mr2) {
|
||||
return mr1.rva_original == mr2.rva_original &&
|
||||
mr1.rva_transformed == mr2.rva_transformed &&
|
||||
mr1.length == mr2.length &&
|
||||
mr1.injected == mr2.injected &&
|
||||
mr1.removed == mr2.removed;
|
||||
}
|
||||
bool operator==(const EndpointIndex& ei1, const EndpointIndex& ei2) {
|
||||
return ei1.endpoint == ei2.endpoint && ei1.index == ei2.index;
|
||||
}
|
||||
|
||||
// Pretty printers for more meaningful error messages. Also need to be outside
|
||||
// the anonymous namespace.
|
||||
std::ostream& operator<<(std::ostream& os, const MappedRange& mr) {
|
||||
os << "MappedRange(rva_original=" << mr.rva_original
|
||||
<< ", rva_transformed=" << mr.rva_transformed
|
||||
<< ", length=" << mr.length
|
||||
<< ", injected=" << mr.injected
|
||||
<< ", removed=" << mr.removed << ")";
|
||||
return os;
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const EndpointIndex& ei) {
|
||||
os << "EndpointIndex(endpoint=" << ei.endpoint
|
||||
<< ", index=" << ei.index << ")";
|
||||
return os;
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const AddressRange& ar) {
|
||||
os << "AddressRange(rva=" << ar.rva << ", length=" << ar.length << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
OMAP CreateOmap(DWORD rva, DWORD rvaTo) {
|
||||
OMAP o = { rva, rvaTo };
|
||||
return o;
|
||||
}
|
||||
|
||||
MappedRange CreateMappedRange(DWORD rva_original,
|
||||
DWORD rva_transformed,
|
||||
DWORD length,
|
||||
DWORD injected,
|
||||
DWORD removed) {
|
||||
MappedRange mr = { rva_original, rva_transformed, length, injected, removed };
|
||||
return mr;
|
||||
}
|
||||
|
||||
EndpointIndex CreateEndpointIndex(DWORD endpoint, size_t index) {
|
||||
EndpointIndex ei = { endpoint, index };
|
||||
return ei;
|
||||
}
|
||||
|
||||
// (C is removed)
|
||||
// Original : A B C D E F G H
|
||||
// Transformed: A B D F E * H1 G1 G2 H2
|
||||
// (* is injected, G is copied, H is split)
|
||||
// A is implied.
|
||||
|
||||
// Layout of the original image.
|
||||
const AddressRange B(100, 15);
|
||||
const AddressRange C(B.end(), 10);
|
||||
const AddressRange D(C.end(), 25);
|
||||
const AddressRange E(D.end(), 10);
|
||||
const AddressRange F(E.end(), 40);
|
||||
const AddressRange G(F.end(), 3);
|
||||
const AddressRange H(G.end(), 7);
|
||||
|
||||
// Layout of the transformed image.
|
||||
const AddressRange Bt(100, 15);
|
||||
const AddressRange Dt(Bt.end(), 20); // D is shortened.
|
||||
const AddressRange Ft(Dt.end(), F.length);
|
||||
const AddressRange Et(Ft.end(), E.length);
|
||||
const AddressRange injected(Et.end(), 5);
|
||||
const AddressRange H1t(injected.end(), 4); // H is split.
|
||||
const AddressRange G1t(H1t.end(), G.length); // G is copied.
|
||||
const AddressRange G2t(G1t.end(), G.length); // G is copied.
|
||||
const AddressRange H2t(G2t.end(), 3); // H is split.
|
||||
|
||||
class BuildImageMapTest : public testing::Test {
|
||||
public:
|
||||
static const DWORD kInvalidAddress = 0xFFFFFFFF;
|
||||
|
||||
void InitOmapData() {
|
||||
omap_data.length_original = H.end();
|
||||
|
||||
// Build the OMAPTO vector (from transformed to original).
|
||||
omap_data.omap_to.push_back(CreateOmap(Bt.rva, B.rva));
|
||||
omap_data.omap_to.push_back(CreateOmap(Dt.rva, D.rva));
|
||||
omap_data.omap_to.push_back(CreateOmap(Ft.rva, F.rva));
|
||||
omap_data.omap_to.push_back(CreateOmap(Et.rva, E.rva));
|
||||
omap_data.omap_to.push_back(CreateOmap(injected.rva, kInvalidAddress));
|
||||
omap_data.omap_to.push_back(CreateOmap(H1t.rva, H.rva));
|
||||
omap_data.omap_to.push_back(CreateOmap(G1t.rva, G.rva));
|
||||
omap_data.omap_to.push_back(CreateOmap(G2t.rva, G.rva));
|
||||
omap_data.omap_to.push_back(CreateOmap(H2t.rva, H.rva + H1t.length));
|
||||
omap_data.omap_to.push_back(CreateOmap(H2t.end(), kInvalidAddress));
|
||||
|
||||
// Build the OMAPFROM vector (from original to transformed).
|
||||
omap_data.omap_from.push_back(CreateOmap(B.rva, Bt.rva));
|
||||
omap_data.omap_from.push_back(CreateOmap(C.rva, kInvalidAddress));
|
||||
omap_data.omap_from.push_back(CreateOmap(D.rva, Dt.rva));
|
||||
omap_data.omap_from.push_back(CreateOmap(E.rva, Et.rva));
|
||||
omap_data.omap_from.push_back(CreateOmap(F.rva, Ft.rva));
|
||||
omap_data.omap_from.push_back(CreateOmap(G.rva, G1t.rva));
|
||||
omap_data.omap_from.push_back(CreateOmap(H.rva, H1t.rva));
|
||||
omap_data.omap_from.push_back(CreateOmap(H.rva + H1t.length, H2t.rva));
|
||||
omap_data.omap_from.push_back(CreateOmap(H.end(), kInvalidAddress));
|
||||
}
|
||||
|
||||
OmapData omap_data;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(BuildImageMapTest, EmptyImageMapOnEmptyOmapData) {
|
||||
ASSERT_EQ(0u, omap_data.omap_from.size());
|
||||
ASSERT_EQ(0u, omap_data.omap_to.size());
|
||||
ASSERT_EQ(0u, omap_data.length_original);
|
||||
|
||||
ImageMap image_map;
|
||||
BuildImageMap(omap_data, &image_map);
|
||||
EXPECT_EQ(0u, image_map.mapping.size());
|
||||
EXPECT_EQ(0u, image_map.endpoint_index_map.size());
|
||||
}
|
||||
|
||||
TEST_F(BuildImageMapTest, ImageMapIsCorrect) {
|
||||
InitOmapData();
|
||||
ASSERT_LE(0u, omap_data.omap_from.size());
|
||||
ASSERT_LE(0u, omap_data.omap_to.size());
|
||||
ASSERT_LE(0u, omap_data.length_original);
|
||||
|
||||
ImageMap image_map;
|
||||
BuildImageMap(omap_data, &image_map);
|
||||
EXPECT_LE(9u, image_map.mapping.size());
|
||||
EXPECT_LE(9u, image_map.endpoint_index_map.size());
|
||||
|
||||
Mapping mapping;
|
||||
mapping.push_back(CreateMappedRange(0, 0, B.rva, 0, 0));
|
||||
// C is removed, and it originally comes immediately after B.
|
||||
mapping.push_back(CreateMappedRange(B.rva, Bt.rva, B.length, 0, C.length));
|
||||
// D is shortened by a length of 5.
|
||||
mapping.push_back(CreateMappedRange(D.rva, Dt.rva, Dt.length, 0, 5));
|
||||
// The injected content comes immediately after E in the transformed image.
|
||||
mapping.push_back(CreateMappedRange(E.rva, Et.rva, E.length, injected.length,
|
||||
0));
|
||||
mapping.push_back(CreateMappedRange(F.rva, Ft.rva, F.length, 0, 0));
|
||||
// G is copied so creates two entries.
|
||||
mapping.push_back(CreateMappedRange(G.rva, G1t.rva, G.length, 0, 0));
|
||||
mapping.push_back(CreateMappedRange(G.rva, G2t.rva, G.length, 0, 0));
|
||||
// H is split, so create two entries.
|
||||
mapping.push_back(CreateMappedRange(H.rva, H1t.rva, H1t.length, 0, 0));
|
||||
mapping.push_back(CreateMappedRange(H.rva + H1t.length, H2t.rva, H2t.length,
|
||||
0, 0));
|
||||
EXPECT_THAT(mapping,
|
||||
testing::ContainerEq(image_map.mapping));
|
||||
|
||||
EndpointIndexMap endpoint_index_map;
|
||||
endpoint_index_map.push_back(CreateEndpointIndex(0, 0));
|
||||
endpoint_index_map.push_back(CreateEndpointIndex(B.rva, 1));
|
||||
endpoint_index_map.push_back(CreateEndpointIndex(D.rva, 2));
|
||||
endpoint_index_map.push_back(CreateEndpointIndex(E.rva, 3));
|
||||
endpoint_index_map.push_back(CreateEndpointIndex(F.rva, 4));
|
||||
// G is duplicated so 2 ranges map back to it, hence the skip from 5 to 7.
|
||||
endpoint_index_map.push_back(CreateEndpointIndex(G.rva, 5));
|
||||
// H is split so we expect 2 endpoints to show up attributed to it.
|
||||
endpoint_index_map.push_back(CreateEndpointIndex(H.rva, 7));
|
||||
endpoint_index_map.push_back(CreateEndpointIndex(H.rva + H1t.length, 8));
|
||||
endpoint_index_map.push_back(CreateEndpointIndex(H.end(), 9));
|
||||
EXPECT_THAT(endpoint_index_map,
|
||||
testing::ContainerEq(image_map.endpoint_index_map));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class MapAddressRangeTest : public BuildImageMapTest {
|
||||
public:
|
||||
typedef BuildImageMapTest Super;
|
||||
virtual void SetUp() {
|
||||
Super::SetUp();
|
||||
InitOmapData();
|
||||
BuildImageMap(omap_data, &image_map);
|
||||
}
|
||||
|
||||
ImageMap image_map;
|
||||
|
||||
private:
|
||||
using BuildImageMapTest::InitOmapData;
|
||||
using BuildImageMapTest::omap_data;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(MapAddressRangeTest, EmptyImageMapReturnsIdentity) {
|
||||
ImageMap im;
|
||||
AddressRangeVector mapped_ranges;
|
||||
AddressRange ar(0, 1024);
|
||||
MapAddressRange(im, ar, &mapped_ranges);
|
||||
EXPECT_EQ(1u, mapped_ranges.size());
|
||||
EXPECT_EQ(ar, mapped_ranges[0]);
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapOutOfImage) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
MapAddressRange(image_map, AddressRange(H.end() + 10, 10), &mapped_ranges);
|
||||
EXPECT_EQ(0u, mapped_ranges.size());
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapIdentity) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
MapAddressRange(image_map, B, &mapped_ranges);
|
||||
EXPECT_EQ(1u, mapped_ranges.size());
|
||||
EXPECT_THAT(mapped_ranges, testing::ElementsAre(B));
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapReorderedContiguous) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
|
||||
AddressRange DEF(D.rva, F.end() - D.rva);
|
||||
MapAddressRange(image_map, DEF, &mapped_ranges);
|
||||
EXPECT_EQ(1u, mapped_ranges.size());
|
||||
|
||||
AddressRange DFEt(Dt.rva, Et.end() - Dt.rva);
|
||||
EXPECT_THAT(mapped_ranges, testing::ElementsAre(DFEt));
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapEmptySingle) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
MapAddressRange(image_map, AddressRange(D.rva, 0), &mapped_ranges);
|
||||
EXPECT_EQ(1u, mapped_ranges.size());
|
||||
EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(Dt.rva, 0)));
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapEmptyCopied) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
MapAddressRange(image_map, AddressRange(G.rva, 0), &mapped_ranges);
|
||||
EXPECT_EQ(2u, mapped_ranges.size());
|
||||
EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(G1t.rva, 0),
|
||||
AddressRange(G2t.rva, 0)));
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapCopiedContiguous) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
MapAddressRange(image_map, G, &mapped_ranges);
|
||||
EXPECT_EQ(1u, mapped_ranges.size());
|
||||
EXPECT_THAT(mapped_ranges, testing::ElementsAre(
|
||||
AddressRange(G1t.rva, G2t.end() - G1t.rva)));
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapSplitDiscontiguous) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
MapAddressRange(image_map, H, &mapped_ranges);
|
||||
EXPECT_EQ(2u, mapped_ranges.size());
|
||||
EXPECT_THAT(mapped_ranges, testing::ElementsAre(H1t, H2t));
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapInjected) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
|
||||
AddressRange EFGH(E.rva, H.end() - E.rva);
|
||||
MapAddressRange(image_map, EFGH, &mapped_ranges);
|
||||
EXPECT_EQ(1u, mapped_ranges.size());
|
||||
|
||||
AddressRange FEHGGHt(Ft.rva, H2t.end() - Ft.rva);
|
||||
EXPECT_THAT(mapped_ranges, testing::ElementsAre(FEHGGHt));
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapRemovedEntirely) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
MapAddressRange(image_map, C, &mapped_ranges);
|
||||
EXPECT_EQ(0u, mapped_ranges.size());
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapRemovedPartly) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
MapAddressRange(image_map, D, &mapped_ranges);
|
||||
EXPECT_EQ(1u, mapped_ranges.size());
|
||||
EXPECT_THAT(mapped_ranges, testing::ElementsAre(Dt));
|
||||
}
|
||||
|
||||
TEST_F(MapAddressRangeTest, MapFull) {
|
||||
AddressRangeVector mapped_ranges;
|
||||
|
||||
AddressRange AH(0, H.end());
|
||||
MapAddressRange(image_map, AH, &mapped_ranges);
|
||||
EXPECT_EQ(1u, mapped_ranges.size());
|
||||
|
||||
AddressRange AHt(0, H2t.end());
|
||||
EXPECT_THAT(mapped_ranges, testing::ElementsAre(AHt));
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
171
thirdparty/breakpad/common/windows/pdb_source_line_writer.cc
vendored
Normal file → Executable file
171
thirdparty/breakpad/common/windows/pdb_source_line_writer.cc
vendored
Normal file → Executable file
@@ -27,15 +27,26 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// We don't want windows.h to define the macro max() which collides with
|
||||
// std::numeric_limits::max()
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "common/windows/pdb_source_line_writer.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <winnt.h>
|
||||
#include <atlbase.h>
|
||||
#include <dia2.h>
|
||||
#include <ImageHlp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
#include <limits>
|
||||
|
||||
#include "common/windows/pdb_source_line_writer.h"
|
||||
#include "common/windows/dia_util.h"
|
||||
#include "common/windows/guid_string.h"
|
||||
#include "common/windows/string_utils-inl.h"
|
||||
|
||||
// This constant may be missing from DbgHelp.h. See the documentation for
|
||||
// IDiaSymbol::get_undecoratedNameEx.
|
||||
@@ -45,6 +56,8 @@
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
namespace {
|
||||
|
||||
using std::vector;
|
||||
|
||||
// A helper class to scope a PLOADED_IMAGE.
|
||||
@@ -63,6 +76,8 @@ class AutoImage {
|
||||
PLOADED_IMAGE img_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) {
|
||||
}
|
||||
|
||||
@@ -109,7 +124,7 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) {
|
||||
fprintf(stderr, "loadDataForPdb and loadDataFromExe failed for %ws\n", file.c_str());
|
||||
return false;
|
||||
}
|
||||
code_file_ = file;
|
||||
code_file_ = file;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -157,7 +172,12 @@ bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(output_, "%x %x %d %d\n", rva, length, line_num, source_id);
|
||||
AddressRangeVector ranges;
|
||||
MapAddressRange(image_map_, AddressRange(rva, length), &ranges);
|
||||
for (size_t i = 0; i < ranges.size(); ++i) {
|
||||
fprintf(output_, "%x %x %d %d\n", ranges[i].rva, ranges[i].length,
|
||||
line_num, source_id);
|
||||
}
|
||||
line.Release();
|
||||
}
|
||||
return true;
|
||||
@@ -196,8 +216,13 @@ bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function,
|
||||
stack_param_size = GetFunctionStackParamSize(function);
|
||||
}
|
||||
|
||||
fprintf(output_, "FUNC %x %" WIN_STRING_FORMAT_LL "x %x %ws\n",
|
||||
rva, length, stack_param_size, name);
|
||||
AddressRangeVector ranges;
|
||||
MapAddressRange(image_map_, AddressRange(rva, static_cast<DWORD>(length)),
|
||||
&ranges);
|
||||
for (size_t i = 0; i < ranges.size(); ++i) {
|
||||
fprintf(output_, "FUNC %x %x %x %ws\n",
|
||||
ranges[i].rva, ranges[i].length, stack_param_size, name);
|
||||
}
|
||||
|
||||
CComPtr<IDiaEnumLineNumbers> lines;
|
||||
if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) {
|
||||
@@ -369,30 +394,17 @@ bool PDBSourceLineWriter::PrintFrameData() {
|
||||
// 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.
|
||||
|
||||
CComPtr<IDiaEnumTables> tables;
|
||||
if (FAILED(session_->getEnumTables(&tables)))
|
||||
return false;
|
||||
|
||||
// Pick up the first table that supports IDiaEnumFrameData.
|
||||
CComPtr<IDiaEnumFrameData> frame_data_enum;
|
||||
CComPtr<IDiaTable> table;
|
||||
ULONG count;
|
||||
while (!frame_data_enum &&
|
||||
SUCCEEDED(tables->Next(1, &table, &count)) &&
|
||||
count == 1) {
|
||||
table->QueryInterface(_uuidof(IDiaEnumFrameData),
|
||||
reinterpret_cast<void**>(&frame_data_enum));
|
||||
table.Release();
|
||||
}
|
||||
if (!frame_data_enum)
|
||||
if (!FindTable(session_, &frame_data_enum))
|
||||
return false;
|
||||
|
||||
DWORD last_type = -1;
|
||||
DWORD last_rva = -1;
|
||||
DWORD last_type = std::numeric_limits<DWORD>::max();
|
||||
DWORD last_rva = std::numeric_limits<DWORD>::max();
|
||||
DWORD last_code_size = 0;
|
||||
DWORD last_prolog_size = -1;
|
||||
DWORD last_prolog_size = std::numeric_limits<DWORD>::max();
|
||||
|
||||
CComPtr<IDiaFrameData> frame_data;
|
||||
ULONG count = 0;
|
||||
while (SUCCEEDED(frame_data_enum->Next(1, &frame_data, &count)) &&
|
||||
count == 1) {
|
||||
DWORD type;
|
||||
@@ -411,9 +423,6 @@ bool PDBSourceLineWriter::PrintFrameData() {
|
||||
if (FAILED(frame_data->get_lengthProlog(&prolog_size)))
|
||||
return false;
|
||||
|
||||
// epliog_size is always 0.
|
||||
DWORD epilog_size = 0;
|
||||
|
||||
// parameter_size is the size of parameters passed on the stack. If any
|
||||
// parameters are not passed on the stack (such as in registers), their
|
||||
// sizes will not be included in parameter_size.
|
||||
@@ -460,14 +469,67 @@ bool PDBSourceLineWriter::PrintFrameData() {
|
||||
// this check reduces the size of the dumped symbol file by a third.
|
||||
if (type != last_type || rva != last_rva || code_size != last_code_size ||
|
||||
prolog_size != last_prolog_size) {
|
||||
fprintf(output_, "STACK WIN %x %x %x %x %x %x %x %x %x %d ",
|
||||
type, rva, code_size, prolog_size, epilog_size,
|
||||
parameter_size, saved_register_size, local_size, max_stack_size,
|
||||
program_string_result == S_OK);
|
||||
if (program_string_result == S_OK) {
|
||||
fprintf(output_, "%ws\n", program_string);
|
||||
// The prolog and the code portions of the frame have to be treated
|
||||
// independently as they may have independently changed in size, or may
|
||||
// even have been split.
|
||||
// NOTE: If epilog size is ever non-zero, we have to do something
|
||||
// similar with it.
|
||||
|
||||
// Figure out where the prolog bytes have landed.
|
||||
AddressRangeVector prolog_ranges;
|
||||
if (prolog_size > 0) {
|
||||
MapAddressRange(image_map_, AddressRange(rva, prolog_size),
|
||||
&prolog_ranges);
|
||||
}
|
||||
|
||||
// And figure out where the code bytes have landed.
|
||||
AddressRangeVector code_ranges;
|
||||
MapAddressRange(image_map_,
|
||||
AddressRange(rva + prolog_size,
|
||||
code_size - prolog_size),
|
||||
&code_ranges);
|
||||
|
||||
struct FrameInfo {
|
||||
DWORD rva;
|
||||
DWORD code_size;
|
||||
DWORD prolog_size;
|
||||
};
|
||||
std::vector<FrameInfo> frame_infos;
|
||||
|
||||
// Special case: The prolog and the code bytes remain contiguous. This is
|
||||
// only done for compactness of the symbol file, and we could actually
|
||||
// be outputting independent frame info for the prolog and code portions.
|
||||
if (prolog_ranges.size() == 1 && code_ranges.size() == 1 &&
|
||||
prolog_ranges[0].end() == code_ranges[0].rva) {
|
||||
FrameInfo fi = { prolog_ranges[0].rva,
|
||||
prolog_ranges[0].length + code_ranges[0].length,
|
||||
prolog_ranges[0].length };
|
||||
frame_infos.push_back(fi);
|
||||
} else {
|
||||
fprintf(output_, "%d\n", allocates_base_pointer);
|
||||
// Otherwise we output the prolog and code frame info independently.
|
||||
for (size_t i = 0; i < prolog_ranges.size(); ++i) {
|
||||
FrameInfo fi = { prolog_ranges[i].rva,
|
||||
prolog_ranges[i].length,
|
||||
prolog_ranges[i].length };
|
||||
frame_infos.push_back(fi);
|
||||
}
|
||||
for (size_t i = 0; i < code_ranges.size(); ++i) {
|
||||
FrameInfo fi = { code_ranges[i].rva, code_ranges[i].length, 0 };
|
||||
frame_infos.push_back(fi);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < frame_infos.size(); ++i) {
|
||||
const FrameInfo& fi(frame_infos[i]);
|
||||
fprintf(output_, "STACK WIN %x %x %x %x %x %x %x %x %x %d ",
|
||||
type, fi.rva, fi.code_size, fi.prolog_size,
|
||||
0 /* epilog_size */, parameter_size, saved_register_size,
|
||||
local_size, max_stack_size, program_string_result == S_OK);
|
||||
if (program_string_result == S_OK) {
|
||||
fprintf(output_, "%ws\n", program_string);
|
||||
} else {
|
||||
fprintf(output_, "%d\n", allocates_base_pointer);
|
||||
}
|
||||
}
|
||||
|
||||
last_type = type;
|
||||
@@ -502,8 +564,12 @@ bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(output_, "PUBLIC %x %x %ws\n", rva,
|
||||
stack_param_size > 0 ? stack_param_size : 0, name);
|
||||
AddressRangeVector ranges;
|
||||
MapAddressRange(image_map_, AddressRange(rva, 1), &ranges);
|
||||
for (size_t i = 0; i < ranges.size(); ++i) {
|
||||
fprintf(output_, "PUBLIC %x %x %ws\n", ranges[i].rva,
|
||||
stack_param_size > 0 ? stack_param_size : 0, name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -530,8 +596,8 @@ bool PDBSourceLineWriter::PrintPEInfo() {
|
||||
}
|
||||
|
||||
fprintf(output_, "INFO CODE_ID %ws %ws\n",
|
||||
info.code_identifier.c_str(),
|
||||
info.code_file.c_str());
|
||||
info.code_identifier.c_str(),
|
||||
info.code_file.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -588,12 +654,12 @@ bool PDBSourceLineWriter::FindPEFile() {
|
||||
for (int i = 0; i < sizeof(extensions) / sizeof(extensions[0]); i++) {
|
||||
size_t dot_pos = file.find_last_of(L".");
|
||||
if (dot_pos != wstring::npos) {
|
||||
file.replace(dot_pos + 1, wstring::npos, extensions[i]);
|
||||
// Check if this file exists.
|
||||
if (GetFileAttributesW(file.c_str()) != INVALID_FILE_ATTRIBUTES) {
|
||||
code_file_ = file;
|
||||
return true;
|
||||
}
|
||||
file.replace(dot_pos + 1, wstring::npos, extensions[i]);
|
||||
// Check if this file exists.
|
||||
if (GetFileAttributesW(file.c_str()) != INVALID_FILE_ATTRIBUTES) {
|
||||
code_file_ = file;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -803,13 +869,20 @@ next_child:
|
||||
bool PDBSourceLineWriter::WriteMap(FILE *map_file) {
|
||||
output_ = map_file;
|
||||
|
||||
// Load the OMAP information, and disable auto-translation of addresses in
|
||||
// preference of doing it ourselves.
|
||||
OmapData omap_data;
|
||||
if (!GetOmapDataAndDisableTranslation(session_, &omap_data))
|
||||
return false;
|
||||
BuildImageMap(omap_data, &image_map_);
|
||||
|
||||
bool ret = PrintPDBInfo();
|
||||
// This is not a critical piece of the symbol file.
|
||||
PrintPEInfo();
|
||||
ret = ret &&
|
||||
PrintSourceFiles() &&
|
||||
PrintFunctions() &&
|
||||
PrintFrameData();
|
||||
PrintSourceFiles() &&
|
||||
PrintFunctions() &&
|
||||
PrintFrameData();
|
||||
|
||||
output_ = NULL;
|
||||
return ret;
|
||||
@@ -956,8 +1029,8 @@ bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) {
|
||||
}
|
||||
wchar_t code_identifier[32];
|
||||
swprintf(code_identifier,
|
||||
sizeof(code_identifier) / sizeof(code_identifier[0]),
|
||||
L"%08X%X", TimeDateStamp, SizeOfImage);
|
||||
sizeof(code_identifier) / sizeof(code_identifier[0]),
|
||||
L"%08X%X", TimeDateStamp, SizeOfImage);
|
||||
info->code_identifier = code_identifier;
|
||||
|
||||
return true;
|
||||
|
@@ -38,6 +38,8 @@
|
||||
#include <hash_map>
|
||||
#include <string>
|
||||
|
||||
#include "common/windows/omap.h"
|
||||
|
||||
struct IDiaEnumLineNumbers;
|
||||
struct IDiaSession;
|
||||
struct IDiaSymbol;
|
||||
@@ -228,6 +230,9 @@ class PDBSourceLineWriter {
|
||||
// This maps unique filenames to file IDs.
|
||||
hash_map<wstring, DWORD> unique_files_;
|
||||
|
||||
// This is used for calculating post-transform symbol addresses and lengths.
|
||||
ImageMap image_map_;
|
||||
|
||||
// Disallow copy ctor and operator=
|
||||
PDBSourceLineWriter(const PDBSourceLineWriter&);
|
||||
void operator=(const PDBSourceLineWriter&);
|
||||
|
Reference in New Issue
Block a user