diff options
Diffstat (limited to 'src/common/windows')
24 files changed, 2005 insertions, 1423 deletions
diff --git a/src/common/windows/common_windows.gyp b/src/common/windows/common_windows.gyp deleted file mode 100644 index 5f7594b1..00000000 --- a/src/common/windows/common_windows.gyp +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2013 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -{ - 'includes': [ - '../../build/common.gypi', - ], - 'targets': [ - { - 'target_name': 'dia_sdk', - 'type': 'none', - 'all_dependent_settings': { - 'include_dirs': [ - '<(DEPTH)', - '$(VSInstallDir)/DIA SDK/include', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'AdditionalDependencies': [ - 'diaguids.lib', - 'imagehlp.lib', - ], - }, - }, - 'configurations': { - 'x86_Base': { - 'msvs_settings': { - 'VCLinkerTool': { - 'AdditionalLibraryDirectories': - ['$(VSInstallDir)/DIA SDK/lib'], - }, - }, - }, - 'x64_Base': { - 'msvs_settings': { - 'VCLinkerTool': { - 'AdditionalLibraryDirectories': - ['$(VSInstallDir)/DIA SDK/lib/amd64'], - }, - }, - }, - }, - }, - }, - { - 'target_name': 'common_windows_lib', - 'type': 'static_library', - 'sources': [ - 'dia_util.cc', - 'dia_util.h', - 'guid_string.cc', - 'guid_string.h', - 'http_upload.cc', - 'http_upload.h', - 'module_info.h', - 'omap.cc', - 'omap.h', - 'omap_internal.h', - 'pdb_source_line_writer.cc', - 'pdb_source_line_writer.h', - 'pe_source_line_writer.cc', - 'pe_source_line_writer.h', - 'pe_util.h', - 'pe_util.cc', - 'string_utils.cc', - 'string_utils-inl.h', - 'symbol_collector_client.cc', - 'symbol_collector_client.h', - ], - 'dependencies': [ - 'dia_sdk', - ], - }, - { - 'target_name': 'common_windows_unittests', - 'type': 'executable', - 'sources': [ - 'omap_unittest.cc', - ], - 'dependencies': [ - '<(DEPTH)/client/windows/unittests/testing.gyp:gmock', - '<(DEPTH)/client/windows/unittests/testing.gyp:gtest', - 'common_windows_lib', - ], - }, - ], -} diff --git a/src/common/windows/dia_util.cc b/src/common/windows/dia_util.cc index ed8cb5b6..dcfe0ef9 100644 --- a/src/common/windows/dia_util.cc +++ b/src/common/windows/dia_util.cc @@ -1,92 +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;
-}
-
+// Copyright 2013 Google LLC +// +// 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 LLC 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/src/common/windows/dia_util.h b/src/common/windows/dia_util.h index b9e0df2d..16ed8380 100644 --- a/src/common/windows/dia_util.h +++ b/src/common/windows/dia_util.h @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2013 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // diff --git a/src/common/windows/guid_string.cc b/src/common/windows/guid_string.cc index b7f877e6..be9eb8a3 100644 --- a/src/common/windows/guid_string.cc +++ b/src/common/windows/guid_string.cc @@ -1,5 +1,4 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. +// Copyright 2006 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -11,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // diff --git a/src/common/windows/guid_string.h b/src/common/windows/guid_string.h index 48a5c1d3..ee3d1006 100644 --- a/src/common/windows/guid_string.h +++ b/src/common/windows/guid_string.h @@ -1,5 +1,4 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. +// Copyright 2006 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -11,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // diff --git a/src/common/windows/http_upload.cc b/src/common/windows/http_upload.cc index b0cc9078..088a5e54 100644 --- a/src/common/windows/http_upload.cc +++ b/src/common/windows/http_upload.cc @@ -1,5 +1,4 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. +// Copyright 2006 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -11,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // @@ -65,7 +64,7 @@ namespace { HINTERNET handle_; }; - wstring UTF8ToWide(const string &utf8) { + wstring UTF8ToWide(const string& utf8) { if (utf8.length() == 0) { return wstring(); } @@ -85,7 +84,7 @@ namespace { return result; } - string WideToMBCP(const wstring &wide, unsigned int cp) { + string WideToMBCP(const wstring& wide, unsigned int cp) { if (wide.length() == 0) { return string(); } @@ -107,7 +106,7 @@ namespace { return result; } - bool GetFileContents(const wstring &filename, vector<char> *contents) { + bool 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 @@ -141,10 +140,10 @@ namespace { return rv; } - bool CheckParameters(const map<wstring, wstring> ¶meters) { + bool CheckParameters(const map<wstring, wstring>& parameters) { for (map<wstring, wstring>::const_iterator pos = parameters.begin(); pos != parameters.end(); ++pos) { - const wstring &str = pos->first; + const wstring& str = pos->first; if (str.size() == 0) { return false; // disallow empty parameter names } @@ -159,7 +158,7 @@ namespace { } // Converts a UTF16 string to UTF8. - string WideToUTF8(const wstring &wide) { + string WideToUTF8(const wstring& wide) { return WideToMBCP(wide, CP_UTF8); } @@ -262,7 +261,7 @@ namespace { NULL, // password INTERNET_SERVICE_HTTP, 0, // flags - NULL)); // context + 0)); // context if (!connection.get()) { return false; } @@ -276,7 +275,7 @@ namespace { NULL, // referer NULL, // agent type http_open_flags, - NULL)); // context + 0)); // context if (!request.get()) { return false; } @@ -305,7 +304,7 @@ namespace { } if (!HttpSendRequest(request.get(), NULL, 0, - const_cast<char *>(request_body.data()), + const_cast<char*>(request_body.data()), static_cast<DWORD>(request_body.size()))) { return false; } @@ -351,16 +350,16 @@ namespace { return wstring(temp); } - wstring GenerateMultipartPostRequestHeader(const wstring &boundary) { + wstring GenerateMultipartPostRequestHeader(const wstring& boundary) { wstring header = L"Content-Type: multipart/form-data; boundary="; header += boundary; return header; } - bool AppendFileToRequestBody( - const wstring& file_part_name, - const wstring& filename, - string* request_body) { + bool AppendFileToRequestBody(const wstring& file_part_name, + const wstring& filename, + string* request_body, + bool set_content_type = true) { string file_part_name_utf8 = WideToUTF8(file_part_name); if (file_part_name_utf8.empty()) { return false; @@ -371,11 +370,17 @@ namespace { return false; } - 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 (set_content_type) { + 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"); + } vector<char> contents; if (!GetFileContents(filename, &contents)) { @@ -385,14 +390,13 @@ namespace { if (!contents.empty()) { request_body->append(&(contents[0]), contents.size()); } - request_body->append("\r\n"); return true; } - bool GenerateRequestBody(const map<wstring, wstring> ¶meters, - const map<wstring, wstring> &files, - const wstring &boundary, + bool 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()) { @@ -432,7 +436,11 @@ namespace google_breakpad { wstring* response_body, int* response_code) { string request_body; - if (!AppendFileToRequestBody(L"symbol_file", path, &request_body)) { + // Turn off content-type in the body. If content-type is set then binary + // files uploaded to GCS end up with the it prepended to the file + // contents. + if (!AppendFileToRequestBody(L"symbol_file", path, &request_body, + /*set_content_type=*/false)) { return false; } diff --git a/src/common/windows/http_upload.h b/src/common/windows/http_upload.h index 57e526e3..e117840e 100644 --- a/src/common/windows/http_upload.h +++ b/src/common/windows/http_upload.h @@ -1,5 +1,4 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. +// Copyright 2006 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -11,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // @@ -114,8 +113,8 @@ class HTTPUpload { // No instances of this class should be created. // Disallow all constructors, destructors, and operator=. HTTPUpload(); - explicit HTTPUpload(const HTTPUpload &); - void operator=(const HTTPUpload &); + explicit HTTPUpload(const HTTPUpload&); + void operator=(const HTTPUpload&); ~HTTPUpload(); }; diff --git a/src/common/windows/module_info.h b/src/common/windows/module_info.h index 3dccc808..ade32c11 100644 --- a/src/common/windows/module_info.h +++ b/src/common/windows/module_info.h @@ -1,75 +1,74 @@ -// Copyright (c) 2019, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef COMMON_WINDOWS_MODULE_INFO_H_
-#define COMMON_WINDOWS_MODULE_INFO_H_
-
-#include <string>
-
-namespace google_breakpad {
-
-using std::wstring;
-// A structure that carries information that identifies a module.
-struct PDBModuleInfo {
-public:
- // The basename of the pe/pdb file from which information was loaded.
- wstring debug_file;
-
- // The module's identifier. For recent pe/pdb files, the identifier consists
- // of the pe/pdb's guid, in uppercase hexadecimal form without any dashes
- // or separators, followed immediately by the pe/pdb's age, also in
- // uppercase hexadecimal form. For older pe/pdb files which have no guid,
- // the identifier is the pe/pdb's 32-bit signature value, in zero-padded
- // hexadecimal form, followed immediately by the pe/pdb's age, in lowercase
- // hexadecimal form.
- wstring debug_identifier;
-
- // A string identifying the cpu that the pe/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;
-};
-
-} // namespace google_breakpad
-
-#endif // COMMON_WINDOWS_MODULE_INFO_H_
+// Copyright 2019 Google LLC +// +// 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 LLC 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. + +#ifndef COMMON_WINDOWS_MODULE_INFO_H_ +#define COMMON_WINDOWS_MODULE_INFO_H_ + +#include <string> + +namespace google_breakpad { + +using std::wstring; +// A structure that carries information that identifies a module. +struct PDBModuleInfo { +public: + // The basename of the pe/pdb file from which information was loaded. + wstring debug_file; + + // The module's identifier. For recent pe/pdb files, the identifier consists + // of the pe/pdb's guid, in uppercase hexadecimal form without any dashes + // or separators, followed immediately by the pe/pdb's age, also in + // uppercase hexadecimal form. For older pe/pdb files which have no guid, + // the identifier is the pe/pdb's 32-bit signature value, in zero-padded + // hexadecimal form, followed immediately by the pe/pdb's age, in lowercase + // hexadecimal form. + wstring debug_identifier; + + // A string identifying the cpu that the pe/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; +}; + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_MODULE_INFO_H_ diff --git a/src/common/windows/omap.cc b/src/common/windows/omap.cc index ba3ce86b..ad916997 100644 --- a/src/common/windows/omap.cc +++ b/src/common/windows/omap.cc @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2013 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // @@ -449,11 +449,11 @@ void BuildEndpointIndexMap(ImageMap* image_map) { } } -void BuildSubsequentRVAMap(const OmapData &omap_data, - std::map<DWORD, DWORD> *subsequent) { +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); + const OmapFromTable& orig2tran = + reinterpret_cast<const OmapFromTable&>(omap_data.omap_from); if (orig2tran.empty()) return; diff --git a/src/common/windows/omap.h b/src/common/windows/omap.h index bc293afb..51601fa9 100644 --- a/src/common/windows/omap.h +++ b/src/common/windows/omap.h @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2013 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // diff --git a/src/common/windows/omap_internal.h b/src/common/windows/omap_internal.h index 2a4713d9..cd20d9fb 100644 --- a/src/common/windows/omap_internal.h +++ b/src/common/windows/omap_internal.h @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2013 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // diff --git a/src/common/windows/omap_unittest.cc b/src/common/windows/omap_unittest.cc index 7fe66bd4..841e5391 100644 --- a/src/common/windows/omap_unittest.cc +++ b/src/common/windows/omap_unittest.cc @@ -1,329 +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
+// Copyright 2013 Google LLC +// +// 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 LLC 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/src/common/windows/pdb_source_line_writer.cc b/src/common/windows/pdb_source_line_writer.cc index 4030a2e9..800c316f 100644 --- a/src/common/windows/pdb_source_line_writer.cc +++ b/src/common/windows/pdb_source_line_writer.cc @@ -1,5 +1,4 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. +// Copyright 2006 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -11,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // @@ -40,6 +39,7 @@ #include <algorithm> #include <limits> #include <map> +#include <memory> #include <set> #include <utility> @@ -58,6 +58,8 @@ namespace google_breakpad { namespace { +using std::set; +using std::unique_ptr; using std::vector; // The symbol (among possibly many) selected to represent an rva. @@ -120,7 +122,7 @@ bool SymbolsMatch(IDiaSymbol* a, IDiaSymbol* b) { return a_section == b_section && a_offset == b_offset; } -bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) { +bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource>& data_source) { if (SUCCEEDED(data_source.CoCreateInstance(CLSID_DiaSource))) { return true; } @@ -134,7 +136,7 @@ bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) { // 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; + const wchar_t* msdia_dll = nullptr; if (CLSID_DiaSource == _uuidof(DiaSource100)) { msdia_dll = L"msdia100.dll"; } else if (CLSID_DiaSource == _uuidof(DiaSource110)) { @@ -147,23 +149,261 @@ bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) { if (msdia_dll && SUCCEEDED(NoRegCoCreate(msdia_dll, CLSID_DiaSource, IID_IDiaDataSource, - reinterpret_cast<void **>(&data_source)))) { + reinterpret_cast<void**>(&data_source)))) { return true; } return false; } +const DWORD kUndecorateOptions = 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; + +#define arraysize(f) (sizeof(f) / sizeof(*f)) + +void StripLlvmSuffixAndUndecorate(BSTR* name) { + // LLVM sometimes puts a suffix on symbols to give them a globally unique + // name. The suffix is either some string preceded by a period (like in the + // Itanium ABI; also on Windows this is safe since periods are otherwise + // never part of mangled names), or a dollar sign followed by a 32-char hex + // string (this should go away in future LLVM versions). Strip such suffixes + // and try demangling again. + // + // + // Example symbol names with such suffixes: + // + // ?foo@@YAXXZ$5520c83448162c04f2b239db4b5a2c61 + // ?foo@@YAXXZ.llvm.13040715209719948753 + + if (**name != L'?') + return; // The name is already demangled. + + for (size_t i = 0, len = wcslen(*name); i < len; i++) { + wchar_t c = (*name)[i]; + + if (c == L'.' || (c == L'$' && len - i == 32 + 1)) { + (*name)[i] = L'\0'; + wchar_t undecorated[1024]; + DWORD res = UnDecorateSymbolNameW(*name, undecorated, + arraysize(undecorated), + kUndecorateOptions); + if (res == 0 || undecorated[0] == L'?') { + // Demangling failed; restore the symbol name and return. + (*name)[i] = c; + return; + } + + SysFreeString(*name); + *name = SysAllocString(undecorated); + return; + } + } +} + +// Prints the error message related to the error code as seen in +// Microsoft's MSVS documentation for loadDataFromPdb and loadDataForExe. +void PrintOpenError(HRESULT hr, const char* fn_name, const wchar_t* file) { + switch (hr) { + case E_PDB_NOT_FOUND: + fprintf(stderr, "%s: Failed to open %ws, or the file has an " + "invalid format.\n", fn_name, file); + break; + case E_PDB_FORMAT: + fprintf(stderr, "%s: Attempted to access %ws with an obsolete " + "format.\n", fn_name, file); + break; + case E_PDB_INVALID_SIG: + fprintf(stderr, "%s: Signature does not match for %ws.\n", fn_name, + file); + break; + case E_PDB_INVALID_AGE: + fprintf(stderr, "%s: Age does not match for %ws.\n", fn_name, file); + break; + case E_INVALIDARG: + fprintf(stderr, "%s: Invalid parameter for %ws.\n", fn_name, file); + break; + case E_UNEXPECTED: + fprintf(stderr, "%s: Data source has already been prepared for %ws.\n", + fn_name, file); + break; + default: + fprintf(stderr, "%s: Unexpected error 0x%lx, file: %ws.\n", + fn_name, hr, file); + break; + } +} + } // namespace -PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) { +PDBSourceLineWriter::Inline::Inline(int inline_nest_level) + : inline_nest_level_(inline_nest_level) {} + +void PDBSourceLineWriter::Inline::SetOriginId(int origin_id) { + origin_id_ = origin_id; +} + +void PDBSourceLineWriter::Inline::ExtendRanges(const Line& line) { + if (ranges_.empty()) { + ranges_[line.rva] = line.length; + return; + } + auto iter = ranges_.lower_bound(line.rva); + // There is no overlap if this function is called with inlinee lines from + // the same callsite. + if (iter == ranges_.begin()) { + return; + } + if (line.rva + line.length == iter->first) { + // If they are connected, merge their ranges into one. + DWORD length = line.length + iter->second; + ranges_.erase(iter); + ranges_[line.rva] = length; + } else { + --iter; + if (iter->first + iter->second == line.rva) { + ranges_[iter->first] = iter->second + line.length; + } else { + ranges_[line.rva] = line.length; + } + } +} + +void PDBSourceLineWriter::Inline::SetCallSiteLine(DWORD call_site_line) { + call_site_line_ = call_site_line; } +void PDBSourceLineWriter::Inline::SetCallSiteFileId(DWORD call_site_file_id) { + call_site_file_id_ = call_site_file_id; +} + +void PDBSourceLineWriter::Inline::SetChildInlines( + vector<unique_ptr<Inline>> child_inlines) { + child_inlines_ = std::move(child_inlines); +} + +void PDBSourceLineWriter::Inline::Print(FILE* output) const { + // Ignore INLINE record that doesn't have any range. + if (ranges_.empty()) + return; + fprintf(output, "INLINE %d %lu %lu %d", inline_nest_level_, call_site_line_, + call_site_file_id_, origin_id_); + for (const auto& r : ranges_) { + fprintf(output, " %lx %lx", r.first, r.second); + } + fprintf(output, "\n"); + for (const unique_ptr<Inline>& in : child_inlines_) { + in->Print(output); + } +} + +const PDBSourceLineWriter::Line* PDBSourceLineWriter::Lines::GetLine( + DWORD rva) const { + auto iter = line_map_.find(rva); + if (iter == line_map_.end()) { + // If not found exact rva, check if it's within any range. + iter = line_map_.lower_bound(rva); + if (iter == line_map_.begin()) + return nullptr; + --iter; + auto l = iter->second; + // This happens when there is no top level lines cover this rva (e.g. empty + // lines found for the function). Then we don't know the call site line + // number for this inlined function. + if (rva >= l.rva + l.length) + return nullptr; + } + return &iter->second; +} + +DWORD PDBSourceLineWriter::Lines::GetLineNum(DWORD rva) const { + const Line* line = GetLine(rva); + return line ? line->line_num : 0; +} + +DWORD PDBSourceLineWriter::Lines::GetFileId(DWORD rva) const { + const Line* line = GetLine(rva); + return line ? line->file_id : 0; +} + +void PDBSourceLineWriter::Lines::AddLine(const Line& line) { + if (line_map_.empty()) { + line_map_[line.rva] = line; + return; + } + + // Given an existing line in line_map_, remove it from line_map_ if it + // overlaps with the line and add a new line for the non-overlap range. Return + // true if there is an overlap. + auto intercept = [&](Line old_line) { + DWORD end = old_line.rva + old_line.length; + // No overlap. + if (old_line.rva >= line.rva + line.length || line.rva >= end) + return false; + // old_line is within the line. + if (old_line.rva >= line.rva && end <= line.rva + line.length) { + line_map_.erase(old_line.rva); + return true; + } + // Then there is a overlap. + if (old_line.rva < line.rva) { + old_line.length -= end - line.rva; + if (end > line.rva + line.length) { + Line new_line = old_line; + new_line.rva = line.rva + line.length; + new_line.length = end - new_line.rva; + line_map_[new_line.rva] = new_line; + } + } else { + line_map_.erase(old_line.rva); + old_line.length -= line.rva + line.length - old_line.rva; + old_line.rva = line.rva + line.length; + } + line_map_[old_line.rva] = old_line; + return true; + }; + + bool is_intercept; + // Use a loop in cases that there are multiple lines within the given line. + do { + auto iter = line_map_.lower_bound(line.rva); + if (iter == line_map_.end()) { + if (!line_map_.empty()) { + --iter; + intercept(iter->second); + } + break; + } + is_intercept = false; + if (iter != line_map_.begin()) { + // Check if the given line overlaps a line with smaller in the map. + auto prev = line_map_.lower_bound(line.rva); + --prev; + is_intercept = intercept(prev->second); + } + // Check if the given line overlaps a line with greater or equal rva in the + // map. Using operator |= here since it's possible that there are multiple + // lines with greater rva in the map overlap with the given line. + is_intercept |= intercept(iter->second); + } while (is_intercept); + line_map_[line.rva] = line; +} + +PDBSourceLineWriter::PDBSourceLineWriter(bool handle_inline) + : output_(NULL), handle_inline_(handle_inline) {} + PDBSourceLineWriter::~PDBSourceLineWriter() { Close(); } -bool PDBSourceLineWriter::SetCodeFile(const wstring &exe_file) { +bool PDBSourceLineWriter::SetCodeFile(const wstring& exe_file) { if (code_file_.empty()) { code_file_ = exe_file; return true; @@ -173,7 +413,7 @@ bool PDBSourceLineWriter::SetCodeFile(const wstring &exe_file) { return exe_file == code_file_; } -bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { +bool PDBSourceLineWriter::Open(const wstring& file, FileFormat format) { Close(); code_file_.clear(); @@ -192,25 +432,32 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { return false; } + HRESULT from_pdb_result; + HRESULT for_exe_result; + const wchar_t* file_name = file.c_str(); switch (format) { case PDB_FILE: - if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { - fprintf(stderr, "loadDataFromPdb failed for %ws\n", file.c_str()); + from_pdb_result = data_source->loadDataFromPdb(file_name); + if (FAILED(from_pdb_result)) { + PrintOpenError(from_pdb_result, "loadDataFromPdb", file_name); 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()); + for_exe_result = data_source->loadDataForExe(file_name, NULL, NULL); + if (FAILED(for_exe_result)) { + PrintOpenError(for_exe_result, "loadDataForExe", file_name); 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()); + from_pdb_result = data_source->loadDataFromPdb(file_name); + if (FAILED(from_pdb_result)) { + for_exe_result = data_source->loadDataForExe(file_name, NULL, NULL); + if (FAILED(for_exe_result)) { + PrintOpenError(from_pdb_result, "loadDataFromPdb", file_name); + PrintOpenError(for_exe_result, "loadDataForExe", file_name); return false; } code_file_ = file; @@ -228,52 +475,65 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { return true; } -bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) { - // The line number format is: - // <rva> <line number> <source file id> - CComPtr<IDiaLineNumber> line; - ULONG count; +bool PDBSourceLineWriter::GetLine(IDiaLineNumber* dia_line, Line* line) const { + if (FAILED(dia_line->get_relativeVirtualAddress(&line->rva))) { + fprintf(stderr, "failed to get line rva\n"); + return false; + } - 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; - } + if (FAILED(dia_line->get_length(&line->length))) { + fprintf(stderr, "failed to get line code length\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(dia_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 + line->file_id = GetRealFileID(dia_source_id); - 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); + if (FAILED(dia_line->get_lineNumber(&line->line_num))) { + fprintf(stderr, "failed to get line number\n"); + return false; + } + return true; +} - DWORD line_num; - if (FAILED(line->get_lineNumber(&line_num))) { - fprintf(stderr, "failed to get line number\n"); +bool PDBSourceLineWriter::GetLines(IDiaEnumLineNumbers* lines, + Lines* line_list) const { + CComPtr<IDiaLineNumber> line; + ULONG count; + + while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) { + Line l; + if (!GetLine(line, &l)) return false; - } + // Silently ignore zero-length lines. + if (l.length != 0) + line_list->AddLine(l); + line.Release(); + } + return true; +} +void PDBSourceLineWriter::PrintLines(const Lines& lines) const { + // The line number format is: + // <rva> <line number> <source file id> + for (const auto& kv : lines.GetLineMap()) { + const Line& l = kv.second; 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); + MapAddressRange(image_map_, AddressRange(l.rva, l.length), &ranges); + for (auto& range : ranges) { + fprintf(output_, "%lx %lx %lu %lu\n", range.rva, range.length, l.line_num, + l.file_id); } - line.Release(); } - return true; } -bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function, - IDiaSymbol *block, +bool PDBSourceLineWriter::PrintFunction(IDiaSymbol* function, + IDiaSymbol* block, bool has_multiple_symbols) { // The function format is: // FUNC <address> <length> <param_stack_size> <function> @@ -320,9 +580,20 @@ bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function, return false; } - if (!PrintLines(lines)) { + // Get top level lines first, which later may be split into multiple smaller + // lines if any inline exists in their ranges if we want to handle inline. + Lines line_list; + if (!GetLines(lines, &line_list)) { return false; } + if (handle_inline_) { + vector<unique_ptr<Inline>> inlines; + if (!GetInlines(block, &line_list, 0, &inlines)) { + return false; + } + PrintInlines(inlines); + } + PrintLines(line_list); return true; } @@ -340,6 +611,10 @@ bool PDBSourceLineWriter::PrintSourceFiles() { return false; } + // Print a dummy file with id equals 0 to represent unknown file, because + // inline records might have unknown call site. + fwprintf(output_, L"FILE %d unknown file\n", 0); + CComPtr<IDiaSymbol> compiland; ULONG count; while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { @@ -503,6 +778,97 @@ bool PDBSourceLineWriter::PrintFunctions() { return true; } +void PDBSourceLineWriter::PrintInlineOrigins() const { + struct OriginCompare { + bool operator()(const InlineOrigin lhs, const InlineOrigin rhs) const { + return lhs.id < rhs.id; + } + }; + set<InlineOrigin, OriginCompare> origins; + // Sort by origin id. + for (auto const& origin : inline_origins_) + origins.insert(origin.second); + for (auto o : origins) { + fprintf(output_, "INLINE_ORIGIN %d %ls\n", o.id, o.name.c_str()); + } +} + +bool PDBSourceLineWriter::GetInlines(IDiaSymbol* block, + Lines* line_list, + int inline_nest_level, + vector<unique_ptr<Inline>>* inlines) { + CComPtr<IDiaEnumSymbols> inline_callsites; + if (FAILED(block->findChildrenEx(SymTagInlineSite, nullptr, nsNone, + &inline_callsites))) { + return false; + } + ULONG count; + CComPtr<IDiaSymbol> callsite; + while (SUCCEEDED(inline_callsites->Next(1, &callsite, &count)) && + count == 1) { + unique_ptr<Inline> new_inline(new Inline(inline_nest_level)); + CComPtr<IDiaEnumLineNumbers> lines; + // All inlinee lines have the same file id. + DWORD file_id = 0; + DWORD call_site_line = 0; + if (FAILED(session_->findInlineeLines(callsite, &lines))) { + return false; + } + CComPtr<IDiaLineNumber> dia_line; + while (SUCCEEDED(lines->Next(1, &dia_line, &count)) && count == 1) { + Line line; + if (!GetLine(dia_line, &line)) { + return false; + } + // Silently ignore zero-length lines. + if (line.length != 0) { + // Use the first line num and file id at rva as this inline's call site + // line number, because after adding lines it may be changed to inner + // line number and inner file id. + if (call_site_line == 0) + call_site_line = line_list->GetLineNum(line.rva); + if (file_id == 0) + file_id = line_list->GetFileId(line.rva); + line_list->AddLine(line); + new_inline->ExtendRanges(line); + } + dia_line.Release(); + } + BSTR name; + callsite->get_name(&name); + if (SysStringLen(name) == 0) { + name = SysAllocString(L"<name omitted>"); + } + auto iter = inline_origins_.find(name); + if (iter == inline_origins_.end()) { + InlineOrigin origin; + origin.id = inline_origins_.size(); + origin.name = name; + inline_origins_[name] = origin; + } + new_inline->SetOriginId(inline_origins_[name].id); + new_inline->SetCallSiteLine(call_site_line); + new_inline->SetCallSiteFileId(file_id); + // Go to next level. + vector<unique_ptr<Inline>> child_inlines; + if (!GetInlines(callsite, line_list, inline_nest_level + 1, + &child_inlines)) { + return false; + } + new_inline->SetChildInlines(std::move(child_inlines)); + inlines->push_back(std::move(new_inline)); + callsite.Release(); + } + return true; +} + +void PDBSourceLineWriter::PrintInlines( + const vector<unique_ptr<Inline>>& inlines) const { + for (const unique_ptr<Inline>& in : inlines) { + in->Print(output_); + } +} + #undef max bool PDBSourceLineWriter::PrintFrameDataUsingPDB() { @@ -673,13 +1039,11 @@ bool PDBSourceLineWriter::PrintFrameData() { PDBModuleInfo info; if (GetModuleInfo(&info) && info.cpu == L"x86_64") { return PrintFrameDataUsingEXE(); - } else { - return PrintFrameDataUsingPDB(); } - return false; + return PrintFrameDataUsingPDB(); } -bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol, +bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol* symbol, bool has_multiple_symbols) { BOOL is_code; if (FAILED(symbol->get_code(&is_code))) { @@ -781,9 +1145,9 @@ bool PDBSourceLineWriter::PrintPEInfo() { // 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) { +static bool wcstol_positive_strict(wchar_t* string, int* result) { int value = 0; - for (wchar_t *c = string; *c != '\0'; ++c) { + for (wchar_t* c = string; *c != '\0'; ++c) { int last_value = value; value *= 10; // Detect overflow. @@ -821,7 +1185,7 @@ bool PDBSourceLineWriter::FindPEFile() { wstring file(symbols_file); // Look for an EXE or DLL file. - const wchar_t *extensions[] = { L"exe", L"dll" }; + 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) { @@ -839,23 +1203,13 @@ bool PDBSourceLineWriter::FindPEFile() { } // static -bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function, - BSTR *name, - int *stack_param_size) { +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_undecoratedNameEx(kUndecorateOptions, name) != S_OK) { if (function->get_name(name) != S_OK) { fprintf(stderr, "failed to get function name\n"); return false; @@ -879,15 +1233,17 @@ bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function, // all of the parameter and return type information may not be included in // the name string. } else { + StripLlvmSuffixAndUndecorate(name); + // 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 wchar_t* replace_string = L"(void)"; const size_t replace_length = wcslen(replace_string); - const wchar_t *replacement_string = L"()"; + const wchar_t* replacement_string = L"()"; size_t length = wcslen(*name); if (length >= replace_length) { - wchar_t *name_end = *name + 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); @@ -903,7 +1259,7 @@ bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function, // whether the undecorated name contains any ':' or '(' characters. if (!wcschr(*name, ':') && !wcschr(*name, '(') && (*name[0] == '_' || *name[0] == '@')) { - wchar_t *last_at = wcsrchr(*name + 1, '@'); + 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). @@ -935,7 +1291,7 @@ bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function, } // static -int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) { +int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol* function) { // This implementation is highly x86-specific. // Gather the symbols corresponding to data. @@ -1050,7 +1406,7 @@ next_child: return param_size; } -bool PDBSourceLineWriter::WriteSymbols(FILE *symbol_file) { +bool PDBSourceLineWriter::WriteSymbols(FILE* symbol_file) { output_ = symbol_file; // Load the OMAP information, and disable auto-translation of addresses in @@ -1063,10 +1419,8 @@ bool PDBSourceLineWriter::WriteSymbols(FILE *symbol_file) { bool ret = PrintPDBInfo(); // This is not a critical piece of the symbol file. PrintPEInfo(); - ret = ret && - PrintSourceFiles() && - PrintFunctions() && - PrintFrameData(); + ret = ret && PrintSourceFiles() && PrintFunctions() && PrintFrameData(); + PrintInlineOrigins(); output_ = NULL; return ret; @@ -1078,7 +1432,7 @@ void PDBSourceLineWriter::Close() { } } -bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { +bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo* info) { if (!info) { return false; } @@ -1143,7 +1497,7 @@ bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { return true; } -bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) { +bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo* info) { if (!info) { return false; } @@ -1156,7 +1510,7 @@ bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) { return ReadPEInfo(code_file_, info); } -bool PDBSourceLineWriter::UsesGUID(bool *uses_guid) { +bool PDBSourceLineWriter::UsesGUID(bool* uses_guid) { if (!uses_guid) return false; diff --git a/src/common/windows/pdb_source_line_writer.h b/src/common/windows/pdb_source_line_writer.h index c0adf29f..8c74e2ca 100644 --- a/src/common/windows/pdb_source_line_writer.h +++ b/src/common/windows/pdb_source_line_writer.h @@ -1,5 +1,4 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. +// Copyright 2006 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -11,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // @@ -35,8 +34,11 @@ #include <atlcomcli.h> +#include <map> +#include <memory> #include <string> #include <unordered_map> +#include <vector> #include "common/windows/module_info.h" #include "common/windows/omap.h" @@ -47,6 +49,8 @@ struct IDiaSymbol; namespace google_breakpad { +using std::map; +using std::vector; using std::wstring; using std::unordered_map; @@ -58,14 +62,14 @@ class PDBSourceLineWriter { ANY_FILE // try PDB_FILE and then EXE_FILE }; - explicit PDBSourceLineWriter(); + explicit PDBSourceLineWriter(bool handle_inline = false); ~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); + bool Open(const wstring& file, FileFormat format); // Closes the current pdb file and its associated resources. void Close(); @@ -77,7 +81,7 @@ class PDBSourceLineWriter { // 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); + bool SetCodeFile(const wstring& exe_file); // Writes a Breakpad symbol file from the current pdb file to |symbol_file|. // Returns true on success. @@ -99,9 +103,110 @@ class PDBSourceLineWriter { bool UsesGUID(bool *uses_guid); private: - // Outputs the line/address pairs for each line in the enumerator. + // InlineOrigin represents INLINE_ORIGIN record in a symbol file. It's an + // inlined function. + struct InlineOrigin { + // The unique id for an InlineOrigin. + int id; + // The name of the inlined function. + wstring name; + }; + + // Line represents LINE record in a symbol file. It represents a source code + // line. + struct Line { + // The relative address of a line. + DWORD rva; + // The number bytes this line has. + DWORD length; + // The source line number. + DWORD line_num; + // The source file id where the source line is located at. + DWORD file_id; + }; + + // Inline represents INLINE record in a symbol file. + class Inline { + public: + explicit Inline(int inline_nest_level); + + void SetOriginId(int origin_id); + + // Adding inlinee line's range into ranges. If line is adjacent with any + // existing lines, extend the range. Otherwise, add line as a new range. + void ExtendRanges(const Line& line); + + void SetCallSiteLine(DWORD call_site_line); + + void SetCallSiteFileId(DWORD call_site_file_id); + + void SetChildInlines(std::vector<std::unique_ptr<Inline>> child_inlines); + + void Print(FILE* output) const; + + private: + // The nest level of this inline record. + int inline_nest_level_; + // The source line number at where this inlined function is called. + DWORD call_site_line_ = 0; + // The call site file id at where this inlined function is called. + DWORD call_site_file_id_ = 0; + // The id used for referring to an InlineOrigin. + int origin_id_ = 0; + // A map from rva to length. This is the address ranges covered by this + // Inline. + map<DWORD, DWORD> ranges_; + // The list of direct Inlines inlined inside this Inline. + vector<std::unique_ptr<Inline>> child_inlines_; + }; + + // Lines represents a map of lines inside a function with rva as the key. + // AddLine function adds a line into the map and ensures that there is no + // overlap between any two lines in the map. + class Lines { + public: + const map<DWORD, Line>& GetLineMap() const { return line_map_; } + + // Finds the line from line_map_ that contains the given rva returns its + // line_num. If not found, return 0. + DWORD GetLineNum(DWORD rva) const; + + // Finds the line from line_map_ that contains the given rva returns its + // file_id. If not found, return 0. + DWORD GetFileId(DWORD rva) const; + + // Add the `line` into line_map_. If the `line` overlaps with existing + // lines, truncate the existing lines and add the given line. It ensures + // that all lines in line_map_ do not overlap with each other. For example, + // suppose there is a line A in the map and we call AddLine with Line B. + // Line A: rva: 100, length: 20, line_num: 10, file_id: 1 + // Line B: rva: 105, length: 10, line_num: 4, file_id: 2 + // After calling AddLine with Line B, we will have the following lines: + // Line 1: rva: 100, length: 5, line_num: 10, file_id: 1 + // Line 2: rva: 105, length: 10, line_num: 4, file_id: 2 + // Line 3: rva: 115, length: 5, line_num: 10, file_id: 1 + void AddLine(const Line& line); + + private: + // Finds the line from line_map_ that contains the given rva. If not found, + // return nullptr. + const Line* GetLine(DWORD rva) const; + // The key is rva. AddLine function ensures that any two lines in the map do + // not overlap. + map<DWORD, Line> line_map_; + }; + + // Construct Line from IDiaLineNumber. The output Line is stored at line. + // Return true on success. + bool GetLine(IDiaLineNumber* dia_line, Line* line) const; + + // Construct Lines from IDiaEnumLineNumbers. The list of Lines are stored at + // line_list. // Returns true on success. - bool PrintLines(IDiaEnumLineNumbers *lines); + bool GetLines(IDiaEnumLineNumbers* lines, Lines* line_list) const; + + // Outputs the line/address pairs for each line in the enumerator. + void PrintLines(const Lines& lines) const; // 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 @@ -118,6 +223,25 @@ class PDBSourceLineWriter { // Returns true on success. bool PrintSourceFiles(); + // Output all inline origins. + void PrintInlineOrigins() const; + + // Retrieve inlines inside the given block. It also adds inlinee lines to + // `line_list` since inner lines are more precise source location. If the + // block has children wih SymTagInlineSite Tag, it will recursively (DFS) call + // itself with each child as first argument. Returns true on success. + // `block`: the IDiaSymbol that may have inline sites. + // `line_list`: the list of lines inside current function. + // `inline_nest_level`: the nest level of block's Inlines. + // `inlines`: the vector to store the list of inlines for the block. + bool GetInlines(IDiaSymbol* block, + Lines* line_list, + int inline_nest_level, + vector<std::unique_ptr<Inline>>* inlines); + + // Outputs all inlines. + void PrintInlines(const vector<std::unique_ptr<Inline>>& inlines) const; + // 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. @@ -150,17 +274,17 @@ class PDBSourceLineWriter { // 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) { + 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) { + 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) { + 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 @@ -172,8 +296,8 @@ class PDBSourceLineWriter { // 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); + DWORD GetRealFileID(DWORD id) const { + unordered_map<DWORD, DWORD>::const_iterator iter = file_ids_.find(id); if (iter == file_ids_.end()) return id; return iter->second; @@ -213,9 +337,15 @@ class PDBSourceLineWriter { // This maps unique filenames to file IDs. unordered_map<wstring, DWORD> unique_files_; + // The INLINE_ORIGINS records. The key is the function name. + std::map<wstring, InlineOrigin> inline_origins_; + // This is used for calculating post-transform symbol addresses and lengths. ImageMap image_map_; + // If we should output INLINE/INLINE_ORIGIN records + bool handle_inline_; + // Disallow copy ctor and operator= PDBSourceLineWriter(const PDBSourceLineWriter&); void operator=(const PDBSourceLineWriter&); diff --git a/src/common/windows/pe_source_line_writer.cc b/src/common/windows/pe_source_line_writer.cc index cb6cc713..a568e0c7 100644 --- a/src/common/windows/pe_source_line_writer.cc +++ b/src/common/windows/pe_source_line_writer.cc @@ -1,77 +1,76 @@ -// Copyright (c) 2019, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "common/windows/pe_source_line_writer.h"
-
-#include "common/windows/pe_util.h"
-
-namespace google_breakpad {
-PESourceLineWriter::PESourceLineWriter(const wstring& pe_file) :
- pe_file_(pe_file) {
-}
-
-PESourceLineWriter::~PESourceLineWriter() {
-}
-
-bool PESourceLineWriter::WriteSymbols(FILE* symbol_file) {
- PDBModuleInfo module_info;
- if (!GetModuleInfo(&module_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(symbol_file, "MODULE windows %ws %ws %ws\n",
- module_info.cpu.c_str(), module_info.debug_identifier.c_str(),
- module_info.debug_file.c_str());
-
- PEModuleInfo pe_info;
- if (!GetPEInfo(&pe_info)) {
- return false;
- }
- fprintf(symbol_file, "INFO CODE_ID %ws %ws\n",
- pe_info.code_identifier.c_str(),
- pe_info.code_file.c_str());
-
- if (!PrintPEFrameData(pe_file_, symbol_file)) {
- return false;
- }
-
- return true;
-}
-
-bool PESourceLineWriter::GetModuleInfo(PDBModuleInfo* info) {
- return ReadModuleInfo(pe_file_, info);
-}
-
-bool PESourceLineWriter::GetPEInfo(PEModuleInfo* info) {
- return ReadPEInfo(pe_file_, info);
-}
-
-} // namespace google_breakpad
+// Copyright 2019 Google LLC +// +// 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 LLC 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/pe_source_line_writer.h" + +#include "common/windows/pe_util.h" + +namespace google_breakpad { +PESourceLineWriter::PESourceLineWriter(const wstring& pe_file) : + pe_file_(pe_file) { +} + +PESourceLineWriter::~PESourceLineWriter() { +} + +bool PESourceLineWriter::WriteSymbols(FILE* symbol_file) { + PDBModuleInfo module_info; + if (!GetModuleInfo(&module_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(symbol_file, "MODULE windows %ws %ws %ws\n", + module_info.cpu.c_str(), module_info.debug_identifier.c_str(), + module_info.debug_file.c_str()); + + PEModuleInfo pe_info; + if (!GetPEInfo(&pe_info)) { + return false; + } + fprintf(symbol_file, "INFO CODE_ID %ws %ws\n", + pe_info.code_identifier.c_str(), + pe_info.code_file.c_str()); + + if (!PrintPEFrameData(pe_file_, symbol_file)) { + return false; + } + + return true; +} + +bool PESourceLineWriter::GetModuleInfo(PDBModuleInfo* info) { + return ReadModuleInfo(pe_file_, info); +} + +bool PESourceLineWriter::GetPEInfo(PEModuleInfo* info) { + return ReadPEInfo(pe_file_, info); +} + +} // namespace google_breakpad diff --git a/src/common/windows/pe_source_line_writer.h b/src/common/windows/pe_source_line_writer.h index 2bf1d4fd..a3748145 100644 --- a/src/common/windows/pe_source_line_writer.h +++ b/src/common/windows/pe_source_line_writer.h @@ -1,69 +1,68 @@ -// Copyright (c) 2019, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_
-#define COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_
-
-#include <string>
-
-#include "common/basictypes.h"
-#include "common/windows/module_info.h"
-
-namespace google_breakpad {
-
-using std::wstring;
-
-// PESourceLineWriter uses a pe file produced by Visual C++ to output
-// a line/address map for use with BasicSourceLineResolver.
-// NOTE: Only supports PE32+ format, ie. a 64bit PE file.
-class PESourceLineWriter {
-public:
- explicit PESourceLineWriter(const wstring& pe_file);
- ~PESourceLineWriter();
-
- // Writes Breakpad symbols from the pe file to |symbol_file|.
- // Returns true on success.
- bool WriteSymbols(FILE* symbol_file);
-
- // Retrieves information about the module. Returns true on success.
- bool GetModuleInfo(PDBModuleInfo* info);
-
+// Copyright 2019 Google LLC +// +// 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 LLC 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. + +#ifndef COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_ +#define COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_ + +#include <string> + +#include "common/basictypes.h" +#include "common/windows/module_info.h" + +namespace google_breakpad { + +using std::wstring; + +// PESourceLineWriter uses a pe file produced by Visual C++ to output +// a line/address map for use with BasicSourceLineResolver. +// NOTE: Only supports PE32+ format, ie. a 64bit PE file. +class PESourceLineWriter { +public: + explicit PESourceLineWriter(const wstring& pe_file); + ~PESourceLineWriter(); + + // Writes Breakpad symbols from the pe file to |symbol_file|. + // Returns true on success. + bool WriteSymbols(FILE* symbol_file); + + // Retrieves information about the module. Returns true on success. + bool GetModuleInfo(PDBModuleInfo* info); + // Retrieves information about the module's PE file. Returns - // true on success.
- bool GetPEInfo(PEModuleInfo* info);
-
-private:
- const wstring pe_file_;
-
- DISALLOW_COPY_AND_ASSIGN(PESourceLineWriter);
-};
-
-} // namespace google_breakpad
-
-#endif // COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_
+ // true on success. + bool GetPEInfo(PEModuleInfo* info); + +private: + const wstring pe_file_; + + DISALLOW_COPY_AND_ASSIGN(PESourceLineWriter); +}; + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_PE_SOURCE_LINE_WRITER_H_ diff --git a/src/common/windows/pe_util.cc b/src/common/windows/pe_util.cc index 6fa63fa3..1df93105 100644 --- a/src/common/windows/pe_util.cc +++ b/src/common/windows/pe_util.cc @@ -1,407 +1,411 @@ -// Copyright (c) 2019, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "pe_util.h"
-
-#include <windows.h>
-#include <winnt.h>
-#include <atlbase.h>
-#include <ImageHlp.h>
-
-#include <functional>
-
-#include "common/windows/string_utils-inl.h"
-#include "common/windows/guid_string.h"
-
-namespace {
-
-/*
- * Not defined in WinNT.h for some reason. Definitions taken from:
- * http://uninformed.org/index.cgi?v=4&a=1&p=13
- *
- */
-typedef unsigned char UBYTE;
-
-#if !defined(_WIN64)
-#define UNW_FLAG_EHANDLER 0x01
-#define UNW_FLAG_UHANDLER 0x02
-#define UNW_FLAG_CHAININFO 0x04
-#endif
-
-union UnwindCode {
- struct {
- UBYTE offset_in_prolog;
- UBYTE unwind_operation_code : 4;
- UBYTE operation_info : 4;
- };
- USHORT frame_offset;
-};
-
-enum UnwindOperationCodes {
- UWOP_PUSH_NONVOL = 0, /* info == register number */
- UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
- UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
- UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
- UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
- UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
- // XXX: these are missing from MSDN!
- // See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm
- UWOP_SAVE_XMM,
- UWOP_SAVE_XMM_FAR,
- UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */
- UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
- UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
-};
-
-// See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
-// Note: some fields removed as we don't use them.
-struct UnwindInfo {
- UBYTE version : 3;
- UBYTE flags : 5;
- UBYTE size_of_prolog;
- UBYTE count_of_codes;
- UBYTE frame_register : 4;
- UBYTE frame_offset : 4;
- UnwindCode unwind_code[1];
-};
-
-struct CV_INFO_PDB70 {
- ULONG cv_signature;
- GUID signature;
- ULONG age;
- CHAR pdb_filename[ANYSIZE_ARRAY];
-};
-
-#define CV_SIGNATURE_RSDS 'SDSR'
-
-// A helper class to scope a PLOADED_IMAGE.
-class AutoImage {
-public:
- explicit AutoImage(PLOADED_IMAGE img) : img_(img) {}
- ~AutoImage() {
- if (img_)
- ImageUnload(img_);
- }
-
- operator PLOADED_IMAGE() { return img_; }
- PLOADED_IMAGE operator->() { return img_; }
-
-private:
- PLOADED_IMAGE img_;
-};
-} // namespace
-
-namespace google_breakpad {
-
-using std::unique_ptr;
-using google_breakpad::GUIDString;
-
-bool ReadModuleInfo(const wstring & pe_file, PDBModuleInfo * info) {
- // Convert wchar to native charset because ImageLoad only takes
- // a PSTR as input.
- string img_file;
- if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
- fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
- pe_file.c_str());
- return false;
- }
-
- AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
- if (!img) {
- fprintf(stderr, "Failed to load %s\n", img_file.c_str());
- return false;
- }
-
- info->cpu = FileHeaderMachineToCpuString(
- img->FileHeader->FileHeader.Machine);
-
- PIMAGE_OPTIONAL_HEADER64 optional_header =
- &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
-
- // Search debug directories for a guid signature & age
- DWORD debug_rva = optional_header->
- DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
- DWORD debug_size = optional_header->
- DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
- PIMAGE_DEBUG_DIRECTORY debug_directories =
- static_cast<PIMAGE_DEBUG_DIRECTORY>(
- ImageRvaToVa(img->FileHeader,
- img->MappedAddress,
- debug_rva,
- &img->LastRvaSection));
-
- for (DWORD i = 0; i < debug_size / sizeof(*debug_directories); i++) {
- if (debug_directories[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW ||
- debug_directories[i].SizeOfData < sizeof(CV_INFO_PDB70)) {
- continue;
- }
-
- struct CV_INFO_PDB70* cv_info = static_cast<CV_INFO_PDB70*>(ImageRvaToVa(
- img->FileHeader,
- img->MappedAddress,
- debug_directories[i].AddressOfRawData,
- &img->LastRvaSection));
- if (cv_info->cv_signature != CV_SIGNATURE_RSDS) {
- continue;
- }
-
- info->debug_identifier = GenerateDebugIdentifier(cv_info->age,
- cv_info->signature);
-
- // This code assumes that the pdb_filename is stored as ASCII without
- // multibyte characters, but it's not clear if that's true.
- size_t debug_file_length = strnlen_s(cv_info->pdb_filename, MAX_PATH);
- if (debug_file_length < 0 || debug_file_length >= MAX_PATH) {
- fprintf(stderr, "PE debug directory is corrupt.\n");
- return false;
- }
- std::string debug_file(cv_info->pdb_filename, debug_file_length);
- if (!WindowsStringUtils::safe_mbstowcs(debug_file, &info->debug_file)) {
- fprintf(stderr, "PDB filename '%s' contains unrecognized characters.\n",
- debug_file.c_str());
- return false;
- }
- info->debug_file = WindowsStringUtils::GetBaseName(info->debug_file);
-
- return true;
- }
-
- fprintf(stderr, "Image is missing debug information.\n");
- return false;
-}
-
-bool ReadPEInfo(const wstring & pe_file, PEModuleInfo * info) {
- // Convert wchar to native charset because ImageLoad only takes
- // a PSTR as input.
- string img_file;
- if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
- fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
- pe_file.c_str());
- return false;
- }
-
- AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
- if (!img) {
- fprintf(stderr, "Failed to open PE file: %S\n", pe_file.c_str());
- return false;
- }
-
- info->code_file = WindowsStringUtils::GetBaseName(pe_file);
-
- // The date and time that the file was created by the linker.
- DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp;
- // The size of the file in bytes, including all headers.
- DWORD SizeOfImage = 0;
- PIMAGE_OPTIONAL_HEADER64 opt =
- &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader;
- if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
- // 64-bit PE file.
- SizeOfImage = opt->SizeOfImage;
- }
- else {
- // 32-bit PE file.
- SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage;
- }
- wchar_t code_identifier[32];
- swprintf(code_identifier,
- sizeof(code_identifier) / sizeof(code_identifier[0]),
- L"%08X%X", TimeDateStamp, SizeOfImage);
- info->code_identifier = code_identifier;
-
- return true;
-}
-
-bool PrintPEFrameData(const wstring & pe_file, FILE * out_file)
-{
- // Convert wchar to native charset because ImageLoad only takes
- // a PSTR as input.
- string img_file;
- if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) {
- fprintf(stderr, "Image path '%S' contains unrecognized characters.\n",
- pe_file.c_str());
- return false;
- }
-
- AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL));
- if (!img) {
- fprintf(stderr, "Failed to load %s\n", img_file.c_str());
- return false;
- }
- PIMAGE_OPTIONAL_HEADER64 optional_header =
- &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
- if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
- fprintf(stderr, "Not a PE32+ image\n");
- return false;
- }
-
- // Read Exception Directory
- DWORD exception_rva = optional_header->
- DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
- DWORD exception_size = optional_header->
- DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
- PIMAGE_RUNTIME_FUNCTION_ENTRY funcs =
- static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
- ImageRvaToVa(img->FileHeader,
- img->MappedAddress,
- exception_rva,
- &img->LastRvaSection));
- for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) {
- DWORD unwind_rva = funcs[i].UnwindInfoAddress;
- // handle chaining
- while (unwind_rva & 0x1) {
- unwind_rva ^= 0x1;
- PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
- static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
- ImageRvaToVa(img->FileHeader,
- img->MappedAddress,
- unwind_rva,
- &img->LastRvaSection));
- unwind_rva = chained_func->UnwindInfoAddress;
- }
-
- UnwindInfo *unwind_info = static_cast<UnwindInfo *>(
- ImageRvaToVa(img->FileHeader,
- img->MappedAddress,
- unwind_rva,
- &img->LastRvaSection));
-
- DWORD stack_size = 8; // minimal stack size is 8 for RIP
- DWORD rip_offset = 8;
- do {
- for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) {
- UnwindCode *unwind_code = &unwind_info->unwind_code[c];
- switch (unwind_code->unwind_operation_code) {
- case UWOP_PUSH_NONVOL: {
- stack_size += 8;
- break;
- }
- case UWOP_ALLOC_LARGE: {
- if (unwind_code->operation_info == 0) {
- c++;
- if (c < unwind_info->count_of_codes)
- stack_size += (unwind_code + 1)->frame_offset * 8;
- }
- else {
- c += 2;
- if (c < unwind_info->count_of_codes)
- stack_size += (unwind_code + 1)->frame_offset |
- ((unwind_code + 2)->frame_offset << 16);
- }
- break;
- }
- case UWOP_ALLOC_SMALL: {
- stack_size += unwind_code->operation_info * 8 + 8;
- break;
- }
- case UWOP_SET_FPREG:
- case UWOP_SAVE_XMM:
- case UWOP_SAVE_XMM_FAR:
- break;
- case UWOP_SAVE_NONVOL:
- case UWOP_SAVE_XMM128: {
- c++; // skip slot with offset
- break;
- }
- case UWOP_SAVE_NONVOL_FAR:
- case UWOP_SAVE_XMM128_FAR: {
- c += 2; // skip 2 slots with offset
- break;
- }
- case UWOP_PUSH_MACHFRAME: {
- if (unwind_code->operation_info) {
- stack_size += 88;
- }
- else {
- stack_size += 80;
- }
- rip_offset += 80;
- break;
- }
- }
- }
- if (unwind_info->flags & UNW_FLAG_CHAININFO) {
- PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
- reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
- (unwind_info->unwind_code +
- ((unwind_info->count_of_codes + 1) & ~1)));
-
- unwind_info = static_cast<UnwindInfo *>(
- ImageRvaToVa(img->FileHeader,
- img->MappedAddress,
- chained_func->UnwindInfoAddress,
- &img->LastRvaSection));
- }
- else {
- unwind_info = NULL;
- }
- } while (unwind_info);
- fprintf(out_file, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n",
- funcs[i].BeginAddress,
- funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset);
- fprintf(out_file, "STACK CFI %lx .cfa: $rsp %lu +\n",
- funcs[i].BeginAddress, stack_size);
- }
-
- return true;
-}
-
-wstring GenerateDebugIdentifier(DWORD age, GUID signature)
-{
- // Use the same format that the MS symbol server uses in filesystem
- // hierarchies.
- wchar_t age_string[9];
- swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]),
- L"%x", age);
-
- // remove when VC++7.1 is no longer supported
- age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0';
-
- wstring debug_identifier = GUIDString::GUIDToSymbolServerWString(&signature);
- debug_identifier.append(age_string);
-
- return debug_identifier;
-}
-
-wstring GenerateDebugIdentifier(DWORD age, DWORD signature)
-{
- // Use the same format that the MS symbol server uses in filesystem
- // hierarchies.
- wchar_t identifier_string[17];
- swprintf(identifier_string,
- sizeof(identifier_string) / sizeof(identifier_string[0]),
- L"%08X%x", signature, age);
-
- // remove when VC++7.1 is no longer supported
- identifier_string[sizeof(identifier_string) /
- sizeof(identifier_string[0]) - 1] = L'\0';
-
- return wstring(identifier_string);
-}
-
-} // namespace google_breakpad
+// Copyright 2019 Google LLC +// +// 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 LLC nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "pe_util.h" + +#include <windows.h> +#include <winnt.h> +#include <atlbase.h> +#include <ImageHlp.h> + +#include <functional> +#include <memory> + +#include "common/windows/string_utils-inl.h" +#include "common/windows/guid_string.h" + +namespace { + +/* + * Not defined in WinNT.h prior to SDK 10.0.20348.0 for some reason. + * Definitions taken from: http://uninformed.org/index.cgi?v=4&a=1&p=13 + * + */ +typedef unsigned char UBYTE; + +#if !defined(UNW_FLAG_EHANDLER) +#define UNW_FLAG_EHANDLER 0x01 +#endif +#if !defined(UNW_FLAG_UHANDLER) +#define UNW_FLAG_UHANDLER 0x02 +#endif +#if !defined(UNW_FLAG_CHAININFO) +#define UNW_FLAG_CHAININFO 0x04 +#endif + +union UnwindCode { + struct { + UBYTE offset_in_prolog; + UBYTE unwind_operation_code : 4; + UBYTE operation_info : 4; + }; + USHORT frame_offset; +}; + +enum UnwindOperationCodes { + UWOP_PUSH_NONVOL = 0, /* info == register number */ + UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */ + UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */ + UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ + UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */ + UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */ + // XXX: these are missing from MSDN! + // See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm + UWOP_SAVE_XMM, + UWOP_SAVE_XMM_FAR, + UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */ + UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */ + UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */ +}; + +// See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx +// Note: some fields removed as we don't use them. +struct UnwindInfo { + UBYTE version : 3; + UBYTE flags : 5; + UBYTE size_of_prolog; + UBYTE count_of_codes; + UBYTE frame_register : 4; + UBYTE frame_offset : 4; + UnwindCode unwind_code[1]; +}; + +struct CV_INFO_PDB70 { + ULONG cv_signature; + GUID signature; + ULONG age; + CHAR pdb_filename[ANYSIZE_ARRAY]; +}; + +#define CV_SIGNATURE_RSDS 'SDSR' + +// A helper class to scope a PLOADED_IMAGE. +class AutoImage { +public: + explicit AutoImage(PLOADED_IMAGE img) : img_(img) {} + ~AutoImage() { + if (img_) + ImageUnload(img_); + } + + operator PLOADED_IMAGE() { return img_; } + PLOADED_IMAGE operator->() { return img_; } + +private: + PLOADED_IMAGE img_; +}; +} // namespace + +namespace google_breakpad { + +using std::unique_ptr; +using google_breakpad::GUIDString; + +bool ReadModuleInfo(const wstring & pe_file, PDBModuleInfo * info) { + // Convert wchar to native charset because ImageLoad only takes + // a PSTR as input. + string img_file; + if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) { + fprintf(stderr, "Image path '%S' contains unrecognized characters.\n", + pe_file.c_str()); + return false; + } + + AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL)); + if (!img) { + fprintf(stderr, "Failed to load %s\n", img_file.c_str()); + return false; + } + + info->cpu = FileHeaderMachineToCpuString( + img->FileHeader->FileHeader.Machine); + + PIMAGE_OPTIONAL_HEADER64 optional_header = + &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader; + + // Search debug directories for a guid signature & age + DWORD debug_rva = optional_header-> + DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; + DWORD debug_size = optional_header-> + DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; + PIMAGE_DEBUG_DIRECTORY debug_directories = + static_cast<PIMAGE_DEBUG_DIRECTORY>( + ImageRvaToVa(img->FileHeader, + img->MappedAddress, + debug_rva, + &img->LastRvaSection)); + + for (DWORD i = 0; i < debug_size / sizeof(*debug_directories); i++) { + if (debug_directories[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW || + debug_directories[i].SizeOfData < sizeof(CV_INFO_PDB70)) { + continue; + } + + struct CV_INFO_PDB70* cv_info = static_cast<CV_INFO_PDB70*>(ImageRvaToVa( + img->FileHeader, + img->MappedAddress, + debug_directories[i].AddressOfRawData, + &img->LastRvaSection)); + if (cv_info->cv_signature != CV_SIGNATURE_RSDS) { + continue; + } + + info->debug_identifier = GenerateDebugIdentifier(cv_info->age, + cv_info->signature); + + // This code assumes that the pdb_filename is stored as ASCII without + // multibyte characters, but it's not clear if that's true. + size_t debug_file_length = strnlen_s(cv_info->pdb_filename, MAX_PATH); + if (debug_file_length < 0 || debug_file_length >= MAX_PATH) { + fprintf(stderr, "PE debug directory is corrupt.\n"); + return false; + } + std::string debug_file(cv_info->pdb_filename, debug_file_length); + if (!WindowsStringUtils::safe_mbstowcs(debug_file, &info->debug_file)) { + fprintf(stderr, "PDB filename '%s' contains unrecognized characters.\n", + debug_file.c_str()); + return false; + } + info->debug_file = WindowsStringUtils::GetBaseName(info->debug_file); + + return true; + } + + fprintf(stderr, "Image is missing debug information.\n"); + return false; +} + +bool ReadPEInfo(const wstring & pe_file, PEModuleInfo * info) { + // Convert wchar to native charset because ImageLoad only takes + // a PSTR as input. + string img_file; + if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) { + fprintf(stderr, "Image path '%S' contains unrecognized characters.\n", + pe_file.c_str()); + return false; + } + + AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL)); + if (!img) { + fprintf(stderr, "Failed to open PE file: %S\n", pe_file.c_str()); + return false; + } + + info->code_file = WindowsStringUtils::GetBaseName(pe_file); + + // The date and time that the file was created by the linker. + DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp; + // The size of the file in bytes, including all headers. + DWORD SizeOfImage = 0; + PIMAGE_OPTIONAL_HEADER64 opt = + &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader; + if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + // 64-bit PE file. + SizeOfImage = opt->SizeOfImage; + } + else { + // 32-bit PE file. + SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage; + } + wchar_t code_identifier[32]; + swprintf(code_identifier, + sizeof(code_identifier) / sizeof(code_identifier[0]), + L"%08X%X", TimeDateStamp, SizeOfImage); + info->code_identifier = code_identifier; + + return true; +} + +bool PrintPEFrameData(const wstring & pe_file, FILE * out_file) +{ + // Convert wchar to native charset because ImageLoad only takes + // a PSTR as input. + string img_file; + if (!WindowsStringUtils::safe_wcstombs(pe_file, &img_file)) { + fprintf(stderr, "Image path '%S' contains unrecognized characters.\n", + pe_file.c_str()); + return false; + } + + AutoImage img(ImageLoad((PSTR)img_file.c_str(), NULL)); + if (!img) { + fprintf(stderr, "Failed to load %s\n", img_file.c_str()); + return false; + } + PIMAGE_OPTIONAL_HEADER64 optional_header = + &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader; + if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + fprintf(stderr, "Not a PE32+ image\n"); + return false; + } + + // Read Exception Directory + DWORD exception_rva = optional_header-> + DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; + DWORD exception_size = optional_header-> + DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size; + PIMAGE_RUNTIME_FUNCTION_ENTRY funcs = + static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( + ImageRvaToVa(img->FileHeader, + img->MappedAddress, + exception_rva, + &img->LastRvaSection)); + for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) { + DWORD unwind_rva = funcs[i].UnwindInfoAddress; + // handle chaining + while (unwind_rva & 0x1) { + unwind_rva ^= 0x1; + PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func = + static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( + ImageRvaToVa(img->FileHeader, + img->MappedAddress, + unwind_rva, + &img->LastRvaSection)); + unwind_rva = chained_func->UnwindInfoAddress; + } + + UnwindInfo *unwind_info = static_cast<UnwindInfo*>( + ImageRvaToVa(img->FileHeader, + img->MappedAddress, + unwind_rva, + &img->LastRvaSection)); + + DWORD stack_size = 8; // minimal stack size is 8 for RIP + DWORD rip_offset = 8; + do { + for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) { + UnwindCode *unwind_code = &unwind_info->unwind_code[c]; + switch (unwind_code->unwind_operation_code) { + case UWOP_PUSH_NONVOL: { + stack_size += 8; + break; + } + case UWOP_ALLOC_LARGE: { + if (unwind_code->operation_info == 0) { + c++; + if (c < unwind_info->count_of_codes) + stack_size += (unwind_code + 1)->frame_offset * 8; + } + else { + c += 2; + if (c < unwind_info->count_of_codes) + stack_size += (unwind_code + 1)->frame_offset | + ((unwind_code + 2)->frame_offset << 16); + } + break; + } + case UWOP_ALLOC_SMALL: { + stack_size += unwind_code->operation_info * 8 + 8; + break; + } + case UWOP_SET_FPREG: + case UWOP_SAVE_XMM: + case UWOP_SAVE_XMM_FAR: + break; + case UWOP_SAVE_NONVOL: + case UWOP_SAVE_XMM128: { + c++; // skip slot with offset + break; + } + case UWOP_SAVE_NONVOL_FAR: + case UWOP_SAVE_XMM128_FAR: { + c += 2; // skip 2 slots with offset + break; + } + case UWOP_PUSH_MACHFRAME: { + if (unwind_code->operation_info) { + stack_size += 88; + } + else { + stack_size += 80; + } + rip_offset += 80; + break; + } + } + } + if (unwind_info->flags & UNW_FLAG_CHAININFO) { + PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func = + reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>( + (unwind_info->unwind_code + + ((unwind_info->count_of_codes + 1) & ~1))); + + unwind_info = static_cast<UnwindInfo*>( + ImageRvaToVa(img->FileHeader, + img->MappedAddress, + chained_func->UnwindInfoAddress, + &img->LastRvaSection)); + } + else { + unwind_info = NULL; + } + } while (unwind_info); + fprintf(out_file, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n", + funcs[i].BeginAddress, + funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset); + fprintf(out_file, "STACK CFI %lx .cfa: $rsp %lu +\n", + funcs[i].BeginAddress, stack_size); + } + + return true; +} + +wstring GenerateDebugIdentifier(DWORD age, GUID signature) +{ + // Use the same format that the MS symbol server uses in filesystem + // hierarchies. + wchar_t age_string[9]; + swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]), + L"%x", age); + + // remove when VC++7.1 is no longer supported + age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0'; + + wstring debug_identifier = GUIDString::GUIDToSymbolServerWString(&signature); + debug_identifier.append(age_string); + + return debug_identifier; +} + +wstring GenerateDebugIdentifier(DWORD age, DWORD signature) +{ + // Use the same format that the MS symbol server uses in filesystem + // hierarchies. + wchar_t identifier_string[17]; + swprintf(identifier_string, + sizeof(identifier_string) / sizeof(identifier_string[0]), + L"%08X%x", signature, age); + + // remove when VC++7.1 is no longer supported + identifier_string[sizeof(identifier_string) / + sizeof(identifier_string[0]) - 1] = L'\0'; + + return wstring(identifier_string); +} + +} // namespace google_breakpad diff --git a/src/common/windows/pe_util.h b/src/common/windows/pe_util.h index 634ba293..6c6b364f 100644 --- a/src/common/windows/pe_util.h +++ b/src/common/windows/pe_util.h @@ -1,78 +1,77 @@ -// Copyright (c) 2019, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef COMMON_WINDOWS_PE_UTIL_H_
-#define COMMON_WINDOWS_PE_UTIL_H_
-
-#include <windows.h>
-
-#include "common/windows/module_info.h"
-
-namespace google_breakpad {
-
-using std::wstring;
-
-// Reads |pe_file| and populates |info|. Returns true on success.
-// Only supports PE32+ format, ie. a 64bit PE file.
-// Will fail if |pe_file| does not contain a valid CodeView record.
-bool ReadModuleInfo(const wstring& pe_file, PDBModuleInfo* info);
-
-// Reads |pe_file| and populates |info|. Returns true on success.
-bool ReadPEInfo(const wstring& pe_file, PEModuleInfo* info);
-
-// Reads |pe_file| and prints frame data (aka. unwind info) to |out_file|.
-// Only supports PE32+ format, ie. a 64bit PE file.
-bool PrintPEFrameData(const wstring& pe_file, FILE* out_file);
-
-// Combines a GUID |signature| and DWORD |age| to create a Breakpad debug
-// identifier.
-wstring GenerateDebugIdentifier(DWORD age, GUID signature);
-
-// Combines a DWORD |signature| and DWORD |age| to create a Breakpad debug
-// identifier.
-wstring GenerateDebugIdentifier(DWORD age, DWORD signature);
-
-// Converts |machine| enum value to the corresponding string used by Breakpad.
-// The enum is IMAGE_FILE_MACHINE_*, contained in winnt.h.
-constexpr const wchar_t* FileHeaderMachineToCpuString(WORD machine) {
- switch (machine) {
- case IMAGE_FILE_MACHINE_I386: {
- return L"x86";
- }
- case IMAGE_FILE_MACHINE_IA64:
- case IMAGE_FILE_MACHINE_AMD64: {
- return L"x86_64";
- }
- default: { return L"unknown"; }
- }
-}
-
-} // namespace google_breakpad
-
-#endif // COMMON_WINDOWS_PE_UTIL_H_
+// Copyright 2019 Google LLC +// +// 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 LLC 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. + +#ifndef COMMON_WINDOWS_PE_UTIL_H_ +#define COMMON_WINDOWS_PE_UTIL_H_ + +#include <windows.h> + +#include "common/windows/module_info.h" + +namespace google_breakpad { + +using std::wstring; + +// Reads |pe_file| and populates |info|. Returns true on success. +// Only supports PE32+ format, ie. a 64bit PE file. +// Will fail if |pe_file| does not contain a valid CodeView record. +bool ReadModuleInfo(const wstring& pe_file, PDBModuleInfo* info); + +// Reads |pe_file| and populates |info|. Returns true on success. +bool ReadPEInfo(const wstring& pe_file, PEModuleInfo* info); + +// Reads |pe_file| and prints frame data (aka. unwind info) to |out_file|. +// Only supports PE32+ format, ie. a 64bit PE file. +bool PrintPEFrameData(const wstring& pe_file, FILE* out_file); + +// Combines a GUID |signature| and DWORD |age| to create a Breakpad debug +// identifier. +wstring GenerateDebugIdentifier(DWORD age, GUID signature); + +// Combines a DWORD |signature| and DWORD |age| to create a Breakpad debug +// identifier. +wstring GenerateDebugIdentifier(DWORD age, DWORD signature); + +// Converts |machine| enum value to the corresponding string used by Breakpad. +// The enum is IMAGE_FILE_MACHINE_*, contained in winnt.h. +constexpr const wchar_t* FileHeaderMachineToCpuString(WORD machine) { + switch (machine) { + case IMAGE_FILE_MACHINE_I386: { + return L"x86"; + } + case IMAGE_FILE_MACHINE_IA64: + case IMAGE_FILE_MACHINE_AMD64: { + return L"x86_64"; + } + default: { return L"unknown"; } + } +} + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_PE_UTIL_H_ diff --git a/src/common/windows/string_utils-inl.h b/src/common/windows/string_utils-inl.h index 9b636072..c6f5e0ac 100644 --- a/src/common/windows/string_utils-inl.h +++ b/src/common/windows/string_utils-inl.h @@ -1,5 +1,4 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. +// Copyright 2006 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -11,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // @@ -72,26 +71,26 @@ class WindowsStringUtils { // Roughly equivalent to MSVC8's wcscpy_s, except pre-MSVC8, this does // not fail if source is longer than destination_size. The destination // buffer is always 0-terminated. - static void safe_wcscpy(wchar_t *destination, size_t destination_size, - const wchar_t *source); + static void safe_wcscpy(wchar_t* destination, size_t destination_size, + const wchar_t* source); // Roughly equivalent to MSVC8's wcsncpy_s, except that _TRUNCATE cannot // be passed directly, and pre-MSVC8, this will not fail if source or count // are longer than destination_size. The destination buffer is always // 0-terminated. - static void safe_wcsncpy(wchar_t *destination, size_t destination_size, - const wchar_t *source, size_t count); + static void safe_wcsncpy(wchar_t* destination, size_t destination_size, + const wchar_t* source, size_t count); // Performs multi-byte to wide character conversion on C++ strings, using // mbstowcs_s (MSVC8) or mbstowcs (pre-MSVC8). Returns false on failure, // without setting wcs. - static bool safe_mbstowcs(const string &mbs, wstring *wcs); + static bool safe_mbstowcs(const string& mbs, wstring* wcs); // The inverse of safe_mbstowcs. - static bool safe_wcstombs(const wstring &wcs, string *mbs); + static bool safe_wcstombs(const wstring& wcs, string* mbs); // Returns the base name of a file, e.g. strips off the path. - static wstring GetBaseName(const wstring &filename); + static wstring GetBaseName(const wstring& filename); private: // Disallow instantiation and other object-based operations. @@ -102,9 +101,9 @@ class WindowsStringUtils { }; // static -inline void WindowsStringUtils::safe_wcscpy(wchar_t *destination, +inline void WindowsStringUtils::safe_wcscpy(wchar_t* destination, size_t destination_size, - const wchar_t *source) { + const wchar_t* source) { #if _MSC_VER >= 1400 // MSVC 2005/8 wcscpy_s(destination, destination_size, source); #else // _MSC_VER >= 1400 @@ -118,9 +117,9 @@ inline void WindowsStringUtils::safe_wcscpy(wchar_t *destination, } // static -inline void WindowsStringUtils::safe_wcsncpy(wchar_t *destination, +inline void WindowsStringUtils::safe_wcsncpy(wchar_t* destination, size_t destination_size, - const wchar_t *source, + const wchar_t* source, size_t count) { #if _MSC_VER >= 1400 // MSVC 2005/8 wcsncpy_s(destination, destination_size, source, count); diff --git a/src/common/windows/string_utils.cc b/src/common/windows/string_utils.cc index 27280003..01dca193 100644 --- a/src/common/windows/string_utils.cc +++ b/src/common/windows/string_utils.cc @@ -1,5 +1,4 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. +// Copyright 2006 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -11,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // @@ -35,7 +34,7 @@ namespace google_breakpad { // static -wstring WindowsStringUtils::GetBaseName(const wstring &filename) { +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) { @@ -45,7 +44,7 @@ wstring WindowsStringUtils::GetBaseName(const wstring &filename) { } // static -bool WindowsStringUtils::safe_mbstowcs(const string &mbs, wstring *wcs) { +bool WindowsStringUtils::safe_mbstowcs(const string& mbs, wstring* wcs) { assert(wcs); // First, determine the length of the destination buffer. @@ -88,7 +87,7 @@ bool WindowsStringUtils::safe_mbstowcs(const string &mbs, wstring *wcs) { } // static -bool WindowsStringUtils::safe_wcstombs(const wstring &wcs, string *mbs) { +bool WindowsStringUtils::safe_wcstombs(const wstring& wcs, string* mbs) { assert(mbs); // First, determine the length of the destination buffer. diff --git a/src/common/windows/sym_upload_v2_protocol.cc b/src/common/windows/sym_upload_v2_protocol.cc new file mode 100644 index 00000000..f2dc660c --- /dev/null +++ b/src/common/windows/sym_upload_v2_protocol.cc @@ -0,0 +1,118 @@ +// Copyright 2022 Google LLC +// +// 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 LLC 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/sym_upload_v2_protocol.h" + +#include <cstdio> + +#include "common/windows/http_upload.h" +#include "common/windows/symbol_collector_client.h" + +using google_breakpad::CompleteUploadResult; +using google_breakpad::HTTPUpload; +using google_breakpad::SymbolCollectorClient; +using google_breakpad::SymbolStatus; +using google_breakpad::UploadUrlResponse; +using std::wstring; + +namespace google_breakpad { + +static bool SymUploadV2ProtocolSend(const wchar_t* api_url, + const wchar_t* api_key, + int* timeout_ms, + const wstring& debug_file, + const wstring& debug_id, + const wstring& symbol_filename, + const wstring& symbol_type, + const wstring& product_name, + bool force) { + wstring url(api_url); + wstring key(api_key); + + if (!force) { + SymbolStatus symbolStatus = SymbolCollectorClient::CheckSymbolStatus( + url, key, timeout_ms, debug_file, debug_id); + if (symbolStatus == SymbolStatus::Found) { + wprintf( + L"Symbol file already exists, upload aborted." + L" Use \"-f\" to overwrite.\n"); + return true; + } else if (symbolStatus == SymbolStatus::Unknown) { + wprintf(L"Failed to get check for existing symbol.\n"); + return false; + } + } + + UploadUrlResponse uploadUrlResponse; + if (!SymbolCollectorClient::CreateUploadUrl(url, key, timeout_ms, + &uploadUrlResponse)) { + wprintf(L"Failed to create upload URL.\n"); + return false; + } + + wstring signed_url = uploadUrlResponse.upload_url; + wstring upload_key = uploadUrlResponse.upload_key; + wstring response; + int response_code; + bool success = HTTPUpload::SendPutRequest( + signed_url, symbol_filename, timeout_ms, &response, &response_code); + if (!success) { + wprintf(L"Failed to send symbol file.\n"); + wprintf(L"Response code: %ld\n", response_code); + wprintf(L"Response:\n"); + wprintf(L"%s\n", response.c_str()); + return false; + } else if (response_code == 0) { + wprintf(L"Failed to send symbol file: No response code\n"); + return false; + } else if (response_code != 200) { + wprintf(L"Failed to send symbol file: Response code %ld\n", response_code); + wprintf(L"Response:\n"); + wprintf(L"%s\n", response.c_str()); + return false; + } + + CompleteUploadResult completeUploadResult = + SymbolCollectorClient::CompleteUpload(url, key, timeout_ms, upload_key, + debug_file, debug_id, symbol_type, + product_name); + if (completeUploadResult == CompleteUploadResult::Error) { + wprintf(L"Failed to complete upload.\n"); + return false; + } else if (completeUploadResult == CompleteUploadResult::DuplicateData) { + wprintf( + L"Uploaded file checksum matched existing file checksum," + L" no change necessary.\n"); + } else { + wprintf(L"Successfully sent the symbol file.\n"); + } + + return true; +} + +} // namespace google_breakpad diff --git a/src/common/windows/sym_upload_v2_protocol.h b/src/common/windows/sym_upload_v2_protocol.h new file mode 100644 index 00000000..19e6f87a --- /dev/null +++ b/src/common/windows/sym_upload_v2_protocol.h @@ -0,0 +1,66 @@ +// Copyright 2022 Google LLC +// +// 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 LLC 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. + +#ifndef COMMON_WINDOWS_SYM_UPLOAD_V2_PROTOCOL_H_ +#define COMMON_WINDOWS_SYM_UPLOAD_V2_PROTOCOL_H_ + +#include <string> + +namespace google_breakpad { + +// Sends file at |symbol_filename| using the sym-upload-v2 protocol to +// |api_url| using key |api_key|, and using identifiers |debug_file| and +// |debug_id|. |timeout_ms| is the number of milliseconds to wait before +// terminating the upload attempt. |symbol_type| is the type of the symbol +// file, which is one of: +// "BREAKPAD" +// "ELF" +// "PE" +// "MACHO" +// "DEBUG_ONLY" +// "DWP" +// "DSYM" +// "PDB" +// "SOURCE_MAP" +// If |product_name| is non-empty then it will be sent as part of the symbol +// metadata. +// If |force| is set then it will overwrite an existing file with the +// same |debug_file| and |debug_id| in the store. +bool SymUploadV2ProtocolSend(const wchar_t* api_url, + const wchar_t* api_key, + int* timeout_ms, + const std::wstring& debug_file, + const std::wstring& debug_id, + const std::wstring& symbol_filename, + const std::wstring& symbol_type, + const std::wstring& product_name, + bool force); + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_SYM_UPLOAD_V2_PROTOCOL_H_
\ No newline at end of file diff --git a/src/common/windows/symbol_collector_client.cc b/src/common/windows/symbol_collector_client.cc index 30c663ed..187b100e 100644 --- a/src/common/windows/symbol_collector_client.cc +++ b/src/common/windows/symbol_collector_client.cc @@ -12,6 +12,7 @@ namespace google_breakpad { bool SymbolCollectorClient::CreateUploadUrl( wstring& api_url, wstring& api_key, + int* timeout_ms, UploadUrlResponse *uploadUrlResponse) { wstring url = api_url + L"/v1/uploads:create" @@ -23,7 +24,7 @@ namespace google_breakpad { url, L"", L"", - NULL, + timeout_ms, &response, &response_code)) { wprintf(L"Failed to create upload url.\n"); @@ -66,17 +67,35 @@ namespace google_breakpad { CompleteUploadResult SymbolCollectorClient::CompleteUpload( wstring& api_url, wstring& api_key, + int* timeout_ms, const wstring& upload_key, const wstring& debug_file, - const wstring& debug_id) { + const wstring& debug_id, + const wstring& type, + const wstring& product_name) { wstring url = api_url + L"/v1/uploads/" + upload_key + L":complete" L"?key=" + api_key; wstring body = L"{ symbol_id: {" - L"debug_file: \"" + debug_file + L"\", " - L"debug_id: \"" + debug_id + L"\" " - L"} }"; + L"debug_file: \"" + + debug_file + + L"\", " + L"debug_id: \"" + + debug_id + + L"\" " + L"}, "; + if (!product_name.empty()) { + body += + L"metadata: {" + L"product_name: \"" + + product_name + + L"\"" + L"},"; + } + body += L"symbol_upload_type: \"" + type + + L"\", " + L"use_async_processing: true }"; wstring response; int response_code; @@ -84,7 +103,7 @@ namespace google_breakpad { url, body, L"application/json", - NULL, + timeout_ms, &response, &response_code)) { wprintf(L"Failed to complete upload.\n"); @@ -116,6 +135,7 @@ namespace google_breakpad { SymbolStatus SymbolCollectorClient::CheckSymbolStatus( wstring& api_url, wstring& api_key, + int* timeout_ms, const wstring& debug_file, const wstring& debug_id) { wstring response; @@ -126,7 +146,7 @@ namespace google_breakpad { if (!HTTPUpload::SendGetRequest( url, - NULL, + timeout_ms, &response, &response_code)) { wprintf(L"Failed to check symbol status.\n"); diff --git a/src/common/windows/symbol_collector_client.h b/src/common/windows/symbol_collector_client.h index 30e0cb32..4e9bf3b6 100644 --- a/src/common/windows/symbol_collector_client.h +++ b/src/common/windows/symbol_collector_client.h @@ -1,5 +1,4 @@ -// Copyright (c) 2019, Google Inc. -// All rights reserved. +// Copyright 2019 Google LLC // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -11,7 +10,7 @@ // 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 +// * Neither the name of Google LLC nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // @@ -64,22 +63,26 @@ namespace google_breakpad { static bool CreateUploadUrl( wstring& api_url, wstring& api_key, + int* timeout_ms, UploadUrlResponse *uploadUrlResponse); // Notify the API that symbol file upload is finished and its contents // are ready to be read and/or used for further processing. - static CompleteUploadResult CompleteUpload( - wstring& api_url, - wstring& api_key, - const wstring& upload_key, - const wstring& debug_file, - const wstring& debug_id); + static CompleteUploadResult CompleteUpload(wstring& api_url, + wstring& api_key, + int* timeout_ms, + const wstring& upload_key, + const wstring& debug_file, + const wstring& debug_id, + const wstring& type, + const wstring& product_name); // Returns whether or not a symbol file corresponding to the debug_file/ // debug_id pair is already present in symbol storage. static SymbolStatus CheckSymbolStatus( wstring& api_url, wstring& api_key, + int* timeout_ms, const wstring& debug_file, const wstring& debug_id); }; |