summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '3rdParty/Breakpad/src/common/windows')
-rw-r--r--3rdParty/Breakpad/src/common/windows/dia_util.cc92
-rw-r--r--3rdParty/Breakpad/src/common/windows/dia_util.h64
-rw-r--r--3rdParty/Breakpad/src/common/windows/guid_string.h8
-rw-r--r--3rdParty/Breakpad/src/common/windows/http_upload.cc420
-rw-r--r--3rdParty/Breakpad/src/common/windows/http_upload.h129
-rw-r--r--3rdParty/Breakpad/src/common/windows/omap.cc716
-rw-r--r--3rdParty/Breakpad/src/common/windows/omap.h72
-rw-r--r--3rdParty/Breakpad/src/common/windows/omap_internal.h140
-rw-r--r--3rdParty/Breakpad/src/common/windows/omap_unittest.cc329
-rw-r--r--3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.cc1412
-rw-r--r--3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.h257
-rw-r--r--3rdParty/Breakpad/src/common/windows/string_utils-inl.h6
-rw-r--r--3rdParty/Breakpad/src/common/windows/string_utils.cc133
13 files changed, 3771 insertions, 7 deletions
diff --git a/3rdParty/Breakpad/src/common/windows/dia_util.cc b/3rdParty/Breakpad/src/common/windows/dia_util.cc
new file mode 100644
index 0000000..f4cfa1d
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/dia_util.cc
@@ -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 \ No newline at end of file
diff --git a/3rdParty/Breakpad/src/common/windows/dia_util.h b/3rdParty/Breakpad/src/common/windows/dia_util.h
new file mode 100644
index 0000000..b9e0df2
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/dia_util.h
@@ -0,0 +1,64 @@
+// 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.
+
+#ifndef COMMON_WINDOWS_DIA_UTIL_H_
+#define COMMON_WINDOWS_DIA_UTIL_H_
+
+#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
+
+#endif // COMMON_WINDOWS_DIA_UTIL_H_
diff --git a/3rdParty/Breakpad/src/common/windows/guid_string.h b/3rdParty/Breakpad/src/common/windows/guid_string.h
index f8aa8a2..48a5c1d 100644
--- a/3rdParty/Breakpad/src/common/windows/guid_string.h
+++ b/3rdParty/Breakpad/src/common/windows/guid_string.h
@@ -29,10 +29,10 @@
// guid_string.cc: Convert GUIDs to strings.
-#ifndef COMMON_WINDOWS_GUID_STRING_H__
-#define COMMON_WINDOWS_GUID_STRING_H__
+#ifndef COMMON_WINDOWS_GUID_STRING_H_
+#define COMMON_WINDOWS_GUID_STRING_H_
-#include <Guiddef.h>
+#include <guiddef.h>
#include <string>
@@ -55,4 +55,4 @@ class GUIDString {
} // namespace google_breakpad
-#endif // COMMON_WINDOWS_GUID_STRING_H__
+#endif // COMMON_WINDOWS_GUID_STRING_H_
diff --git a/3rdParty/Breakpad/src/common/windows/http_upload.cc b/3rdParty/Breakpad/src/common/windows/http_upload.cc
new file mode 100644
index 0000000..7676bdc
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/http_upload.cc
@@ -0,0 +1,420 @@
+// Copyright (c) 2006, 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 <assert.h>
+
+// Disable exception handler warnings.
+#pragma warning(disable:4530)
+
+#include <fstream>
+
+#include "common/windows/string_utils-inl.h"
+
+#include "common/windows/http_upload.h"
+
+namespace google_breakpad {
+
+using std::ifstream;
+using std::ios;
+
+static const wchar_t kUserAgent[] = L"Breakpad/1.0 (Windows)";
+
+// Helper class which closes an internet handle when it goes away
+class HTTPUpload::AutoInternetHandle {
+ public:
+ explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {}
+ ~AutoInternetHandle() {
+ if (handle_) {
+ InternetCloseHandle(handle_);
+ }
+ }
+
+ HINTERNET get() { return handle_; }
+
+ private:
+ HINTERNET handle_;
+};
+
+// static
+bool HTTPUpload::SendRequest(const wstring &url,
+ const map<wstring, wstring> &parameters,
+ const map<wstring, wstring> &files,
+ int *timeout,
+ wstring *response_body,
+ int *response_code) {
+ if (response_code) {
+ *response_code = 0;
+ }
+
+ // TODO(bryner): support non-ASCII parameter names
+ if (!CheckParameters(parameters)) {
+ return false;
+ }
+
+ // Break up the URL and make sure we can handle it
+ wchar_t scheme[16], host[256], path[256];
+ URL_COMPONENTS components;
+ memset(&components, 0, sizeof(components));
+ components.dwStructSize = sizeof(components);
+ components.lpszScheme = scheme;
+ components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]);
+ components.lpszHostName = host;
+ components.dwHostNameLength = sizeof(host) / sizeof(host[0]);
+ components.lpszUrlPath = path;
+ components.dwUrlPathLength = sizeof(path) / sizeof(path[0]);
+ if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
+ 0, &components)) {
+ return false;
+ }
+ bool secure = false;
+ if (wcscmp(scheme, L"https") == 0) {
+ secure = true;
+ } else if (wcscmp(scheme, L"http") != 0) {
+ return false;
+ }
+
+ AutoInternetHandle internet(InternetOpen(kUserAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG,
+ NULL, // proxy name
+ NULL, // proxy bypass
+ 0)); // flags
+ if (!internet.get()) {
+ return false;
+ }
+
+ AutoInternetHandle connection(InternetConnect(internet.get(),
+ host,
+ components.nPort,
+ NULL, // user name
+ NULL, // password
+ INTERNET_SERVICE_HTTP,
+ 0, // flags
+ NULL)); // context
+ if (!connection.get()) {
+ return false;
+ }
+
+ DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0;
+ http_open_flags |= INTERNET_FLAG_NO_COOKIES;
+ AutoInternetHandle request(HttpOpenRequest(connection.get(),
+ L"POST",
+ path,
+ NULL, // version
+ NULL, // referer
+ NULL, // agent type
+ http_open_flags,
+ NULL)); // context
+ if (!request.get()) {
+ return false;
+ }
+
+ wstring boundary = GenerateMultipartBoundary();
+ wstring content_type_header = GenerateRequestHeader(boundary);
+ HttpAddRequestHeaders(request.get(),
+ content_type_header.c_str(),
+ static_cast<DWORD>(-1),
+ HTTP_ADDREQ_FLAG_ADD);
+
+ string request_body;
+ if (!GenerateRequestBody(parameters, files, boundary, &request_body)) {
+ return false;
+ }
+
+ if (timeout) {
+ if (!InternetSetOption(request.get(),
+ INTERNET_OPTION_SEND_TIMEOUT,
+ timeout,
+ sizeof(*timeout))) {
+ fwprintf(stderr, L"Could not unset send timeout, continuing...\n");
+ }
+
+ if (!InternetSetOption(request.get(),
+ INTERNET_OPTION_RECEIVE_TIMEOUT,
+ 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()))) {
+ return false;
+ }
+
+ // The server indicates a successful upload with HTTP status 200.
+ wchar_t http_status[4];
+ DWORD http_status_size = sizeof(http_status);
+ if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE,
+ static_cast<LPVOID>(&http_status), &http_status_size,
+ 0)) {
+ return false;
+ }
+
+ int http_response = wcstol(http_status, NULL, 10);
+ if (response_code) {
+ *response_code = http_response;
+ }
+
+ bool result = (http_response == 200);
+
+ if (result) {
+ result = ReadResponse(request.get(), response_body);
+ }
+
+ return result;
+}
+
+// static
+bool HTTPUpload::ReadResponse(HINTERNET request, wstring *response) {
+ bool has_content_length_header = false;
+ wchar_t content_length[32];
+ DWORD content_length_size = sizeof(content_length);
+ DWORD claimed_size = 0;
+ string response_body;
+
+ if (HttpQueryInfo(request, HTTP_QUERY_CONTENT_LENGTH,
+ static_cast<LPVOID>(&content_length),
+ &content_length_size, 0)) {
+ has_content_length_header = true;
+ claimed_size = wcstol(content_length, NULL, 10);
+ response_body.reserve(claimed_size);
+ }
+
+
+ DWORD bytes_available;
+ DWORD total_read = 0;
+ BOOL return_code;
+
+ while (((return_code = InternetQueryDataAvailable(request, &bytes_available,
+ 0, 0)) != 0) && bytes_available > 0) {
+ vector<char> response_buffer(bytes_available);
+ DWORD size_read;
+
+ return_code = InternetReadFile(request,
+ &response_buffer[0],
+ bytes_available, &size_read);
+
+ if (return_code && size_read > 0) {
+ total_read += size_read;
+ response_body.append(&response_buffer[0], size_read);
+ } else {
+ break;
+ }
+ }
+
+ bool succeeded = return_code && (!has_content_length_header ||
+ (total_read == claimed_size));
+ if (succeeded && response) {
+ *response = UTF8ToWide(response_body);
+ }
+
+ return succeeded;
+}
+
+// static
+wstring HTTPUpload::GenerateMultipartBoundary() {
+ // The boundary has 27 '-' characters followed by 16 hex digits
+ static const wchar_t kBoundaryPrefix[] = L"---------------------------";
+ static const int kBoundaryLength = 27 + 16 + 1;
+
+ // Generate some random numbers to fill out the boundary
+ int r0 = rand();
+ int r1 = rand();
+
+ wchar_t temp[kBoundaryLength];
+ swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
+
+ // remove when VC++7.1 is no longer supported
+ temp[kBoundaryLength - 1] = L'\0';
+
+ return wstring(temp);
+}
+
+// static
+wstring HTTPUpload::GenerateRequestHeader(const wstring &boundary) {
+ wstring header = L"Content-Type: multipart/form-data; boundary=";
+ header += boundary;
+ return header;
+}
+
+// static
+bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> &parameters,
+ const map<wstring, wstring> &files,
+ const wstring &boundary,
+ string *request_body) {
+ string boundary_str = WideToUTF8(boundary);
+ if (boundary_str.empty()) {
+ return false;
+ }
+
+ request_body->clear();
+
+ // Append each of the parameter pairs as a form-data part
+ for (map<wstring, wstring>::const_iterator pos = parameters.begin();
+ pos != parameters.end(); ++pos) {
+ request_body->append("--" + boundary_str + "\r\n");
+ request_body->append("Content-Disposition: form-data; name=\"" +
+ WideToUTF8(pos->first) + "\"\r\n\r\n" +
+ WideToUTF8(pos->second) + "\r\n");
+ }
+
+ for (map<wstring, wstring>::const_iterator pos = files.begin();
+ pos != files.end(); ++pos) {
+ vector<char> contents;
+ if (!GetFileContents(pos->second, &contents)) {
+ return false;
+ }
+
+ // Now append the upload files as a binary (octet-stream) part
+ string filename_utf8 = WideToUTF8(pos->second);
+ if (filename_utf8.empty()) {
+ return false;
+ }
+
+ string file_part_name_utf8 = WideToUTF8(pos->first);
+ if (file_part_name_utf8.empty()) {
+ return false;
+ }
+
+ request_body->append("--" + boundary_str + "\r\n");
+ request_body->append("Content-Disposition: form-data; "
+ "name=\"" + file_part_name_utf8 + "\"; "
+ "filename=\"" + filename_utf8 + "\"\r\n");
+ request_body->append("Content-Type: application/octet-stream\r\n");
+ request_body->append("\r\n");
+
+ if (!contents.empty()) {
+ request_body->append(&(contents[0]), contents.size());
+ }
+ request_body->append("\r\n");
+ }
+ request_body->append("--" + boundary_str + "--\r\n");
+ return true;
+}
+
+// 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.
+ // GCC doesn't support wide file name and opening on FILE* requires ugly
+ // hacks, so fallback to multi byte file.
+#ifdef _MSC_VER
+ ifstream file;
+ file.open(filename.c_str(), ios::binary);
+#else // GCC
+ ifstream file(WideToMBCP(filename, CP_ACP).c_str(), ios::binary);
+#endif // _MSC_VER >= 1400
+ if (file.is_open()) {
+ file.seekg(0, ios::end);
+ std::streamoff length = file.tellg();
+ // 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 rv;
+}
+
+// static
+wstring HTTPUpload::UTF8ToWide(const string &utf8) {
+ if (utf8.length() == 0) {
+ return wstring();
+ }
+
+ // compute the length of the buffer we'll need
+ int charcount = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0);
+
+ if (charcount == 0) {
+ return wstring();
+ }
+
+ // convert
+ wchar_t* buf = new wchar_t[charcount];
+ MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, buf, charcount);
+ wstring result(buf);
+ delete[] buf;
+ return result;
+}
+
+// static
+string HTTPUpload::WideToMBCP(const wstring &wide, unsigned int cp) {
+ if (wide.length() == 0) {
+ return string();
+ }
+
+ // compute the length of the buffer we'll need
+ int charcount = WideCharToMultiByte(cp, 0, wide.c_str(), -1,
+ NULL, 0, NULL, NULL);
+ if (charcount == 0) {
+ return string();
+ }
+
+ // convert
+ char *buf = new char[charcount];
+ WideCharToMultiByte(cp, 0, wide.c_str(), -1, buf, charcount,
+ NULL, NULL);
+
+ string result(buf);
+ delete[] buf;
+ return result;
+}
+
+// static
+bool HTTPUpload::CheckParameters(const map<wstring, wstring> &parameters) {
+ for (map<wstring, wstring>::const_iterator pos = parameters.begin();
+ pos != parameters.end(); ++pos) {
+ const wstring &str = pos->first;
+ if (str.size() == 0) {
+ return false; // disallow empty parameter names
+ }
+ for (unsigned int i = 0; i < str.size(); ++i) {
+ wchar_t c = str[i];
+ if (c < 32 || c == '"' || c > 127) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/windows/http_upload.h b/3rdParty/Breakpad/src/common/windows/http_upload.h
new file mode 100644
index 0000000..f8d48cb
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/http_upload.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2006, 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.
+
+// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST
+// request using wininet. It currently supports requests that contain
+// a set of string parameters (key/value pairs), and a file to upload.
+
+#ifndef COMMON_WINDOWS_HTTP_UPLOAD_H_
+#define COMMON_WINDOWS_HTTP_UPLOAD_H_
+
+#pragma warning(push)
+// Disable exception handler warnings.
+#pragma warning(disable : 4530)
+
+#include <windows.h>
+#include <wininet.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace google_breakpad {
+
+using std::string;
+using std::wstring;
+using std::map;
+using std::vector;
+
+class HTTPUpload {
+ public:
+ // Sends the given sets of parameters and files as a multipart POST
+ // request to the given URL.
+ // Each key in |files| is the name of the file part of the request
+ // (i.e. it corresponds to the name= attribute on an <input type="file">.
+ // Parameter names must contain only printable ASCII characters,
+ // and may not contain a quote (") character.
+ // Only HTTP(S) URLs are currently supported. Returns true on success.
+ // If the request is successful and response_body is non-NULL,
+ // the response body will be returned in response_body.
+ // If response_code is non-NULL, it will be set to the HTTP response code
+ // received (or 0 if the request failed before getting an HTTP response).
+ static bool SendRequest(const wstring &url,
+ const map<wstring, wstring> &parameters,
+ const map<wstring, wstring> &files,
+ int *timeout,
+ wstring *response_body,
+ int *response_code);
+
+ private:
+ class AutoInternetHandle;
+
+ // Retrieves the HTTP response. If NULL is passed in for response,
+ // 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 ReadResponse(HINTERNET request, wstring* response);
+
+ // Generates a new multipart boundary for a POST request
+ static wstring GenerateMultipartBoundary();
+
+ // Generates a HTTP request header for a multipart form submit.
+ static wstring GenerateRequestHeader(const wstring &boundary);
+
+ // Given a set of parameters, a set of upload files, and a file part name,
+ // generates a multipart request body string with these parameters
+ // and minidump contents. Returns true on success.
+ static bool GenerateRequestBody(const map<wstring, wstring> &parameters,
+ const map<wstring, wstring> &files,
+ const wstring &boundary,
+ string *request_body);
+
+ // Fills the supplied vector with the contents of filename.
+ static bool GetFileContents(const wstring &filename, vector<char> *contents);
+
+ // Converts a UTF8 string to UTF16.
+ static wstring UTF8ToWide(const string &utf8);
+
+ // Converts a UTF16 string to UTF8.
+ static string WideToUTF8(const wstring &wide) {
+ return WideToMBCP(wide, CP_UTF8);
+ }
+
+ // Converts a UTF16 string to specified code page.
+ static string WideToMBCP(const wstring &wide, unsigned int cp);
+
+ // Checks that the given list of parameters has only printable
+ // ASCII characters in the parameter name, and does not contain
+ // any quote (") characters. Returns true if so.
+ static bool CheckParameters(const map<wstring, wstring> &parameters);
+
+ // No instances of this class should be created.
+ // Disallow all constructors, destructors, and operator=.
+ HTTPUpload();
+ explicit HTTPUpload(const HTTPUpload &);
+ void operator=(const HTTPUpload &);
+ ~HTTPUpload();
+};
+
+} // namespace google_breakpad
+
+#pragma warning(pop)
+
+#endif // COMMON_WINDOWS_HTTP_UPLOAD_H_
diff --git a/3rdParty/Breakpad/src/common/windows/omap.cc b/3rdParty/Breakpad/src/common/windows/omap.cc
new file mode 100644
index 0000000..ba3ce86
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/omap.cc
@@ -0,0 +1,716 @@
+// 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", name);
+ 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);
+ }
+}
+
+void BuildSubsequentRVAMap(const OmapData &omap_data,
+ std::map<DWORD, DWORD> *subsequent) {
+ assert(subsequent->empty());
+ const OmapFromTable &orig2tran =
+ reinterpret_cast<const OmapFromTable &>(omap_data.omap_from);
+
+ if (orig2tran.empty())
+ return;
+
+ for (size_t i = 0; i < orig2tran.size() - 1; ++i) {
+ // Expect that orig2tran is sorted.
+ if (orig2tran[i].rva_original >= orig2tran[i + 1].rva_original) {
+ fprintf(stderr, "OMAP 'from' table unexpectedly unsorted\n");
+ subsequent->clear();
+ return;
+ }
+ subsequent->insert(std::make_pair(orig2tran[i].rva_original,
+ orig2tran[i + 1].rva_original));
+ }
+}
+
+// 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);
+ BuildSubsequentRVAMap(omap_data, &image_map->subsequent_rva_block);
+}
+
+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
diff --git a/3rdParty/Breakpad/src/common/windows/omap.h b/3rdParty/Breakpad/src/common/windows/omap.h
new file mode 100644
index 0000000..bc293af
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/omap.h
@@ -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_
diff --git a/3rdParty/Breakpad/src/common/windows/omap_internal.h b/3rdParty/Breakpad/src/common/windows/omap_internal.h
new file mode 100644
index 0000000..2a4713d
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/omap_internal.h
@@ -0,0 +1,140 @@
+// 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 <map>
+#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;
+
+ std::map<DWORD, DWORD> subsequent_rva_block;
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_OMAP_INTERNAL_H_
diff --git a/3rdParty/Breakpad/src/common/windows/omap_unittest.cc b/3rdParty/Breakpad/src/common/windows/omap_unittest.cc
new file mode 100644
index 0000000..b244334
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/omap_unittest.cc
@@ -0,0 +1,329 @@
+// 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 "breakpad_googletest_includes.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
diff --git a/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.cc b/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.cc
new file mode 100644
index 0000000..31920ba
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.cc
@@ -0,0 +1,1412 @@
+// Copyright (c) 2006, 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/pdb_source_line_writer.h"
+
+#include <windows.h>
+#include <winnt.h>
+#include <atlbase.h>
+#include <dia2.h>
+#include <diacreate.h>
+#include <ImageHlp.h>
+#include <stdio.h>
+
+#include <limits>
+#include <set>
+
+#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.
+#ifndef UNDNAME_NO_ECSU
+#define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union.
+#endif // UNDNAME_NO_ECSU
+
+/*
+ * Not defined in WinNT.h for some reason. Definitions taken from:
+ * http://uninformed.org/index.cgi?v=4&a=1&p=13
+ *
+ */
+typedef unsigned char UBYTE;
+
+#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];
+};
+
+namespace google_breakpad {
+
+namespace {
+
+using std::vector;
+
+// 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_;
+};
+
+bool SymbolsMatch(IDiaSymbol* a, IDiaSymbol* b) {
+ DWORD a_section, a_offset, b_section, b_offset;
+ if (FAILED(a->get_addressSection(&a_section)) ||
+ FAILED(a->get_addressOffset(&a_offset)) ||
+ FAILED(b->get_addressSection(&b_section)) ||
+ FAILED(b->get_addressOffset(&b_offset)))
+ return false;
+ return a_section == b_section && a_offset == b_offset;
+}
+
+bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) {
+ if (SUCCEEDED(data_source.CoCreateInstance(CLSID_DiaSource))) {
+ return true;
+ }
+
+ class DECLSPEC_UUID("B86AE24D-BF2F-4ac9-B5A2-34B14E4CE11D") DiaSource100;
+ class DECLSPEC_UUID("761D3BCD-1304-41D5-94E8-EAC54E4AC172") DiaSource110;
+ class DECLSPEC_UUID("3BFCEA48-620F-4B6B-81F7-B9AF75454C7D") DiaSource120;
+ class DECLSPEC_UUID("E6756135-1E65-4D17-8576-610761398C3C") DiaSource140;
+
+ // If the CoCreateInstance call above failed, msdia*.dll is not registered.
+ // We can try loading the DLL corresponding to the #included DIA SDK, but
+ // the DIA headers don't provide a version. Lets try to figure out which DIA
+ // version we're compiling against by comparing CLSIDs.
+ const wchar_t *msdia_dll = nullptr;
+ if (CLSID_DiaSource == _uuidof(DiaSource100)) {
+ msdia_dll = L"msdia100.dll";
+ } else if (CLSID_DiaSource == _uuidof(DiaSource110)) {
+ msdia_dll = L"msdia110.dll";
+ } else if (CLSID_DiaSource == _uuidof(DiaSource120)) {
+ msdia_dll = L"msdia120.dll";
+ } else if (CLSID_DiaSource == _uuidof(DiaSource140)) {
+ msdia_dll = L"msdia140.dll";
+ }
+
+ if (msdia_dll &&
+ SUCCEEDED(NoRegCoCreate(msdia_dll, CLSID_DiaSource, IID_IDiaDataSource,
+ reinterpret_cast<void **>(&data_source)))) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) {
+}
+
+PDBSourceLineWriter::~PDBSourceLineWriter() {
+}
+
+bool PDBSourceLineWriter::SetCodeFile(const wstring &exe_file) {
+ if (code_file_.empty()) {
+ code_file_ = exe_file;
+ return true;
+ }
+ // Setting a different code file path is an error. It is success only if the
+ // file paths are the same.
+ return exe_file == code_file_;
+}
+
+bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) {
+ Close();
+ code_file_.clear();
+
+ if (FAILED(CoInitialize(NULL))) {
+ fprintf(stderr, "CoInitialize failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaDataSource> data_source;
+ if (!CreateDiaDataSourceInstance(data_source)) {
+ const int kGuidSize = 64;
+ wchar_t classid[kGuidSize] = {0};
+ StringFromGUID2(CLSID_DiaSource, classid, kGuidSize);
+ fprintf(stderr, "CoCreateInstance CLSID_DiaSource %S failed "
+ "(msdia*.dll unregistered?)\n", classid);
+ return false;
+ }
+
+ switch (format) {
+ case PDB_FILE:
+ if (FAILED(data_source->loadDataFromPdb(file.c_str()))) {
+ fprintf(stderr, "loadDataFromPdb failed for %ws\n", file.c_str());
+ return false;
+ }
+ break;
+ case EXE_FILE:
+ if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) {
+ fprintf(stderr, "loadDataForExe failed for %ws\n", file.c_str());
+ return false;
+ }
+ code_file_ = file;
+ break;
+ case ANY_FILE:
+ if (FAILED(data_source->loadDataFromPdb(file.c_str()))) {
+ if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) {
+ fprintf(stderr, "loadDataForPdb and loadDataFromExe failed for %ws\n",
+ file.c_str());
+ return false;
+ }
+ code_file_ = file;
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown file format\n");
+ return false;
+ }
+
+ if (FAILED(data_source->openSession(&session_))) {
+ fprintf(stderr, "openSession failed\n");
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) {
+ // The line number format is:
+ // <rva> <line number> <source file id>
+ CComPtr<IDiaLineNumber> line;
+ ULONG count;
+
+ while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) {
+ DWORD rva;
+ if (FAILED(line->get_relativeVirtualAddress(&rva))) {
+ fprintf(stderr, "failed to get line rva\n");
+ return false;
+ }
+
+ DWORD length;
+ if (FAILED(line->get_length(&length))) {
+ fprintf(stderr, "failed to get line code length\n");
+ return false;
+ }
+
+ DWORD dia_source_id;
+ if (FAILED(line->get_sourceFileId(&dia_source_id))) {
+ fprintf(stderr, "failed to get line source file id\n");
+ return false;
+ }
+ // duplicate file names are coalesced to share one ID
+ DWORD source_id = GetRealFileID(dia_source_id);
+
+ DWORD line_num;
+ if (FAILED(line->get_lineNumber(&line_num))) {
+ fprintf(stderr, "failed to get line number\n");
+ return false;
+ }
+
+ AddressRangeVector ranges;
+ MapAddressRange(image_map_, AddressRange(rva, length), &ranges);
+ for (size_t i = 0; i < ranges.size(); ++i) {
+ fprintf(output_, "%lx %lx %lu %lu\n", ranges[i].rva, ranges[i].length,
+ line_num, source_id);
+ }
+ line.Release();
+ }
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function,
+ IDiaSymbol *block) {
+ // The function format is:
+ // FUNC <address> <length> <param_stack_size> <function>
+ DWORD rva;
+ if (FAILED(block->get_relativeVirtualAddress(&rva))) {
+ fprintf(stderr, "couldn't get rva\n");
+ return false;
+ }
+
+ ULONGLONG length;
+ if (FAILED(block->get_length(&length))) {
+ fprintf(stderr, "failed to get function length\n");
+ return false;
+ }
+
+ if (length == 0) {
+ // Silently ignore zero-length functions, which can infrequently pop up.
+ return true;
+ }
+
+ CComBSTR name;
+ int stack_param_size;
+ if (!GetSymbolFunctionName(function, &name, &stack_param_size)) {
+ return false;
+ }
+
+ // If the decorated name didn't give the parameter size, try to
+ // calculate it.
+ if (stack_param_size < 0) {
+ stack_param_size = GetFunctionStackParamSize(function);
+ }
+
+ AddressRangeVector ranges;
+ MapAddressRange(image_map_, AddressRange(rva, static_cast<DWORD>(length)),
+ &ranges);
+ for (size_t i = 0; i < ranges.size(); ++i) {
+ fprintf(output_, "FUNC %lx %lx %x %ws\n",
+ ranges[i].rva, ranges[i].length, stack_param_size,
+ name.m_str);
+ }
+
+ CComPtr<IDiaEnumLineNumbers> lines;
+ if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) {
+ return false;
+ }
+
+ if (!PrintLines(lines)) {
+ return false;
+ }
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintSourceFiles() {
+ CComPtr<IDiaSymbol> global;
+ if (FAILED(session_->get_globalScope(&global))) {
+ fprintf(stderr, "get_globalScope failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaEnumSymbols> compilands;
+ if (FAILED(global->findChildren(SymTagCompiland, NULL,
+ nsNone, &compilands))) {
+ fprintf(stderr, "findChildren failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaSymbol> compiland;
+ ULONG count;
+ while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) {
+ CComPtr<IDiaEnumSourceFiles> source_files;
+ if (FAILED(session_->findFile(compiland, NULL, nsNone, &source_files))) {
+ return false;
+ }
+ CComPtr<IDiaSourceFile> file;
+ while (SUCCEEDED(source_files->Next(1, &file, &count)) && count == 1) {
+ DWORD file_id;
+ if (FAILED(file->get_uniqueId(&file_id))) {
+ return false;
+ }
+
+ CComBSTR file_name;
+ if (FAILED(file->get_fileName(&file_name))) {
+ return false;
+ }
+
+ wstring file_name_string(file_name);
+ if (!FileIDIsCached(file_name_string)) {
+ // this is a new file name, cache it and output a FILE line.
+ CacheFileID(file_name_string, file_id);
+ fwprintf(output_, L"FILE %d %ws\n", file_id, file_name_string.c_str());
+ } else {
+ // this file name has already been seen, just save this
+ // ID for later lookup.
+ StoreDuplicateFileID(file_name_string, file_id);
+ }
+ file.Release();
+ }
+ compiland.Release();
+ }
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFunctions() {
+ ULONG count = 0;
+ DWORD rva = 0;
+ CComPtr<IDiaSymbol> global;
+ HRESULT hr;
+
+ if (FAILED(session_->get_globalScope(&global))) {
+ fprintf(stderr, "get_globalScope failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaEnumSymbols> symbols = NULL;
+
+ // Find all function symbols first.
+ std::set<DWORD> rvas;
+ hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols);
+
+ if (SUCCEEDED(hr)) {
+ CComPtr<IDiaSymbol> symbol = NULL;
+
+ while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
+ if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
+ // To maintain existing behavior of one symbol per address, place the
+ // rva onto a set here to uniquify them.
+ rvas.insert(rva);
+ } else {
+ fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
+ return false;
+ }
+
+ symbol.Release();
+ }
+
+ symbols.Release();
+ }
+
+ // Find all public symbols. Store public symbols that are not also private
+ // symbols for later.
+ std::set<DWORD> public_only_rvas;
+ hr = global->findChildren(SymTagPublicSymbol, NULL, nsNone, &symbols);
+
+ if (SUCCEEDED(hr)) {
+ CComPtr<IDiaSymbol> symbol = NULL;
+
+ while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
+ if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
+ if (rvas.count(rva) == 0) {
+ rvas.insert(rva); // Keep symbols in rva order.
+ public_only_rvas.insert(rva);
+ }
+ } else {
+ fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
+ return false;
+ }
+
+ symbol.Release();
+ }
+
+ symbols.Release();
+ }
+
+ std::set<DWORD>::iterator it;
+
+ // For each rva, dump the first symbol DIA knows about at the address.
+ for (it = rvas.begin(); it != rvas.end(); ++it) {
+ CComPtr<IDiaSymbol> symbol = NULL;
+ // If the symbol is not in the public list, look for SymTagFunction. This is
+ // a workaround to a bug where DIA will hang if searching for a private
+ // symbol at an address where only a public symbol exists.
+ // See http://connect.microsoft.com/VisualStudio/feedback/details/722366
+ if (public_only_rvas.count(*it) == 0) {
+ if (SUCCEEDED(session_->findSymbolByRVA(*it, SymTagFunction, &symbol))) {
+ // Sometimes findSymbolByRVA returns S_OK, but NULL.
+ if (symbol) {
+ if (!PrintFunction(symbol, symbol))
+ return false;
+ symbol.Release();
+ }
+ } else {
+ fprintf(stderr, "findSymbolByRVA SymTagFunction failed\n");
+ return false;
+ }
+ } else if (SUCCEEDED(session_->findSymbolByRVA(*it,
+ SymTagPublicSymbol,
+ &symbol))) {
+ // Sometimes findSymbolByRVA returns S_OK, but NULL.
+ if (symbol) {
+ if (!PrintCodePublicSymbol(symbol))
+ return false;
+ symbol.Release();
+ }
+ } else {
+ fprintf(stderr, "findSymbolByRVA SymTagPublicSymbol failed\n");
+ return false;
+ }
+ }
+
+ // When building with PGO, the compiler can split functions into
+ // "hot" and "cold" blocks, and move the "cold" blocks out to separate
+ // pages, so the function can be noncontiguous. To find these blocks,
+ // we have to iterate over all the compilands, and then find blocks
+ // that are children of them. We can then find the lexical parents
+ // of those blocks and print out an extra FUNC line for blocks
+ // that are not contained in their parent functions.
+ CComPtr<IDiaEnumSymbols> compilands;
+ if (FAILED(global->findChildren(SymTagCompiland, NULL,
+ nsNone, &compilands))) {
+ fprintf(stderr, "findChildren failed on the global\n");
+ return false;
+ }
+
+ CComPtr<IDiaSymbol> compiland;
+ while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) {
+ CComPtr<IDiaEnumSymbols> blocks;
+ if (FAILED(compiland->findChildren(SymTagBlock, NULL,
+ nsNone, &blocks))) {
+ fprintf(stderr, "findChildren failed on a compiland\n");
+ return false;
+ }
+
+ CComPtr<IDiaSymbol> block;
+ while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) {
+ // find this block's lexical parent function
+ CComPtr<IDiaSymbol> parent;
+ DWORD tag;
+ if (SUCCEEDED(block->get_lexicalParent(&parent)) &&
+ SUCCEEDED(parent->get_symTag(&tag)) &&
+ tag == SymTagFunction) {
+ // now get the block's offset and the function's offset and size,
+ // and determine if the block is outside of the function
+ DWORD func_rva, block_rva;
+ ULONGLONG func_length;
+ if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) &&
+ SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) &&
+ SUCCEEDED(parent->get_length(&func_length))) {
+ if (block_rva < func_rva || block_rva > (func_rva + func_length)) {
+ if (!PrintFunction(parent, block)) {
+ return false;
+ }
+ }
+ }
+ }
+ parent.Release();
+ block.Release();
+ }
+ blocks.Release();
+ compiland.Release();
+ }
+
+ global.Release();
+ return true;
+}
+
+#undef max
+
+bool PDBSourceLineWriter::PrintFrameDataUsingPDB() {
+ // It would be nice if it were possible to output frame data alongside the
+ // associated function, as is done with line numbers, but the DIA API
+ // doesn't make it possible to get the frame data in that way.
+
+ CComPtr<IDiaEnumFrameData> frame_data_enum;
+ if (!FindTable(session_, &frame_data_enum))
+ return false;
+
+ 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 = 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;
+ if (FAILED(frame_data->get_type(&type)))
+ return false;
+
+ DWORD rva;
+ if (FAILED(frame_data->get_relativeVirtualAddress(&rva)))
+ return false;
+
+ DWORD code_size;
+ if (FAILED(frame_data->get_lengthBlock(&code_size)))
+ return false;
+
+ DWORD prolog_size;
+ if (FAILED(frame_data->get_lengthProlog(&prolog_size)))
+ return false;
+
+ // 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.
+ DWORD parameter_size;
+ if (FAILED(frame_data->get_lengthParams(&parameter_size)))
+ return false;
+
+ DWORD saved_register_size;
+ if (FAILED(frame_data->get_lengthSavedRegisters(&saved_register_size)))
+ return false;
+
+ DWORD local_size;
+ if (FAILED(frame_data->get_lengthLocals(&local_size)))
+ return false;
+
+ // get_maxStack can return S_FALSE, just use 0 in that case.
+ DWORD max_stack_size = 0;
+ if (FAILED(frame_data->get_maxStack(&max_stack_size)))
+ return false;
+
+ // get_programString can return S_FALSE, indicating that there is no
+ // program string. In that case, check whether %ebp is used.
+ HRESULT program_string_result;
+ CComBSTR program_string;
+ if (FAILED(program_string_result = frame_data->get_program(
+ &program_string))) {
+ return false;
+ }
+
+ // get_allocatesBasePointer can return S_FALSE, treat that as though
+ // %ebp is not used.
+ BOOL allocates_base_pointer = FALSE;
+ if (program_string_result != S_OK) {
+ if (FAILED(frame_data->get_allocatesBasePointer(
+ &allocates_base_pointer))) {
+ return false;
+ }
+ }
+
+ // Only print out a line if type, rva, code_size, or prolog_size have
+ // changed from the last line. It is surprisingly common (especially in
+ // system library PDBs) for DIA to return a series of identical
+ // IDiaFrameData objects. For kernel32.pdb from Windows XP SP2 on x86,
+ // 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) {
+ // 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 {
+ // 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 %lx %lx %lx %lx %x %lx %lx %lx %lx %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.m_str);
+ } else {
+ fprintf(output_, "%d\n", allocates_base_pointer);
+ }
+ }
+
+ last_type = type;
+ last_rva = rva;
+ last_code_size = code_size;
+ last_prolog_size = prolog_size;
+ }
+
+ frame_data.Release();
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFrameDataUsingEXE() {
+ if (code_file_.empty() && !FindPEFile()) {
+ fprintf(stderr, "Couldn't locate EXE or DLL file.\n");
+ return false;
+ }
+
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string code_file;
+ if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) {
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to load %s\n", code_file.c_str());
+ return false;
+ }
+ PIMAGE_OPTIONAL_HEADER64 optional_header =
+ &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
+ if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ fprintf(stderr, "Not a PE32+ image\n");
+ return false;
+ }
+
+ // Read Exception Directory
+ DWORD exception_rva = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
+ DWORD exception_size = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY funcs =
+ static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ exception_rva,
+ &img->LastRvaSection));
+ for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) {
+ DWORD unwind_rva = funcs[i].UnwindInfoAddress;
+ // handle chaining
+ while (unwind_rva & 0x1) {
+ unwind_rva ^= 0x1;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
+ static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ unwind_rva,
+ &img->LastRvaSection));
+ unwind_rva = chained_func->UnwindInfoAddress;
+ }
+
+ UnwindInfo *unwind_info = static_cast<UnwindInfo *>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ unwind_rva,
+ &img->LastRvaSection));
+
+ DWORD stack_size = 8; // minimal stack size is 8 for RIP
+ DWORD rip_offset = 8;
+ do {
+ for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) {
+ UnwindCode *unwind_code = &unwind_info->unwind_code[c];
+ switch (unwind_code->unwind_operation_code) {
+ case UWOP_PUSH_NONVOL: {
+ stack_size += 8;
+ break;
+ }
+ case UWOP_ALLOC_LARGE: {
+ if (unwind_code->operation_info == 0) {
+ c++;
+ if (c < unwind_info->count_of_codes)
+ stack_size += (unwind_code + 1)->frame_offset * 8;
+ } else {
+ c += 2;
+ if (c < unwind_info->count_of_codes)
+ stack_size += (unwind_code + 1)->frame_offset |
+ ((unwind_code + 2)->frame_offset << 16);
+ }
+ break;
+ }
+ case UWOP_ALLOC_SMALL: {
+ stack_size += unwind_code->operation_info * 8 + 8;
+ break;
+ }
+ case UWOP_SET_FPREG:
+ case UWOP_SAVE_XMM:
+ case UWOP_SAVE_XMM_FAR:
+ break;
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM128: {
+ c++; // skip slot with offset
+ break;
+ }
+ case UWOP_SAVE_NONVOL_FAR:
+ case UWOP_SAVE_XMM128_FAR: {
+ c += 2; // skip 2 slots with offset
+ break;
+ }
+ case UWOP_PUSH_MACHFRAME: {
+ if (unwind_code->operation_info) {
+ stack_size += 88;
+ } else {
+ stack_size += 80;
+ }
+ rip_offset += 80;
+ break;
+ }
+ }
+ }
+ if (unwind_info->flags & UNW_FLAG_CHAININFO) {
+ PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
+ reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ (unwind_info->unwind_code +
+ ((unwind_info->count_of_codes + 1) & ~1)));
+
+ unwind_info = static_cast<UnwindInfo *>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ chained_func->UnwindInfoAddress,
+ &img->LastRvaSection));
+ } else {
+ unwind_info = NULL;
+ }
+ } while (unwind_info);
+ fprintf(output_, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n",
+ funcs[i].BeginAddress,
+ funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset);
+ fprintf(output_, "STACK CFI %lx .cfa: $rsp %lu +\n",
+ funcs[i].BeginAddress, stack_size);
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFrameData() {
+ PDBModuleInfo info;
+ if (GetModuleInfo(&info) && info.cpu == L"x86_64") {
+ return PrintFrameDataUsingEXE();
+ } else {
+ return PrintFrameDataUsingPDB();
+ }
+ return false;
+}
+
+bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
+ BOOL is_code;
+ if (FAILED(symbol->get_code(&is_code))) {
+ return false;
+ }
+ if (!is_code) {
+ return true;
+ }
+
+ DWORD rva;
+ if (FAILED(symbol->get_relativeVirtualAddress(&rva))) {
+ return false;
+ }
+
+ CComBSTR name;
+ int stack_param_size;
+ if (!GetSymbolFunctionName(symbol, &name, &stack_param_size)) {
+ return false;
+ }
+
+ AddressRangeVector ranges;
+ MapAddressRange(image_map_, AddressRange(rva, 1), &ranges);
+ for (size_t i = 0; i < ranges.size(); ++i) {
+ fprintf(output_, "PUBLIC %lx %x %ws\n", ranges[i].rva,
+ stack_param_size > 0 ? stack_param_size : 0,
+ name.m_str);
+ }
+
+ // Now walk the function in the original untranslated space, asking DIA
+ // what function is at that location, stepping through OMAP blocks. If
+ // we're still in the same function, emit another entry, because the
+ // symbol could have been split into multiple pieces. If we've gotten to
+ // another symbol in the original address space, then we're done for
+ // this symbol. See https://crbug.com/678874.
+ for (;;) {
+ // This steps to the next block in the original image. Simply doing
+ // rva++ would also be correct, but would emit tons of unnecessary
+ // entries.
+ rva = image_map_.subsequent_rva_block[rva];
+ if (rva == 0)
+ break;
+
+ CComPtr<IDiaSymbol> next_sym = NULL;
+ LONG displacement;
+ if (FAILED(session_->findSymbolByRVAEx(rva, SymTagPublicSymbol, &next_sym,
+ &displacement))) {
+ break;
+ }
+
+ if (!SymbolsMatch(symbol, next_sym))
+ break;
+
+ AddressRangeVector next_ranges;
+ MapAddressRange(image_map_, AddressRange(rva, 1), &next_ranges);
+ for (size_t i = 0; i < next_ranges.size(); ++i) {
+ fprintf(output_, "PUBLIC %lx %x %ws\n", next_ranges[i].rva,
+ stack_param_size > 0 ? stack_param_size : 0, name.m_str);
+ }
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintPDBInfo() {
+ PDBModuleInfo info;
+ if (!GetModuleInfo(&info)) {
+ return false;
+ }
+
+ // Hard-code "windows" for the OS because that's the only thing that makes
+ // sense for PDB files. (This might not be strictly correct for Windows CE
+ // support, but we don't care about that at the moment.)
+ fprintf(output_, "MODULE windows %ws %ws %ws\n",
+ info.cpu.c_str(), info.debug_identifier.c_str(),
+ info.debug_file.c_str());
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintPEInfo() {
+ PEModuleInfo info;
+ if (!GetPEInfo(&info)) {
+ return false;
+ }
+
+ fprintf(output_, "INFO CODE_ID %ws %ws\n",
+ info.code_identifier.c_str(),
+ info.code_file.c_str());
+ return true;
+}
+
+// wcstol_positive_strict is sort of like wcstol, but much stricter. string
+// should be a buffer pointing to a null-terminated string containing only
+// decimal digits. If the entire string can be converted to an integer
+// without overflowing, and there are no non-digit characters before the
+// result is set to the value and this function returns true. Otherwise,
+// this function returns false. This is an alternative to the strtol, atoi,
+// and scanf families, which are not as strict about input and in some cases
+// don't provide a good way for the caller to determine if a conversion was
+// successful.
+static bool wcstol_positive_strict(wchar_t *string, int *result) {
+ int value = 0;
+ for (wchar_t *c = string; *c != '\0'; ++c) {
+ int last_value = value;
+ value *= 10;
+ // Detect overflow.
+ if (value / 10 != last_value || value < 0) {
+ return false;
+ }
+ if (*c < '0' || *c > '9') {
+ return false;
+ }
+ unsigned int c_value = *c - '0';
+ last_value = value;
+ value += c_value;
+ // Detect overflow.
+ if (value < last_value) {
+ return false;
+ }
+ // Forbid leading zeroes unless the string is just "0".
+ if (value == 0 && *(c+1) != '\0') {
+ return false;
+ }
+ }
+ *result = value;
+ return true;
+}
+
+bool PDBSourceLineWriter::FindPEFile() {
+ CComPtr<IDiaSymbol> global;
+ if (FAILED(session_->get_globalScope(&global))) {
+ fprintf(stderr, "get_globalScope failed\n");
+ return false;
+ }
+
+ CComBSTR symbols_file;
+ if (SUCCEEDED(global->get_symbolsFileName(&symbols_file))) {
+ wstring file(symbols_file);
+
+ // Look for an EXE or DLL file.
+ const wchar_t *extensions[] = { L"exe", L"dll" };
+ for (size_t 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;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+// static
+bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function,
+ BSTR *name,
+ int *stack_param_size) {
+ *stack_param_size = -1;
+ const DWORD undecorate_options = UNDNAME_NO_MS_KEYWORDS |
+ UNDNAME_NO_FUNCTION_RETURNS |
+ UNDNAME_NO_ALLOCATION_MODEL |
+ UNDNAME_NO_ALLOCATION_LANGUAGE |
+ UNDNAME_NO_THISTYPE |
+ UNDNAME_NO_ACCESS_SPECIFIERS |
+ UNDNAME_NO_THROW_SIGNATURES |
+ UNDNAME_NO_MEMBER_TYPE |
+ UNDNAME_NO_RETURN_UDT_MODEL |
+ UNDNAME_NO_ECSU;
+
+ // Use get_undecoratedNameEx to get readable C++ names with arguments.
+ if (function->get_undecoratedNameEx(undecorate_options, name) != S_OK) {
+ if (function->get_name(name) != S_OK) {
+ fprintf(stderr, "failed to get function name\n");
+ return false;
+ }
+
+ // It's possible for get_name to return an empty string, so
+ // special-case that.
+ if (wcscmp(*name, L"") == 0) {
+ SysFreeString(*name);
+ // dwarf_cu_to_module.cc uses "<name omitted>", so match that.
+ *name = SysAllocString(L"<name omitted>");
+ return true;
+ }
+
+ // If a name comes from get_name because no undecorated form existed,
+ // it's already formatted properly to be used as output. Don't do any
+ // additional processing.
+ //
+ // MSVC7's DIA seems to not undecorate names in as many cases as MSVC8's.
+ // This will result in calling get_name for some C++ symbols, so
+ // all of the parameter and return type information may not be included in
+ // the name string.
+ } else {
+ // C++ uses a bogus "void" argument for functions and methods that don't
+ // take any parameters. Take it out of the undecorated name because it's
+ // ugly and unnecessary.
+ const wchar_t *replace_string = L"(void)";
+ const size_t replace_length = wcslen(replace_string);
+ const wchar_t *replacement_string = L"()";
+ size_t length = wcslen(*name);
+ if (length >= replace_length) {
+ wchar_t *name_end = *name + length - replace_length;
+ if (wcscmp(name_end, replace_string) == 0) {
+ WindowsStringUtils::safe_wcscpy(name_end, replace_length,
+ replacement_string);
+ length = wcslen(*name);
+ }
+ }
+
+ // Undecorate names used for stdcall and fastcall. These names prefix
+ // the identifier with '_' (stdcall) or '@' (fastcall) and suffix it
+ // with '@' followed by the number of bytes of parameters, in decimal.
+ // If such a name is found, take note of the size and undecorate it.
+ // Only do this for names that aren't C++, which is determined based on
+ // whether the undecorated name contains any ':' or '(' characters.
+ if (!wcschr(*name, ':') && !wcschr(*name, '(') &&
+ (*name[0] == '_' || *name[0] == '@')) {
+ wchar_t *last_at = wcsrchr(*name + 1, '@');
+ if (last_at && wcstol_positive_strict(last_at + 1, stack_param_size)) {
+ // If this function adheres to the fastcall convention, it accepts up
+ // to the first 8 bytes of parameters in registers (%ecx and %edx).
+ // We're only interested in the stack space used for parameters, so
+ // so subtract 8 and don't let the size go below 0.
+ if (*name[0] == '@') {
+ if (*stack_param_size > 8) {
+ *stack_param_size -= 8;
+ } else {
+ *stack_param_size = 0;
+ }
+ }
+
+ // Undecorate the name by moving it one character to the left in its
+ // buffer, and terminating it where the last '@' had been.
+ WindowsStringUtils::safe_wcsncpy(*name, length,
+ *name + 1, last_at - *name - 1);
+ } else if (*name[0] == '_') {
+ // This symbol's name is encoded according to the cdecl rules. The
+ // name doesn't end in a '@' character followed by a decimal positive
+ // integer, so it's not a stdcall name. Strip off the leading
+ // underscore.
+ WindowsStringUtils::safe_wcsncpy(*name, length, *name + 1, length);
+ }
+ }
+ }
+
+ return true;
+}
+
+// static
+int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) {
+ // This implementation is highly x86-specific.
+
+ // Gather the symbols corresponding to data.
+ CComPtr<IDiaEnumSymbols> data_children;
+ if (FAILED(function->findChildren(SymTagData, NULL, nsNone,
+ &data_children))) {
+ return 0;
+ }
+
+ // lowest_base is the lowest %ebp-relative byte offset used for a parameter.
+ // highest_end is one greater than the highest offset (i.e. base + length).
+ // Stack parameters are assumed to be contiguous, because in reality, they
+ // are.
+ int lowest_base = INT_MAX;
+ int highest_end = INT_MIN;
+
+ CComPtr<IDiaSymbol> child;
+ DWORD count;
+ while (SUCCEEDED(data_children->Next(1, &child, &count)) && count == 1) {
+ // If any operation fails at this point, just proceed to the next child.
+ // Use the next_child label instead of continue because child needs to
+ // be released before it's reused. Declare constructable/destructable
+ // types early to avoid gotos that cross initializations.
+ CComPtr<IDiaSymbol> child_type;
+
+ // DataIsObjectPtr is only used for |this|. Because |this| can be passed
+ // as a stack parameter, look for it in addition to traditional
+ // parameters.
+ DWORD child_kind;
+ if (FAILED(child->get_dataKind(&child_kind)) ||
+ (child_kind != DataIsParam && child_kind != DataIsObjectPtr)) {
+ goto next_child;
+ }
+
+ // Only concentrate on register-relative parameters. Parameters may also
+ // be enregistered (passed directly in a register), but those don't
+ // consume any stack space, so they're not of interest.
+ DWORD child_location_type;
+ if (FAILED(child->get_locationType(&child_location_type)) ||
+ child_location_type != LocIsRegRel) {
+ goto next_child;
+ }
+
+ // Of register-relative parameters, the only ones that make any sense are
+ // %ebp- or %esp-relative. Note that MSVC's debugging information always
+ // gives parameters as %ebp-relative even when a function doesn't use a
+ // traditional frame pointer and stack parameters are accessed relative to
+ // %esp, so just look for %ebp-relative parameters. If you wanted to
+ // access parameters, you'd probably want to treat these %ebp-relative
+ // offsets as if they were relative to %esp before a function's prolog
+ // executed.
+ DWORD child_register;
+ if (FAILED(child->get_registerId(&child_register)) ||
+ child_register != CV_REG_EBP) {
+ goto next_child;
+ }
+
+ LONG child_register_offset;
+ if (FAILED(child->get_offset(&child_register_offset))) {
+ goto next_child;
+ }
+
+ // IDiaSymbol::get_type can succeed but still pass back a NULL value.
+ if (FAILED(child->get_type(&child_type)) || !child_type) {
+ goto next_child;
+ }
+
+ ULONGLONG child_length;
+ if (FAILED(child_type->get_length(&child_length))) {
+ goto next_child;
+ }
+
+ int child_end = child_register_offset + static_cast<ULONG>(child_length);
+ if (child_register_offset < lowest_base) {
+ lowest_base = child_register_offset;
+ }
+ if (child_end > highest_end) {
+ highest_end = child_end;
+ }
+
+next_child:
+ child.Release();
+ }
+
+ int param_size = 0;
+ // Make sure lowest_base isn't less than 4, because [%esp+4] is the lowest
+ // possible address to find a stack parameter before executing a function's
+ // prolog (see above). Some optimizations cause parameter offsets to be
+ // lower than 4, but we're not concerned with those because we're only
+ // looking for parameters contained in addresses higher than where the
+ // return address is stored.
+ if (lowest_base < 4) {
+ lowest_base = 4;
+ }
+ if (highest_end > lowest_base) {
+ // All stack parameters are pushed as at least 4-byte quantities. If the
+ // last type was narrower than 4 bytes, promote it. This assumes that all
+ // parameters' offsets are 4-byte-aligned, which is always the case. Only
+ // worry about the last type, because we're not summing the type sizes,
+ // just looking at the lowest and highest offsets.
+ int remainder = highest_end % 4;
+ if (remainder) {
+ highest_end += 4 - remainder;
+ }
+
+ param_size = highest_end - lowest_base;
+ }
+
+ return param_size;
+}
+
+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();
+
+ output_ = NULL;
+ return ret;
+}
+
+void PDBSourceLineWriter::Close() {
+ session_.Release();
+}
+
+bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) {
+ if (!info) {
+ return false;
+ }
+
+ info->debug_file.clear();
+ info->debug_identifier.clear();
+ info->cpu.clear();
+
+ CComPtr<IDiaSymbol> global;
+ if (FAILED(session_->get_globalScope(&global))) {
+ return false;
+ }
+
+ DWORD machine_type;
+ // get_machineType can return S_FALSE.
+ if (global->get_machineType(&machine_type) == S_OK) {
+ // The documentation claims that get_machineType returns a value from
+ // the CV_CPU_TYPE_e enumeration, but that's not the case.
+ // Instead, it returns one of the IMAGE_FILE_MACHINE values as
+ // defined here:
+ // http://msdn.microsoft.com/en-us/library/ms680313%28VS.85%29.aspx
+ switch (machine_type) {
+ case IMAGE_FILE_MACHINE_I386:
+ info->cpu = L"x86";
+ break;
+ case IMAGE_FILE_MACHINE_AMD64:
+ info->cpu = L"x86_64";
+ break;
+ default:
+ info->cpu = L"unknown";
+ break;
+ }
+ } else {
+ // Unexpected, but handle gracefully.
+ info->cpu = L"unknown";
+ }
+
+ // DWORD* and int* are not compatible. This is clean and avoids a cast.
+ DWORD age;
+ if (FAILED(global->get_age(&age))) {
+ return false;
+ }
+
+ bool uses_guid;
+ if (!UsesGUID(&uses_guid)) {
+ return false;
+ }
+
+ if (uses_guid) {
+ GUID guid;
+ if (FAILED(global->get_guid(&guid))) {
+ return false;
+ }
+
+ // 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';
+
+ info->debug_identifier = GUIDString::GUIDToSymbolServerWString(&guid);
+ info->debug_identifier.append(age_string);
+ } else {
+ DWORD signature;
+ if (FAILED(global->get_signature(&signature))) {
+ return false;
+ }
+
+ // 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';
+
+ info->debug_identifier = identifier_string;
+ }
+
+ CComBSTR debug_file_string;
+ if (FAILED(global->get_symbolsFileName(&debug_file_string))) {
+ return false;
+ }
+ info->debug_file =
+ WindowsStringUtils::GetBaseName(wstring(debug_file_string));
+
+ return true;
+}
+
+bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) {
+ if (!info) {
+ return false;
+ }
+
+ if (code_file_.empty() && !FindPEFile()) {
+ fprintf(stderr, "Couldn't locate EXE or DLL file.\n");
+ return false;
+ }
+
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string code_file;
+ if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) {
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to open PE file: %s\n", code_file.c_str());
+ return false;
+ }
+
+ info->code_file = WindowsStringUtils::GetBaseName(code_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 PDBSourceLineWriter::UsesGUID(bool *uses_guid) {
+ if (!uses_guid)
+ return false;
+
+ CComPtr<IDiaSymbol> global;
+ if (FAILED(session_->get_globalScope(&global)))
+ return false;
+
+ GUID guid;
+ if (FAILED(global->get_guid(&guid)))
+ return false;
+
+ DWORD signature;
+ if (FAILED(global->get_signature(&signature)))
+ return false;
+
+ // There are two possibilities for guid: either it's a real 128-bit GUID
+ // as identified in a code module by a new-style CodeView record, or it's
+ // a 32-bit signature (timestamp) as identified by an old-style record.
+ // See MDCVInfoPDB70 and MDCVInfoPDB20 in minidump_format.h.
+ //
+ // Because DIA doesn't provide a way to directly determine whether a module
+ // uses a GUID or a 32-bit signature, this code checks whether the first 32
+ // bits of guid are the same as the signature, and if the rest of guid is
+ // zero. If so, then with a pretty high degree of certainty, there's an
+ // old-style CodeView record in use. This method will only falsely find an
+ // an old-style CodeView record if a real 128-bit GUID has its first 32
+ // bits set the same as the module's signature (timestamp) and the rest of
+ // the GUID is set to 0. This is highly unlikely.
+
+ GUID signature_guid = {signature}; // 0-initializes other members
+ *uses_guid = !IsEqualGUID(guid, signature_guid);
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.h b/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.h
new file mode 100644
index 0000000..e9e89bb
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.h
@@ -0,0 +1,257 @@
+// Copyright (c) 2006, 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.
+
+// PDBSourceLineWriter uses a pdb file produced by Visual C++ to output
+// a line/address map for use with BasicSourceLineResolver.
+
+#ifndef COMMON_WINDOWS_PDB_SOURCE_LINE_WRITER_H_
+#define COMMON_WINDOWS_PDB_SOURCE_LINE_WRITER_H_
+
+#include <atlcomcli.h>
+
+#include <unordered_map>
+#include <string>
+
+#include "common/windows/omap.h"
+
+struct IDiaEnumLineNumbers;
+struct IDiaSession;
+struct IDiaSymbol;
+
+namespace google_breakpad {
+
+using std::wstring;
+using std::unordered_map;
+
+// A structure that carries information that identifies a pdb file.
+struct PDBModuleInfo {
+ public:
+ // The basename of the pdb file from which information was loaded.
+ wstring debug_file;
+
+ // The pdb's identifier. For recent pdb files, the identifier consists
+ // of the pdb's guid, in uppercase hexadecimal form without any dashes
+ // or separators, followed immediately by the pdb's age, also in
+ // uppercase hexadecimal form. For older pdb files which have no guid,
+ // the identifier is the pdb's 32-bit signature value, in zero-padded
+ // hexadecimal form, followed immediately by the pdb's age, in lowercase
+ // hexadecimal form.
+ wstring debug_identifier;
+
+ // A string identifying the cpu that the pdb is associated with.
+ // Currently, this may be "x86" or "unknown".
+ wstring cpu;
+};
+
+// A structure that carries information that identifies a PE file,
+// either an EXE or a DLL.
+struct PEModuleInfo {
+ // The basename of the PE file.
+ wstring code_file;
+
+ // The PE file's code identifier, which consists of its timestamp
+ // and file size concatenated together into a single hex string.
+ // (The fields IMAGE_OPTIONAL_HEADER::SizeOfImage and
+ // IMAGE_FILE_HEADER::TimeDateStamp, as defined in the ImageHlp
+ // documentation.) This is not well documented, if it's documented
+ // at all, but it's what symstore does and what DbgHelp supports.
+ wstring code_identifier;
+};
+
+class PDBSourceLineWriter {
+ public:
+ enum FileFormat {
+ PDB_FILE, // a .pdb file containing debug symbols
+ EXE_FILE, // a .exe or .dll file
+ ANY_FILE // try PDB_FILE and then EXE_FILE
+ };
+
+ explicit PDBSourceLineWriter();
+ ~PDBSourceLineWriter();
+
+ // Opens the given file. For executable files, the corresponding pdb
+ // file must be available; Open will be if it is not.
+ // If there is already a pdb file open, it is automatically closed.
+ // Returns true on success.
+ bool Open(const wstring &file, FileFormat format);
+
+ // Sets the code file full path. This is optional for 32-bit modules. It is
+ // also optional for 64-bit modules when there is an executable file stored
+ // in the same directory as the PDB file. It is only required for 64-bit
+ // modules when the executable file is not in the same location as the PDB
+ // file and it must be called after Open() and before WriteMap().
+ // If Open() was called for an executable file, then it is an error to call
+ // SetCodeFile() with a different file path and it will return false.
+ bool SetCodeFile(const wstring &exe_file);
+
+ // Writes a map file from the current pdb file to the given file stream.
+ // Returns true on success.
+ bool WriteMap(FILE *map_file);
+
+ // Closes the current pdb file and its associated resources.
+ void Close();
+
+ // Retrieves information about the module's debugging file. Returns
+ // true on success and false on failure.
+ bool GetModuleInfo(PDBModuleInfo *info);
+
+ // Retrieves information about the module's PE file. Returns
+ // true on success and false on failure.
+ bool GetPEInfo(PEModuleInfo *info);
+
+ // Sets uses_guid to true if the opened file uses a new-style CodeView
+ // record with a 128-bit GUID, or false if the opened file uses an old-style
+ // CodeView record. When no GUID is available, a 32-bit signature should be
+ // used to identify the module instead. If the information cannot be
+ // determined, this method returns false.
+ bool UsesGUID(bool *uses_guid);
+
+ private:
+ // Outputs the line/address pairs for each line in the enumerator.
+ // Returns true on success.
+ bool PrintLines(IDiaEnumLineNumbers *lines);
+
+ // Outputs a function address and name, followed by its source line list.
+ // block can be the same object as function, or it can be a reference
+ // to a code block that is lexically part of this function, but
+ // resides at a separate address.
+ // Returns true on success.
+ bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block);
+
+ // Outputs all functions as described above. Returns true on success.
+ bool PrintFunctions();
+
+ // Outputs all of the source files in the session's pdb file.
+ // Returns true on success.
+ bool PrintSourceFiles();
+
+ // Outputs all of the frame information necessary to construct stack
+ // backtraces in the absence of frame pointers. For x86 data stored in
+ // .pdb files. Returns true on success.
+ bool PrintFrameDataUsingPDB();
+
+ // Outputs all of the frame information necessary to construct stack
+ // backtraces in the absence of frame pointers. For x64 data stored in
+ // .exe, .dll files. Returns true on success.
+ bool PrintFrameDataUsingEXE();
+
+ // Outputs all of the frame information necessary to construct stack
+ // backtraces in the absence of frame pointers. Returns true on success.
+ bool PrintFrameData();
+
+ // Outputs a single public symbol address and name, if the symbol corresponds
+ // to a code address. Returns true on success. If symbol is does not
+ // correspond to code, returns true without outputting anything.
+ bool PrintCodePublicSymbol(IDiaSymbol *symbol);
+
+ // Outputs a line identifying the PDB file that is being dumped, along with
+ // its uuid and age.
+ bool PrintPDBInfo();
+
+ // Outputs a line identifying the PE file corresponding to the PDB
+ // file that is being dumped, along with its code identifier,
+ // which consists of its timestamp and file size.
+ bool PrintPEInfo();
+
+ // Returns true if this filename has already been seen,
+ // and an ID is stored for it, or false if it has not.
+ bool FileIDIsCached(const wstring &file) {
+ return unique_files_.find(file) != unique_files_.end();
+ }
+
+ // Cache this filename and ID for later reuse.
+ void CacheFileID(const wstring &file, DWORD id) {
+ unique_files_[file] = id;
+ }
+
+ // Store this ID in the cache as a duplicate for this filename.
+ void StoreDuplicateFileID(const wstring &file, DWORD id) {
+ unordered_map<wstring, DWORD>::iterator iter = unique_files_.find(file);
+ if (iter != unique_files_.end()) {
+ // map this id to the previously seen one
+ file_ids_[id] = iter->second;
+ }
+ }
+
+ // Given a file's unique ID, return the ID that should be used to
+ // reference it. There may be multiple files with identical filenames
+ // but different unique IDs. The cache attempts to coalesce these into
+ // one ID per unique filename.
+ DWORD GetRealFileID(DWORD id) {
+ unordered_map<DWORD, DWORD>::iterator iter = file_ids_.find(id);
+ if (iter == file_ids_.end())
+ return id;
+ return iter->second;
+ }
+
+ // Find the PE file corresponding to the loaded PDB file, and
+ // set the code_file_ member. Returns false on failure.
+ bool FindPEFile();
+
+ // Returns the function name for a symbol. If possible, the name is
+ // undecorated. If the symbol's decorated form indicates the size of
+ // parameters on the stack, this information is returned in stack_param_size.
+ // Returns true on success. If the symbol doesn't encode parameter size
+ // information, stack_param_size is set to -1.
+ static bool GetSymbolFunctionName(IDiaSymbol *function, BSTR *name,
+ int *stack_param_size);
+
+ // Returns the number of bytes of stack space used for a function's
+ // parameters. function must have the tag SymTagFunction. In the event of
+ // a failure, returns 0, which is also a valid number of bytes.
+ static int GetFunctionStackParamSize(IDiaSymbol *function);
+
+ // The filename of the PE file corresponding to the currently-open
+ // pdb file.
+ wstring code_file_;
+
+ // The session for the currently-open pdb file.
+ CComPtr<IDiaSession> session_;
+
+ // The current output file for this WriteMap invocation.
+ FILE *output_;
+
+ // There may be many duplicate filenames with different IDs.
+ // This maps from the DIA "unique ID" to a single ID per unique
+ // filename.
+ unordered_map<DWORD, DWORD> file_ids_;
+ // This maps unique filenames to file IDs.
+ unordered_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&);
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_PDB_SOURCE_LINE_WRITER_H_
diff --git a/3rdParty/Breakpad/src/common/windows/string_utils-inl.h b/3rdParty/Breakpad/src/common/windows/string_utils-inl.h
index d281aaa..9b63607 100644
--- a/3rdParty/Breakpad/src/common/windows/string_utils-inl.h
+++ b/3rdParty/Breakpad/src/common/windows/string_utils-inl.h
@@ -30,8 +30,8 @@
// string_utils-inl.h: Safer string manipulation on Windows, supporting
// pre-MSVC8 environments.
-#ifndef COMMON_WINDOWS_STRING_UTILS_INL_H__
-#define COMMON_WINDOWS_STRING_UTILS_INL_H__
+#ifndef COMMON_WINDOWS_STRING_UTILS_INL_H_
+#define COMMON_WINDOWS_STRING_UTILS_INL_H_
#include <stdarg.h>
#include <wchar.h>
@@ -139,4 +139,4 @@ inline void WindowsStringUtils::safe_wcsncpy(wchar_t *destination,
} // namespace google_breakpad
-#endif // COMMON_WINDOWS_STRING_UTILS_INL_H__
+#endif // COMMON_WINDOWS_STRING_UTILS_INL_H_
diff --git a/3rdParty/Breakpad/src/common/windows/string_utils.cc b/3rdParty/Breakpad/src/common/windows/string_utils.cc
new file mode 100644
index 0000000..2728000
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/string_utils.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2006, 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 <cassert>
+#include <vector>
+
+#include "common/windows/string_utils-inl.h"
+
+namespace google_breakpad {
+
+// static
+wstring WindowsStringUtils::GetBaseName(const wstring &filename) {
+ wstring base_name(filename);
+ size_t slash_pos = base_name.find_last_of(L"/\\");
+ if (slash_pos != wstring::npos) {
+ base_name.erase(0, slash_pos + 1);
+ }
+ return base_name;
+}
+
+// static
+bool WindowsStringUtils::safe_mbstowcs(const string &mbs, wstring *wcs) {
+ assert(wcs);
+
+ // First, determine the length of the destination buffer.
+ size_t wcs_length;
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ errno_t err;
+ if ((err = mbstowcs_s(&wcs_length, NULL, 0, mbs.c_str(), _TRUNCATE)) != 0) {
+ return false;
+ }
+ assert(wcs_length > 0);
+#else // _MSC_VER >= 1400
+ if ((wcs_length = mbstowcs(NULL, mbs.c_str(), mbs.length())) == (size_t)-1) {
+ return false;
+ }
+
+ // Leave space for the 0-terminator.
+ ++wcs_length;
+#endif // _MSC_VER >= 1400
+
+ std::vector<wchar_t> wcs_v(wcs_length);
+
+ // Now, convert.
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ if ((err = mbstowcs_s(NULL, &wcs_v[0], wcs_length, mbs.c_str(),
+ _TRUNCATE)) != 0) {
+ return false;
+ }
+#else // _MSC_VER >= 1400
+ if (mbstowcs(&wcs_v[0], mbs.c_str(), mbs.length()) == (size_t)-1) {
+ return false;
+ }
+
+ // Ensure presence of 0-terminator.
+ wcs_v[wcs_length - 1] = '\0';
+#endif // _MSC_VER >= 1400
+
+ *wcs = &wcs_v[0];
+ return true;
+}
+
+// static
+bool WindowsStringUtils::safe_wcstombs(const wstring &wcs, string *mbs) {
+ assert(mbs);
+
+ // First, determine the length of the destination buffer.
+ size_t mbs_length;
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ errno_t err;
+ if ((err = wcstombs_s(&mbs_length, NULL, 0, wcs.c_str(), _TRUNCATE)) != 0) {
+ return false;
+ }
+ assert(mbs_length > 0);
+#else // _MSC_VER >= 1400
+ if ((mbs_length = wcstombs(NULL, wcs.c_str(), wcs.length())) == (size_t)-1) {
+ return false;
+ }
+
+ // Leave space for the 0-terminator.
+ ++mbs_length;
+#endif // _MSC_VER >= 1400
+
+ std::vector<char> mbs_v(mbs_length);
+
+ // Now, convert.
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ if ((err = wcstombs_s(NULL, &mbs_v[0], mbs_length, wcs.c_str(),
+ _TRUNCATE)) != 0) {
+ return false;
+ }
+#else // _MSC_VER >= 1400
+ if (wcstombs(&mbs_v[0], wcs.c_str(), wcs.length()) == (size_t)-1) {
+ return false;
+ }
+
+ // Ensure presence of 0-terminator.
+ mbs_v[mbs_length - 1] = '\0';
+#endif // _MSC_VER >= 1400
+
+ *mbs = &mbs_v[0];
+ return true;
+}
+
+} // namespace google_breakpad