Move dead submodules in-tree

Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
swurl 2025-05-31 02:33:02 -04:00
parent c0cceff365
commit 6c655321e6
Signed by untrusted user: crueter
GPG key ID: A5A7629F109C8FD1
4081 changed files with 1185566 additions and 45 deletions

View file

@ -0,0 +1,77 @@
// Copyright 2012 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.
// core2md.cc: A utility to convert an ELF core file to a minidump file.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <stdio.h>
#include "client/linux/minidump_writer/minidump_writer.h"
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include "common/path_helper.h"
using google_breakpad::AppMemoryList;
using google_breakpad::MappingList;
using google_breakpad::LinuxCoreDumper;
static int ShowUsage(const char* argv0) {
fprintf(stderr, "Usage: %s <core file> <procfs dir> <output>\n",
google_breakpad::BaseName(argv0).c_str());
return 1;
}
bool WriteMinidumpFromCore(const char* filename,
const char* core_path,
const char* procfs_override) {
MappingList mappings;
AppMemoryList memory_list;
LinuxCoreDumper dumper(0, core_path, procfs_override);
return google_breakpad::WriteMinidump(filename, mappings, memory_list,
&dumper);
}
int main(int argc, char *argv[]) {
if (argc != 4) {
return ShowUsage(argv[0]);
}
const char* core_file = argv[1];
const char* procfs_dir = argv[2];
const char* minidump_file = argv[3];
if (!WriteMinidumpFromCore(minidump_file,
core_file,
procfs_dir)) {
perror("core2md: Unable to generate minidump");
return 1;
}
return 0;
}

View file

@ -0,0 +1,151 @@
// Copyright 2020 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.
// core_handler.cc: A tool to handle coredumps on Linux
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include <sstream>
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/path_helper.h"
#include "common/scoped_ptr.h"
namespace {
using google_breakpad::AppMemoryList;
using google_breakpad::LinuxCoreDumper;
using google_breakpad::MappingList;
using google_breakpad::scoped_array;
// Size of the core dump to read in order to access all the threads
// descriptions.
//
// The first section is the note0 section which contains the thread states. On
// x86-64 a typical thread description take about 1432B. Reading 1 MB allows
// several hundreds of threads.
const int core_read_size = 1024 * 1024;
void ShowUsage(const char* argv0) {
fprintf(stderr, "Usage: %s <process id> <minidump file>\n\n",
google_breakpad::BaseName(argv0).c_str());
fprintf(stderr,
"A tool which serves as a core dump handler and produces "
"minidump files.\n");
fprintf(stderr, "Please refer to the online documentation:\n");
fprintf(stderr,
"https://chromium.googlesource.com/breakpad/breakpad/+/HEAD"
"/docs/linux_core_handler.md\n");
}
bool WriteMinidumpFromCore(const char* filename,
const char* core_path,
const char* procfs_override) {
MappingList mappings;
AppMemoryList memory_list;
LinuxCoreDumper dumper(0, core_path, procfs_override);
return google_breakpad::WriteMinidump(filename, mappings, memory_list,
&dumper);
}
bool HandleCrash(pid_t pid, const char* procfs_dir, const char* md_filename) {
int r = 0;
scoped_array<char> buf(new char[core_read_size]);
while (r != core_read_size) {
int ret = read(STDIN_FILENO, &buf[r], core_read_size - r);
if (ret == 0) {
break;
} else if (ret == -1) {
return false;
}
r += ret;
}
int fd = memfd_create("core_file", MFD_CLOEXEC);
if (fd == -1) {
return false;
}
int w = write(fd, &buf[0], r);
if (w != r) {
close(fd);
return false;
}
std::stringstream core_file_ss;
core_file_ss << "/proc/self/fd/" << fd;
std::string core_file(core_file_ss.str());
if (!WriteMinidumpFromCore(md_filename, core_file.c_str(), procfs_dir)) {
close(fd);
return false;
}
close(fd);
return true;
}
} // namespace
int main(int argc, char* argv[]) {
int ret = EXIT_FAILURE;
if (argc != 3) {
ShowUsage(argv[0]);
return ret;
}
const char* pid_str = argv[1];
const char* md_filename = argv[2];
pid_t pid = atoi(pid_str);
std::stringstream proc_dir_ss;
proc_dir_ss << "/proc/" << pid_str;
std::string proc_dir(proc_dir_ss.str());
openlog("core_handler", 0, 0);
if (HandleCrash(pid, proc_dir.c_str(), md_filename)) {
syslog(LOG_NOTICE, "Minidump generated at %s\n", md_filename);
ret = EXIT_SUCCESS;
} else {
syslog(LOG_ERR, "Cannot generate minidump %s\n", md_filename);
}
closelog();
return ret;
}

View file

@ -0,0 +1,155 @@
// Copyright 2011 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.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <paths.h>
#include <stdio.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
#include "common/linux/dump_symbols.h"
#include "common/path_helper.h"
using google_breakpad::WriteSymbolFile;
using google_breakpad::WriteSymbolFileHeader;
int usage(const char* self) {
fprintf(stderr,
"Usage: %s [OPTION] <binary-with-debugging-info> "
"[directories-for-debug-file]\n\n",
google_breakpad::BaseName(self).c_str());
fprintf(stderr, "Options:\n");
fprintf(stderr, " -i: Output module header information only.\n");
fprintf(stderr, " -c Do not generate CFI section\n");
fprintf(stderr, " -d Generate INLINE/INLINE_ORIGIN records\n");
fprintf(stderr, " -r Do not handle inter-compilation "
"unit references\n");
fprintf(stderr, " -v Print all warnings to stderr\n");
fprintf(stderr, " -n <name> Use specified name for name of the object\n");
fprintf(stderr, " -o <os> Use specified name for the "
"operating system\n");
fprintf(stderr, " -m Enable writing the optional 'm' field on FUNC "
"and PUBLIC, denoting multiple symbols for "
"the address.\n");
return 1;
}
int main(int argc, char** argv) {
if (argc < 2)
return usage(argv[0]);
bool header_only = false;
bool cfi = true;
bool handle_inlines = false;
bool handle_inter_cu_refs = true;
bool log_to_stderr = false;
bool enable_multiple_field = false;
std::string obj_name;
const char* obj_os = "Linux";
int arg_index = 1;
while (arg_index < argc && strlen(argv[arg_index]) > 0 &&
argv[arg_index][0] == '-') {
if (strcmp("-i", argv[arg_index]) == 0) {
header_only = true;
} else if (strcmp("-c", argv[arg_index]) == 0) {
cfi = false;
} else if (strcmp("-d", argv[arg_index]) == 0) {
handle_inlines = true;
} else if (strcmp("-r", argv[arg_index]) == 0) {
handle_inter_cu_refs = false;
} else if (strcmp("-v", argv[arg_index]) == 0) {
log_to_stderr = true;
} else if (strcmp("-n", argv[arg_index]) == 0) {
if (arg_index + 1 >= argc) {
fprintf(stderr, "Missing argument to -n\n");
return usage(argv[0]);
}
obj_name = argv[arg_index + 1];
++arg_index;
} else if (strcmp("-o", argv[arg_index]) == 0) {
if (arg_index + 1 >= argc) {
fprintf(stderr, "Missing argument to -o\n");
return usage(argv[0]);
}
obj_os = argv[arg_index + 1];
++arg_index;
} else if (strcmp("-m", argv[arg_index]) == 0) {
enable_multiple_field = true;
} else {
printf("2.4 %s\n", argv[arg_index]);
return usage(argv[0]);
}
++arg_index;
}
if (arg_index == argc)
return usage(argv[0]);
// Save stderr so it can be used below.
FILE* saved_stderr = fdopen(dup(fileno(stderr)), "w");
if (!log_to_stderr) {
if (freopen(_PATH_DEVNULL, "w", stderr)) {
// If it fails, not a lot we can (or should) do.
// Add this brace section to silence gcc warnings.
}
}
const char* binary;
std::vector<string> debug_dirs;
binary = argv[arg_index];
for (int debug_dir_index = arg_index + 1;
debug_dir_index < argc;
++debug_dir_index) {
debug_dirs.push_back(argv[debug_dir_index]);
}
if (obj_name.empty())
obj_name = binary;
if (header_only) {
if (!WriteSymbolFileHeader(binary, obj_name, obj_os, std::cout)) {
fprintf(saved_stderr, "Failed to process file.\n");
return 1;
}
} else {
SymbolData symbol_data = (handle_inlines ? INLINES : NO_DATA) |
(cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES;
google_breakpad::DumpOptions options(symbol_data, handle_inter_cu_refs,
enable_multiple_field);
if (!WriteSymbolFile(binary, obj_name, obj_os, debug_dirs, options,
std::cout)) {
fprintf(saved_stderr, "Failed to write symbol file.\n");
return 1;
}
}
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
// Copyright 2011 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.
// minidump_memory_range.h: Define the google_breakpad::MinidumpMemoryRange
// class, which adds methods for handling minidump specific data structures
// on top of google_breakpad::MemoryRange. See common/memory_range.h for
// more details on MemoryRange.
#ifndef TOOLS_LINUX_MD2CORE_MINIDUMP_MEMORY_RANGE_H_
#define TOOLS_LINUX_MD2CORE_MINIDUMP_MEMORY_RANGE_H_
#include <string>
#include "common/memory_range.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
// A derived class of MemoryRange with added methods for handling minidump
// specific data structures. To avoid virtual functions, it is not designed
// to be used polymorphically.
class MinidumpMemoryRange : public MemoryRange {
public:
MinidumpMemoryRange() {}
MinidumpMemoryRange(const void* data, size_t length)
: MemoryRange(data, length) {}
// Returns a subrange of |length| bytes at |offset| bytes of this memory
// range, or an empty range if the subrange is out of bounds.
// This methods overrides the base implemementation in order to return
// an instance of MinidumpMemoryRange instead of MemoryRange.
MinidumpMemoryRange Subrange(size_t sub_offset, size_t sub_length) const {
if (Covers(sub_offset, sub_length))
return MinidumpMemoryRange(data() + sub_offset, sub_length);
return MinidumpMemoryRange();
}
// Returns a subrange that covers the offset and length specified by
// |location|, or an empty range if the subrange is out of bounds.
MinidumpMemoryRange Subrange(const MDLocationDescriptor& location) const {
return MinidumpMemoryRange::Subrange(location.rva, location.data_size);
}
// Gets a STL string from a MDString at |sub_offset| bytes of this memory
// range. This method only works correctly for ASCII characters and does
// not convert between UTF-16 and UTF-8.
const std::string GetAsciiMDString(size_t sub_offset) const {
std::string str;
const MDString* md_str = GetData<MDString>(sub_offset);
if (md_str) {
const uint16_t* buffer = &md_str->buffer[0];
for (uint32_t i = 0; i < md_str->length && buffer[i]; ++i) {
str.push_back(buffer[i]);
}
}
return str;
}
};
} // namespace google_breakpad
#endif // TOOLS_LINUX_MD2CORE_MINIDUMP_MEMORY_RANGE_H_

View file

@ -0,0 +1,261 @@
// Copyright 2011 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.
// minidump_memory_range_unittest.cc:
// Unit tests for google_breakpad::MinidumpMemoryRange.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include "breakpad_googletest_includes.h"
#include "tools/linux/md2core/minidump_memory_range.h"
using google_breakpad::MinidumpMemoryRange;
using testing::Message;
namespace {
const uint32_t kBuffer[10] = { 0 };
const size_t kBufferSize = sizeof(kBuffer);
const uint8_t* kBufferPointer = reinterpret_cast<const uint8_t*>(kBuffer);
// Test vectors for verifying Covers, GetData, and Subrange.
const struct {
bool valid;
size_t offset;
size_t length;
} kSubranges[] = {
{ true, 0, 0 },
{ true, 0, 2 },
{ true, 0, kBufferSize },
{ true, 2, 0 },
{ true, 2, 4 },
{ true, 2, kBufferSize - 2 },
{ true, kBufferSize - 1, 1 },
{ false, kBufferSize, 0 },
{ false, kBufferSize, static_cast<size_t>(-1) },
{ false, kBufferSize + 1, 0 },
{ false, static_cast<size_t>(-1), 2 },
{ false, 1, kBufferSize },
{ false, kBufferSize - 1, 2 },
{ false, 0, static_cast<size_t>(-1) },
{ false, 1, static_cast<size_t>(-1) },
};
const size_t kNumSubranges = sizeof(kSubranges) / sizeof(kSubranges[0]);
// Test vectors for verifying GetArrayElement.
const struct {
size_t offset;
size_t size;
size_t index;
const void* const pointer;
} kElements[] = {
// Valid array elemenets
{ 0, 1, 0, kBufferPointer },
{ 0, 1, 1, kBufferPointer + 1 },
{ 0, 1, kBufferSize - 1, kBufferPointer + kBufferSize - 1 },
{ 0, 2, 1, kBufferPointer + 2 },
{ 0, 4, 2, kBufferPointer + 8 },
{ 0, 4, 9, kBufferPointer + 36 },
{ kBufferSize - 1, 1, 0, kBufferPointer + kBufferSize - 1 },
// Invalid array elemenets
{ 0, 1, kBufferSize, NULL },
{ 0, 4, 10, NULL },
{ kBufferSize - 1, 1, 1, NULL },
{ kBufferSize - 1, 2, 0, NULL },
{ kBufferSize, 1, 0, NULL },
};
const size_t kNumElements = sizeof(kElements) / sizeof(kElements[0]);
} // namespace
TEST(MinidumpMemoryRangeTest, DefaultConstructor) {
MinidumpMemoryRange range;
EXPECT_EQ(NULL, range.data());
EXPECT_EQ(0U, range.length());
}
TEST(MinidumpMemoryRangeTest, ConstructorWithDataAndLength) {
MinidumpMemoryRange range(kBuffer, kBufferSize);
EXPECT_EQ(kBufferPointer, range.data());
EXPECT_EQ(kBufferSize, range.length());
}
TEST(MinidumpMemoryRangeTest, Reset) {
MinidumpMemoryRange range;
range.Reset();
EXPECT_EQ(NULL, range.data());
EXPECT_EQ(0U, range.length());
range.Set(kBuffer, kBufferSize);
EXPECT_EQ(kBufferPointer, range.data());
EXPECT_EQ(kBufferSize, range.length());
range.Reset();
EXPECT_EQ(NULL, range.data());
EXPECT_EQ(0U, range.length());
}
TEST(MinidumpMemoryRangeTest, Set) {
MinidumpMemoryRange range;
range.Set(kBuffer, kBufferSize);
EXPECT_EQ(kBufferPointer, range.data());
EXPECT_EQ(kBufferSize, range.length());
range.Set(NULL, 0);
EXPECT_EQ(NULL, range.data());
EXPECT_EQ(0U, range.length());
}
TEST(MinidumpMemoryRangeTest, SubrangeOfEmptyMemoryRange) {
MinidumpMemoryRange range;
MinidumpMemoryRange subrange = range.Subrange(0, 10);
EXPECT_EQ(NULL, subrange.data());
EXPECT_EQ(0U, subrange.length());
}
TEST(MinidumpMemoryRangeTest, SubrangeAndGetData) {
MinidumpMemoryRange range(kBuffer, kBufferSize);
for (size_t i = 0; i < kNumSubranges; ++i) {
bool valid = kSubranges[i].valid;
size_t sub_offset = kSubranges[i].offset;
size_t sub_length = kSubranges[i].length;
SCOPED_TRACE(Message() << "offset=" << sub_offset
<< ", length=" << sub_length);
MinidumpMemoryRange subrange = range.Subrange(sub_offset, sub_length);
if (valid) {
EXPECT_TRUE(range.Covers(sub_offset, sub_length));
EXPECT_EQ(kBufferPointer + sub_offset,
range.GetData(sub_offset, sub_length));
EXPECT_EQ(kBufferPointer + sub_offset, subrange.data());
EXPECT_EQ(sub_length, subrange.length());
} else {
EXPECT_FALSE(range.Covers(sub_offset, sub_length));
EXPECT_EQ(NULL, range.GetData(sub_offset, sub_length));
EXPECT_EQ(NULL, subrange.data());
EXPECT_EQ(0U, subrange.length());
}
}
}
TEST(MinidumpMemoryRangeTest, SubrangeWithMDLocationDescriptor) {
MinidumpMemoryRange range(kBuffer, kBufferSize);
for (size_t i = 0; i < kNumSubranges; ++i) {
bool valid = kSubranges[i].valid;
size_t sub_offset = kSubranges[i].offset;
size_t sub_length = kSubranges[i].length;
SCOPED_TRACE(Message() << "offset=" << sub_offset
<< ", length=" << sub_length);
MDLocationDescriptor location;
location.rva = sub_offset;
location.data_size = sub_length;
MinidumpMemoryRange subrange = range.Subrange(location);
if (valid) {
EXPECT_TRUE(range.Covers(sub_offset, sub_length));
EXPECT_EQ(kBufferPointer + sub_offset,
range.GetData(sub_offset, sub_length));
EXPECT_EQ(kBufferPointer + sub_offset, subrange.data());
EXPECT_EQ(sub_length, subrange.length());
} else {
EXPECT_FALSE(range.Covers(sub_offset, sub_length));
EXPECT_EQ(NULL, range.GetData(sub_offset, sub_length));
EXPECT_EQ(NULL, subrange.data());
EXPECT_EQ(0U, subrange.length());
}
}
}
TEST(MinidumpMemoryRangeTest, GetDataWithTemplateType) {
MinidumpMemoryRange range(kBuffer, kBufferSize);
const char* char_pointer = range.GetData<char>(0);
EXPECT_EQ(reinterpret_cast<const char*>(kBufferPointer), char_pointer);
const int* int_pointer = range.GetData<int>(0);
EXPECT_EQ(reinterpret_cast<const int*>(kBufferPointer), int_pointer);
}
TEST(MinidumpMemoryRangeTest, GetArrayElement) {
MinidumpMemoryRange range(kBuffer, kBufferSize);
for (size_t i = 0; i < kNumElements; ++i) {
size_t element_offset = kElements[i].offset;
size_t element_size = kElements[i].size;
unsigned element_index = kElements[i].index;
const void* const element_pointer = kElements[i].pointer;
SCOPED_TRACE(Message() << "offset=" << element_offset
<< ", size=" << element_size
<< ", index=" << element_index);
EXPECT_EQ(element_pointer, range.GetArrayElement(
element_offset, element_size, element_index));
}
}
TEST(MinidumpMemoryRangeTest, GetArrayElmentWithTemplateType) {
MinidumpMemoryRange range(kBuffer, kBufferSize);
const char* char_pointer = range.GetArrayElement<char>(0, 0);
EXPECT_EQ(reinterpret_cast<const char*>(kBufferPointer), char_pointer);
const int* int_pointer = range.GetArrayElement<int>(0, 0);
EXPECT_EQ(reinterpret_cast<const int*>(kBufferPointer), int_pointer);
}
TEST(MinidumpMemoryRangeTest, GetAsciiMDString) {
uint8_t buffer[100] = { 0 };
MDString* md_str = reinterpret_cast<MDString*>(buffer);
md_str->length = 4;
md_str->buffer[0] = 'T';
md_str->buffer[1] = 'e';
md_str->buffer[2] = 's';
md_str->buffer[3] = 't';
md_str->buffer[4] = '\0';
size_t str2_offset =
sizeof(MDString) + (md_str->length + 1) * sizeof(uint16_t);
md_str = reinterpret_cast<MDString*>(buffer + str2_offset);
md_str->length = 9; // Test length larger than actual string
md_str->buffer[0] = 'S';
md_str->buffer[1] = 't';
md_str->buffer[2] = 'r';
md_str->buffer[3] = 'i';
md_str->buffer[4] = 'n';
md_str->buffer[5] = 'g';
md_str->buffer[6] = '\0';
md_str->buffer[7] = '1';
md_str->buffer[8] = '2';
MinidumpMemoryRange range(buffer, sizeof(buffer));
EXPECT_EQ("Test", range.GetAsciiMDString(0));
EXPECT_EQ("String", range.GetAsciiMDString(str2_offset));
// Test out-of-bounds cases.
EXPECT_EQ("", range.GetAsciiMDString(
sizeof(buffer) - sizeof(MDString) + 1));
EXPECT_EQ("", range.GetAsciiMDString(sizeof(buffer)));
}

View file

@ -0,0 +1,62 @@
// Copyright 2020 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.
// pid2md.cc: An utility to generate a minidump from a running process
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/path_helper.h"
int main(int argc, char* argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <process id> <minidump file>\n\n",
google_breakpad::BaseName(argv[0]).c_str());
fprintf(stderr,
"A tool to generate a minidump from a running process. The process "
"resumes its\nactivity once the operation is completed. Permission "
"to trace the process is\nrequired.\n");
return EXIT_FAILURE;
}
pid_t process_id = atoi(argv[1]);
const char* minidump_file = argv[2];
if (!google_breakpad::WriteMinidump(minidump_file, process_id, process_id)) {
fprintf(stderr, "Unable to generate minidump.\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,159 @@
// Copyright 2006 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.
// minidump_upload.cc: Upload a minidump to a HTTP server.
// The upload is sent as a multipart/form-data POST request with
// the following parameters:
// prod: the product name
// ver: the product version
// symbol_file: the breakpad format symbol file
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include "common/linux/http_upload.h"
#include "common/path_helper.h"
#include "common/using_std_string.h"
using google_breakpad::HTTPUpload;
struct Options {
string minidumpPath;
string uploadURLStr;
string product;
string version;
string proxy;
string proxy_user_pwd;
bool success;
};
//=============================================================================
static void Start(Options *options) {
std::map<string, string> parameters;
// Add parameters
parameters["prod"] = options->product;
parameters["ver"] = options->version;
std::map<string, string> files;
files["upload_file_minidump"] = options->minidumpPath;
// Send it
string response, error;
bool success = HTTPUpload::SendRequest(options->uploadURLStr,
parameters,
files,
options->proxy,
options->proxy_user_pwd,
"",
&response,
NULL,
&error);
if (success) {
printf("Successfully sent the minidump file.\n");
} else {
printf("Failed to send minidump: %s\n", error.c_str());
}
printf("Response:\n");
printf("%s\n", response.c_str());
options->success = success;
}
//=============================================================================
static void
Usage(int argc, const char *argv[]) {
fprintf(stderr, "Submit minidump information.\n");
fprintf(stderr,
"Usage: %s [options...] -p <product> -v <version> <minidump> "
"<upload-URL>\n",
google_breakpad::BaseName(argv[0]).c_str());
fprintf(stderr, "Options:\n");
fprintf(stderr, "<minidump> should be a minidump.\n");
fprintf(stderr, "<upload-URL> is the destination for the upload\n");
fprintf(stderr, "-p:\t <product> Product name\n");
fprintf(stderr, "-v:\t <version> Product version\n");
fprintf(stderr, "-x:\t <host[:port]> Use HTTP proxy on given port\n");
fprintf(stderr, "-u:\t <user[:password]> Set proxy user and password\n");
fprintf(stderr, "-h:\t Usage\n");
fprintf(stderr, "-?:\t Usage\n");
}
//=============================================================================
static void
SetupOptions(int argc, const char *argv[], Options *options) {
extern int optind;
int ch;
while ((ch = getopt(argc, (char * const*)argv, "p:u:v:x:h?")) != -1) {
switch (ch) {
case 'p':
options->product = optarg;
break;
case 'u':
options->proxy_user_pwd = optarg;
break;
case 'v':
options->version = optarg;
break;
case 'x':
options->proxy = optarg;
break;
default:
fprintf(stderr, "Invalid option '%c'\n", ch);
Usage(argc, argv);
exit(1);
break;
}
}
if ((argc - optind) != 2) {
fprintf(stderr, "%s: Missing symbols file and/or upload-URL\n", argv[0]);
Usage(argc, argv);
exit(1);
}
options->minidumpPath = argv[optind];
options->uploadURLStr = argv[optind + 1];
}
//=============================================================================
int main(int argc, const char* argv[]) {
Options options;
SetupOptions(argc, argv, &options);
Start(&options);
return options.success ? 0 : 1;
}

View file

@ -0,0 +1,217 @@
// Copyright 2006 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.
// symupload.cc: Upload a symbol file to a HTTP server. The upload is sent as
// a multipart/form-data POST request with the following parameters:
// code_file: the basename of the module, e.g. "app"
// debug_file: the basename of the debugging file, e.g. "app"
// debug_identifier: the debug file's identifier, usually consisting of
// the guid and age embedded in the pdb, e.g.
// "11111111BBBB3333DDDD555555555555F"
// version: the file version of the module, e.g. "1.2.3.4"
// os: the operating system that the module was built for
// cpu: the CPU that the module was built for
// symbol_file: the contents of the breakpad-format symbol file
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <locale>
#include "common/linux/symbol_upload.h"
#include "common/path_helper.h"
using google_breakpad::sym_upload::UploadProtocol;
using google_breakpad::sym_upload::Options;
static void StrToUpper(std::string* str) {
if (str == nullptr) {
fprintf(stderr, "nullptr passed to StrToUpper.\n");
exit(1);
}
for (size_t i = 0; i < str->length(); i++) {
(*str)[i] = std::toupper((*str)[i], std::locale::classic());
}
}
//=============================================================================
static void
Usage(int argc, const char *argv[]) {
fprintf(stderr, "Submit symbol information.\n");
fprintf(stderr, "Usage: %s [options...] <symbol-file> <upload-URL>\n",
google_breakpad::BaseName(argv[0]).c_str());
fprintf(stderr, "Options:\n");
fprintf(stderr,
"<symbol-file> should be created by using the dump_syms "
"tool.\n");
fprintf(stderr, "<upload-URL> is the destination for the upload\n");
fprintf(stderr, "-p:\t <protocol> One of ['sym-upload-v1',"
" 'sym-upload-v2'], defaults to 'sym-upload-v1'.\n");
fprintf(stderr, "-v:\t Version information (e.g., 1.2.3.4)\n");
fprintf(stderr, "-x:\t <host[:port]> Use HTTP proxy on given port\n");
fprintf(stderr, "-u:\t <user[:password]> Set proxy user and password\n");
fprintf(stderr, "-h:\t Usage\n");
fprintf(stderr, "-?:\t Usage\n");
fprintf(stderr, "\n");
fprintf(stderr, "These options only work with 'sym-upload-v2' protocol:\n");
fprintf(stderr, "-k:\t <API-key> A secret used to authenticate with the"
" API.\n");
fprintf(stderr, "-f:\t Force symbol upload if already exists.\n");
fprintf(stderr, "-t:\t <symbol-type> Explicitly set symbol upload type ("
"default is 'breakpad').\n"
"\t One of ['breakpad', 'elf', 'pe', 'macho', 'debug_only', 'dwp', "
"'dsym', 'pdb'].\n"
"\t Note: When this flag is set to anything other than 'breakpad', then "
"the '-c' and '-i' flags must also be set.\n");
fprintf(stderr, "-c:\t <code-file> Explicitly set 'code_file' for symbol "
"upload (basename of executable).\n");
fprintf(stderr, "-i:\t <debug-id> Explicitly set 'debug_id' for symbol "
"upload (typically build ID of executable).\n");
fprintf(stderr, "\n");
fprintf(stderr, "Examples:\n");
fprintf(stderr, " With 'sym-upload-v1':\n");
fprintf(stderr, " %s path/to/symbol_file http://myuploadserver\n",
argv[0]);
fprintf(stderr, " With 'sym-upload-v2':\n");
fprintf(stderr, " [Defaulting to symbol type 'BREAKPAD']\n");
fprintf(stderr, " %s -p sym-upload-v2 -k mysecret123! "
"path/to/symbol_file http://myuploadserver\n", argv[0]);
fprintf(stderr, " [Explicitly set symbol type to 'elf']\n");
fprintf(stderr, " %s -p sym-upload-v2 -k mysecret123! -t elf "
"-c app -i 11111111BBBB3333DDDD555555555555F "
"path/to/symbol_file http://myuploadserver\n", argv[0]);
}
//=============================================================================
static void
SetupOptions(int argc, const char *argv[], Options *options) {
extern int optind, optopt;
int ch;
constexpr char flag_pattern[] = "u:v:x:p:k:t:c:i:hf?";
while ((ch = getopt(argc, (char * const*)argv, flag_pattern)) != -1) {
switch (ch) {
case 'h':
case '?':
Usage(argc, argv);
// ch might be '?' because getopt found an error while parsing args (as
// opposed to finding "-?" as an arg), in which case optopt is set to
// the bad arg value, so return an error code if optopt is set,
// otherwise exit cleanly.
exit(optopt == 0 ? 0 : 1);
case 'u':
options->proxy_user_pwd = optarg;
break;
case 'v':
options->version = optarg;
break;
case 'x':
options->proxy = optarg;
break;
case 'p':
if (strcmp(optarg, "sym-upload-v2") == 0) {
options->upload_protocol = UploadProtocol::SYM_UPLOAD_V2;
} else if (strcmp(optarg, "sym-upload-v1") == 0) {
options->upload_protocol = UploadProtocol::SYM_UPLOAD_V1;
} else {
fprintf(stderr, "Invalid protocol '%s'\n", optarg);
Usage(argc, argv);
exit(1);
}
break;
case 'k':
options->api_key = optarg;
break;
case 't': {
// This is really an enum, so treat as upper-case for consistency with
// enum naming convention on server-side.
options->type = optarg;
StrToUpper(&(options->type));
break;
}
case 'c':
options->code_file = optarg;
break;
case 'i':
options->debug_id = optarg;
break;
case 'f':
options->force = true;
break;
default:
fprintf(stderr, "Invalid option '%c'\n", ch);
Usage(argc, argv);
exit(1);
}
}
if ((argc - optind) != 2) {
fprintf(stderr, "%s: Missing symbols file and/or upload-URL\n", argv[0]);
Usage(argc, argv);
exit(1);
}
bool is_breakpad_upload = options->type.empty() ||
options->type == google_breakpad::sym_upload::kBreakpadSymbolType;
bool has_code_file = !options->code_file.empty();
bool has_debug_id = !options->debug_id.empty();
if (is_breakpad_upload && (has_code_file || has_debug_id)) {
fprintf(stderr, "\n");
fprintf(stderr, "%s: -c and -i should only be specified for non-breakpad "
"symbol upload types.\n", argv[0]);
fprintf(stderr, "\n");
Usage(argc, argv);
exit(1);
}
if (!is_breakpad_upload && (!has_code_file || !has_debug_id)) {
fprintf(stderr, "\n");
fprintf(stderr, "%s: -c and -i must be specified for non-breakpad "
"symbol upload types.\n", argv[0]);
fprintf(stderr, "\n");
Usage(argc, argv);
exit(1);
}
options->symbolsPath = argv[optind];
options->uploadURLStr = argv[optind + 1];
}
//=============================================================================
int main(int argc, const char* argv[]) {
Options options;
SetupOptions(argc, argv, &options);
google_breakpad::sym_upload::Start(&options);
return options.success ? 0 : 1;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,340 @@
// -*- mode: c++ -*-
// Copyright 2011 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.
// dump_syms_tool.cc: Command line tool that uses the DumpSymbols class.
// TODO(waylonis): accept stdin
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <mach-o/arch.h>
#include <unistd.h>
#include <algorithm>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include "common/mac/dump_syms.h"
#include "common/mac/arch_utilities.h"
#include "common/mac/macho_utilities.h"
#include "common/scoped_ptr.h"
using google_breakpad::DumpSymbols;
using google_breakpad::Module;
using google_breakpad::scoped_ptr;
using std::vector;
struct Options {
Options()
: srcPath(),
dsymPath(),
arch(),
header_only(false),
cfi(true),
handle_inter_cu_refs(true),
handle_inlines(false),
enable_multiple(false),
module_name(),
prefer_extern_name(false) {}
string srcPath;
string dsymPath;
std::optional<ArchInfo> arch;
bool header_only;
bool cfi;
bool handle_inter_cu_refs;
bool handle_inlines;
bool enable_multiple;
string module_name;
bool prefer_extern_name;
};
static bool StackFrameEntryComparator(const Module::StackFrameEntry* a,
const Module::StackFrameEntry* b) {
return a->address < b->address;
}
// Copy the CFI data from |from_module| into |to_module|, for any non-
// overlapping ranges.
static void CopyCFIDataBetweenModules(Module* to_module,
const Module* from_module) {
typedef vector<Module::StackFrameEntry*>::const_iterator Iterator;
// Get the CFI data from both the source and destination modules and ensure
// it is sorted by start address.
vector<Module::StackFrameEntry*> from_data;
from_module->GetStackFrameEntries(&from_data);
std::sort(from_data.begin(), from_data.end(), &StackFrameEntryComparator);
vector<Module::StackFrameEntry*> to_data;
to_module->GetStackFrameEntries(&to_data);
std::sort(to_data.begin(), to_data.end(), &StackFrameEntryComparator);
Iterator to_it = to_data.begin();
for (Iterator it = from_data.begin(); it != from_data.end(); ++it) {
Module::StackFrameEntry* from_entry = *it;
Module::Address from_entry_end = from_entry->address + from_entry->size;
// Find the first CFI record in the |to_module| that does not have an
// address less than the entry to be copied.
while (to_it != to_data.end()) {
if (from_entry->address > (*to_it)->address)
++to_it;
else
break;
}
// If the entry does not overlap, then it is safe to copy to |to_module|.
if (to_it == to_data.end() || (from_entry->address < (*to_it)->address &&
from_entry_end < (*to_it)->address)) {
to_module->AddStackFrameEntry(
std::make_unique<Module::StackFrameEntry>(*from_entry));
}
}
}
static bool SetArchitecture(DumpSymbols& dump_symbols,
const ArchInfo& arch,
const std::string& filename) {
if (!dump_symbols.SetArchitecture(arch)) {
fprintf(stderr, "%s: no architecture '%s' is present in file.\n",
filename.c_str(),
GetNameFromCPUType(arch.cputype, arch.cpusubtype));
size_t available_size;
const SuperFatArch* available =
dump_symbols.AvailableArchitectures(&available_size);
if (available_size == 1)
fprintf(stderr, "the file's architecture is: ");
else
fprintf(stderr, "architectures present in the file are:\n");
for (size_t i = 0; i < available_size; i++) {
const SuperFatArch* arch = &available[i];
fprintf(stderr, "%s\n",
GetNameFromCPUType(arch->cputype, arch->cpusubtype));
}
return false;
}
return true;
}
static bool Start(const Options& options) {
SymbolData symbol_data =
(options.handle_inlines ? INLINES : NO_DATA) |
(options.cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES;
DumpSymbols dump_symbols(symbol_data, options.handle_inter_cu_refs,
options.enable_multiple, options.module_name,
options.prefer_extern_name);
// For x86_64 binaries, the CFI data is in the __TEXT,__eh_frame of the
// Mach-O file, which is not copied into the dSYM. Whereas in i386, the CFI
// data is in the __DWARF,__debug_frame section, which is moved into the
// dSYM. Therefore, to get x86_64 CFI data, dump_syms needs to look at both
// the dSYM and the Mach-O file. If both paths are present and CFI was
// requested, then consider the Module as "split" and dump all the debug data
// from the primary debug info file, the dSYM, and then dump additional CFI
// data from the source Mach-O file.
bool split_module =
!options.dsymPath.empty() && !options.srcPath.empty() && options.cfi;
const string& primary_file =
split_module ? options.dsymPath : options.srcPath;
if (!dump_symbols.Read(primary_file))
return false;
if (options.arch &&
!SetArchitecture(dump_symbols, *options.arch, primary_file)) {
return false;
}
if (options.header_only)
return dump_symbols.WriteSymbolFileHeader(std::cout);
// Read the primary file into a Breakpad Module.
Module* module = NULL;
if (!dump_symbols.ReadSymbolData(&module))
return false;
scoped_ptr<Module> scoped_module(module);
// If this is a split module, read the secondary Mach-O file, from which the
// CFI data will be extracted.
if (split_module && primary_file == options.dsymPath) {
if (!dump_symbols.Read(options.srcPath))
return false;
if (options.arch &&
!SetArchitecture(dump_symbols, *options.arch, options.srcPath)) {
return false;
}
Module* cfi_module = NULL;
if (!dump_symbols.ReadSymbolData(&cfi_module))
return false;
scoped_ptr<Module> scoped_cfi_module(cfi_module);
bool name_matches;
if (!options.module_name.empty()) {
// Ignore the basename of the dSYM and binary and use the passed-in module
// name.
name_matches = true;
} else {
name_matches = cfi_module->name() == module->name();
}
// Ensure that the modules are for the same debug code file.
if (!name_matches || cfi_module->os() != module->os() ||
cfi_module->architecture() != module->architecture() ||
cfi_module->identifier() != module->identifier()) {
fprintf(stderr, "Cannot generate a symbol file from split sources that do"
" not match.\n");
if (!name_matches) {
fprintf(stderr, "Name mismatch: binary=[%s], dSYM=[%s]\n",
cfi_module->name().c_str(), module->name().c_str());
}
if (cfi_module->os() != module->os()) {
fprintf(stderr, "OS mismatch: binary=[%s], dSYM=[%s]\n",
cfi_module->os().c_str(), module->os().c_str());
}
if (cfi_module->architecture() != module->architecture()) {
fprintf(stderr, "Architecture mismatch: binary=[%s], dSYM=[%s]\n",
cfi_module->architecture().c_str(),
module->architecture().c_str());
}
if (cfi_module->identifier() != module->identifier()) {
fprintf(stderr, "Identifier mismatch: binary=[%s], dSYM=[%s]\n",
cfi_module->identifier().c_str(), module->identifier().c_str());
}
return false;
}
CopyCFIDataBetweenModules(module, cfi_module);
}
return module->Write(std::cout, symbol_data);
}
//=============================================================================
static void Usage(int argc, const char *argv[]) {
fprintf(stderr, "Output a Breakpad symbol file from a Mach-o file.\n");
fprintf(stderr,
"Usage: %s [-a ARCHITECTURE] [-c] [-g dSYM path] "
"[-n MODULE] [-x] <Mach-o file>\n",
argv[0]);
fprintf(stderr, "\t-i: Output module header information only.\n");
fprintf(stderr, "\t-a: Architecture type [default: native, or whatever is\n");
fprintf(stderr, "\t in the file, if it contains only one architecture]\n");
fprintf(stderr, "\t-g: Debug symbol file (dSYM) to dump in addition to the "
"Mach-o file\n");
fprintf(stderr, "\t-c: Do not generate CFI section\n");
fprintf(stderr, "\t-r: Do not handle inter-compilation unit references\n");
fprintf(stderr, "\t-d: Generate INLINE and INLINE_ORIGIN records\n");
fprintf(stderr,
"\t-m: Enable writing the optional 'm' field on FUNC "
"and PUBLIC, denoting multiple symbols for the address.\n");
fprintf(stderr,
"\t-n: Use MODULE as the name of the module rather than \n"
"the basename of the Mach-O file/dSYM.\n");
fprintf(stderr,
"\t-x: Prefer the PUBLIC (extern) name over the FUNC if\n"
"they do not match.\n");
fprintf(stderr, "\t-h: Usage\n");
fprintf(stderr, "\t-?: Usage\n");
}
//=============================================================================
static void SetupOptions(int argc, const char *argv[], Options *options) {
extern int optind;
signed char ch;
while ((ch = getopt(argc, (char* const*)argv, "ia:g:crdm?hn:x")) != -1) {
switch (ch) {
case 'i':
options->header_only = true;
break;
case 'a': {
std::optional<ArchInfo> arch_info = GetArchInfoFromName(optarg);
if (!arch_info) {
fprintf(stderr, "%s: Invalid architecture: %s\n", argv[0], optarg);
Usage(argc, argv);
exit(1);
}
options->arch = arch_info;
break;
}
case 'g':
options->dsymPath = optarg;
break;
case 'c':
options->cfi = false;
break;
case 'r':
options->handle_inter_cu_refs = false;
break;
case 'd':
options->handle_inlines = true;
break;
case 'm':
options->enable_multiple = true;
break;
case 'n':
options->module_name = optarg;
break;
case 'x':
options->prefer_extern_name = true;
break;
case '?':
case 'h':
Usage(argc, argv);
exit(0);
break;
}
}
if ((argc - optind) != 1) {
fprintf(stderr, "Must specify Mach-o file\n");
Usage(argc, argv);
exit(1);
}
options->srcPath = argv[optind];
}
//=============================================================================
int main (int argc, const char * argv[]) {
Options options;
bool result;
SetupOptions(argc, argv, &options);
result = Start(options);
return !result;
}

View file

@ -0,0 +1,474 @@
// Copyright 2006 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.
// symupload.mm: Upload a symbol file to a HTTP server. The upload is sent as
// a multipart/form-data POST request with the following parameters:
// code_file: the basename of the module, e.g. "app"
// debug_file: the basename of the debugging file, e.g. "app"
// debug_identifier: the debug file's identifier, usually consisting of
// the guid and age embedded in the pdb, e.g.
// "11111111BBBB3333DDDD555555555555F"
// os: the operating system that the module was built for
// cpu: the CPU that the module was built for (x86 or ppc)
// symbol_file: the contents of the breakpad-format symbol file
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <Foundation/Foundation.h>
#include "HTTPMultipartUpload.h"
#include "HTTPPutRequest.h"
#include "SymbolCollectorClient.h"
#include "common/mac/dump_syms.h"
using google_breakpad::DumpSymbols;
NSString* const kBreakpadSymbolType = @"BREAKPAD";
NSString* const kMachOSymbolType = @"MACHO";
NSString* const kDSYMSymbolType = @"DSYM";
typedef enum { kSymUploadProtocolV1, kSymUploadProtocolV2 } SymUploadProtocol;
typedef enum {
kResultSuccess = 0,
kResultFailure = 1,
kResultAlreadyExists = 2
} Result;
typedef struct {
NSString* symbolsPath;
NSString* uploadURLStr;
SymUploadProtocol symUploadProtocol;
NSString* apiKey;
BOOL force;
Result result;
NSString* type;
NSString* codeFile;
NSString* debugID;
NSString* productName;
} Options;
//=============================================================================
static NSArray* ModuleDataForSymbolFile(NSString* file) {
NSFileHandle* fh = [NSFileHandle fileHandleForReadingAtPath:file];
NSData* data = [fh readDataOfLength:1024];
NSString* str = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSScanner* scanner = [NSScanner scannerWithString:str];
NSString* line;
NSMutableArray* parts = nil;
const int MODULE_ID_INDEX = 3;
if ([scanner scanUpToString:@"\n" intoString:&line]) {
parts = [[NSMutableArray alloc] init];
NSScanner* moduleInfoScanner = [NSScanner scannerWithString:line];
NSString* moduleInfo;
// Get everything BEFORE the module name. None of these properties
// can have spaces.
for (int i = 0; i <= MODULE_ID_INDEX; i++) {
[moduleInfoScanner scanUpToString:@" " intoString:&moduleInfo];
[parts addObject:moduleInfo];
}
// Now get the module name. This can have a space so we scan to
// the end of the line.
[moduleInfoScanner scanUpToString:@"\n" intoString:&moduleInfo];
[parts addObject:moduleInfo];
}
[str release];
return parts;
}
//=============================================================================
static void StartSymUploadProtocolV1(Options* options,
NSString* OS,
NSString* CPU,
NSString* debugID,
NSString* debugFile) {
NSURL* url = [NSURL URLWithString:options->uploadURLStr];
HTTPMultipartUpload* ul = [[HTTPMultipartUpload alloc] initWithURL:url];
NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
// Add parameters
[parameters setObject:debugID forKey:@"debug_identifier"];
[parameters setObject:OS forKey:@"os"];
[parameters setObject:CPU forKey:@"cpu"];
[parameters setObject:debugFile forKey:@"debug_file"];
[parameters setObject:debugFile forKey:@"code_file"];
[ul setParameters:parameters];
NSArray* keys = [parameters allKeys];
int count = [keys count];
for (int i = 0; i < count; ++i) {
NSString* key = [keys objectAtIndex:i];
NSString* value = [parameters objectForKey:key];
fprintf(stdout, "'%s' = '%s'\n", [key UTF8String], [value UTF8String]);
}
// Add file
[ul addFileAtPath:options->symbolsPath name:@"symbol_file"];
// Send it
NSError* error = nil;
NSData* data = [ul send:&error];
NSString* result = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
int status = [[ul response] statusCode];
fprintf(stdout, "Send: %s\n",
error ? [[error description] UTF8String] : "No Error");
fprintf(stdout, "Response: %d\n", status);
fprintf(stdout, "Result: %lu bytes\n%s\n", (unsigned long)[data length],
[result UTF8String]);
[result release];
[ul release];
options->result = (!error && status == 200) ? kResultSuccess : kResultFailure;
}
//=============================================================================
static void StartSymUploadProtocolV2(Options* options,
NSString* debugID,
NSString* debugFile) {
options->result = kResultFailure;
// Only check status of BREAKPAD symbols, because the v2 protocol doesn't
// (yet) have a way to check status of other symbol types.
if (!options->force && [options->type isEqualToString:kBreakpadSymbolType]) {
SymbolStatus symbolStatus =
[SymbolCollectorClient checkSymbolStatusOnServer:options->uploadURLStr
withAPIKey:options->apiKey
withDebugFile:debugFile
withDebugID:debugID];
if (symbolStatus == SymbolStatusFound) {
fprintf(stdout, "Symbol file already exists, upload aborted."
" Use \"-f\" to overwrite.\n");
options->result = kResultAlreadyExists;
return;
} else if (symbolStatus == SymbolStatusUnknown) {
fprintf(stdout, "Failed to get check for existing symbol.\n");
return;
}
}
UploadURLResponse* URLResponse =
[SymbolCollectorClient createUploadURLOnServer:options->uploadURLStr
withAPIKey:options->apiKey];
if (URLResponse == nil) {
return;
}
NSURL* uploadURL = [NSURL URLWithString:[URLResponse uploadURL]];
HTTPPutRequest* putRequest = [[HTTPPutRequest alloc] initWithURL:uploadURL];
[putRequest setFile:options->symbolsPath];
NSError* error = nil;
NSData* data = [putRequest send:&error];
NSString* result = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
int responseCode = [[putRequest response] statusCode];
[putRequest release];
if (error || responseCode != 200) {
fprintf(stdout, "Failed to upload symbol file.\n");
fprintf(stdout, "Response code: %d\n", responseCode);
fprintf(stdout, "Response:\n");
fprintf(stdout, "%s\n", [result UTF8String]);
return;
}
CompleteUploadResult completeUploadResult =
[SymbolCollectorClient completeUploadOnServer:options->uploadURLStr
withAPIKey:options->apiKey
withUploadKey:[URLResponse uploadKey]
withDebugFile:debugFile
withDebugID:debugID
withType:options->type
withProductName:options->productName];
[URLResponse release];
if (completeUploadResult == CompleteUploadResultError) {
fprintf(stdout, "Failed to complete upload.\n");
return;
} else if (completeUploadResult == CompleteUploadResultDuplicateData) {
fprintf(stdout, "Uploaded file checksum matched existing file checksum,"
" no change necessary.\n");
} else {
fprintf(stdout, "Successfully sent the symbol file.\n");
}
options->result = kResultSuccess;
}
//=============================================================================
static void Start(Options* options) {
// If non-BREAKPAD upload special-case.
if (![options->type isEqualToString:kBreakpadSymbolType]) {
StartSymUploadProtocolV2(options, options->debugID, options->codeFile);
return;
}
NSArray* moduleParts = ModuleDataForSymbolFile(options->symbolsPath);
// MODULE <os> <cpu> <uuid> <module-name>
// 0 1 2 3 4
NSString* OS = [moduleParts objectAtIndex:1];
NSString* CPU = [moduleParts objectAtIndex:2];
NSMutableString* debugID =
[NSMutableString stringWithString:[moduleParts objectAtIndex:3]];
[debugID replaceOccurrencesOfString:@"-"
withString:@""
options:0
range:NSMakeRange(0, [debugID length])];
NSString* debugFile = [moduleParts objectAtIndex:4];
if (options->symUploadProtocol == kSymUploadProtocolV1) {
StartSymUploadProtocolV1(options, OS, CPU, debugID, debugFile);
} else if (options->symUploadProtocol == kSymUploadProtocolV2) {
StartSymUploadProtocolV2(options, debugID, debugFile);
}
}
//=============================================================================
static void Usage(int argc, const char* argv[]) {
fprintf(stderr, "Submit symbol information.\n");
fprintf(stderr, "Usage: %s [options] <symbol-file> <upload-URL>\n", argv[0]);
fprintf(stderr, "<symbol-file> should be created by using the dump_syms "
"tool.\n");
fprintf(stderr, "<upload-URL> is the destination for the upload.\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, "\t-p <protocol>: protocol to use for upload, accepts "
"[\"sym-upload-v1\", \"sym-upload-v2\"]. Default is "
"\"sym-upload-v1\".\n");
fprintf(stderr, "\t-k <api-key>: secret for authentication with upload "
"server. [Only in sym-upload-v2 protocol mode]\n");
fprintf(stderr, "\t-f: Overwrite symbol file on server if already present. "
"[Only in sym-upload-v2 protocol mode]\n");
fprintf(
stderr,
"\t-t: <symbol-type> Explicitly set symbol upload type ("
"default is 'breakpad').\n"
"\t One of ['breakpad', 'elf', 'pe', 'macho', 'debug_only', 'dwp', "
"'dsym', 'pdb'].\n"
"\t Note: When this flag is set to anything other than 'breakpad', then "
"the '-c' and '-i' flags must also be set.\n");
fprintf(stderr, "\t-c: <code-file> Explicitly set 'code_file' for symbol "
"upload (basename of executable).\n");
fprintf(stderr, "\t-i: <debug-id> Explicitly set 'debug_id' for symbol "
"upload (typically build ID of executable). The debug-id for "
"symbol-types 'dsym' and 'macho' will be determined "
"automatically. \n");
fprintf(stderr, "\t-n: <product-name> Optionally set 'product_name' for "
"symbol upload\n");
fprintf(stderr, "\t-h: Usage\n");
fprintf(stderr, "\t-?: Usage\n");
fprintf(stderr, "\n");
fprintf(stderr, "Exit codes:\n");
fprintf(stderr, "\t%d: Success\n", kResultSuccess);
fprintf(stderr, "\t%d: Failure\n", kResultFailure);
fprintf(stderr,
"\t%d: Symbol file already exists on server (and -f was not "
"specified).\n",
kResultAlreadyExists);
fprintf(stderr,
"\t [This exit code will only be returned by the sym-upload-v2 "
"protocol.\n");
fprintf(stderr,
"\t The sym-upload-v1 protocol can return either Success or "
"Failure\n");
fprintf(stderr, "\t in this case, and the action taken by the server is "
"unspecified.]\n");
fprintf(stderr, "\n");
fprintf(stderr, "Examples:\n");
fprintf(stderr, " With 'sym-upload-v1':\n");
fprintf(stderr, " %s path/to/symbol_file http://myuploadserver\n",
argv[0]);
fprintf(stderr, " With 'sym-upload-v2':\n");
fprintf(stderr, " [Defaulting to symbol type 'BREAKPAD']\n");
fprintf(stderr,
" %s -p sym-upload-v2 -k mysecret123! "
"path/to/symbol_file http://myuploadserver\n",
argv[0]);
fprintf(stderr, " [Explicitly set symbol type to 'macho']\n");
fprintf(stderr,
" %s -p sym-upload-v2 -k mysecret123! -t macho "
"-c app -i 11111111BBBB3333DDDD555555555555F "
"path/to/symbol_file http://myuploadserver\n",
argv[0]);
}
//=============================================================================
static void SetupOptions(int argc, const char* argv[], Options* options) {
// Set default options values.
options->symUploadProtocol = kSymUploadProtocolV1;
options->apiKey = nil;
options->type = kBreakpadSymbolType;
options->codeFile = nil;
options->debugID = nil;
options->force = NO;
options->productName = nil;
extern int optind;
int ch;
while ((ch = getopt(argc, (char* const*)argv, "p:k:t:c:i:n:hf?")) != -1) {
switch (ch) {
case 'p':
if (strcmp(optarg, "sym-upload-v2") == 0) {
options->symUploadProtocol = kSymUploadProtocolV2;
break;
} else if (strcmp(optarg, "sym-upload-v1") == 0) {
// This is already the default but leave in case that changes.
options->symUploadProtocol = kSymUploadProtocolV1;
break;
}
Usage(argc, argv);
exit(0);
break;
case 'k':
options->apiKey = [NSString stringWithCString:optarg
encoding:NSASCIIStringEncoding];
break;
case 't': {
// This is really an enum, so treat as upper-case for consistency with
// enum naming convention on server-side.
options->type = [[NSString stringWithCString:optarg
encoding:NSASCIIStringEncoding]
uppercaseString];
break;
}
case 'c':
options->codeFile = [NSString stringWithCString:optarg
encoding:NSASCIIStringEncoding];
break;
case 'i':
options->debugID = [NSString stringWithCString:optarg
encoding:NSASCIIStringEncoding];
break;
case 'n':
options->productName =
[NSString stringWithCString:optarg
encoding:NSASCIIStringEncoding];
break;
case 'f':
options->force = YES;
break;
default:
Usage(argc, argv);
exit(0);
break;
}
}
if ((argc - optind) != 2) {
fprintf(stderr, "%s: Missing symbols file and/or upload-URL\n", argv[0]);
Usage(argc, argv);
exit(1);
}
int fd = open(argv[optind], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "%s: %s: %s\n", argv[0], argv[optind], strerror(errno));
exit(1);
}
struct stat statbuf;
if (fstat(fd, &statbuf) < 0) {
fprintf(stderr, "%s: %s: %s\n", argv[0], argv[optind], strerror(errno));
close(fd);
exit(1);
}
close(fd);
if (!S_ISREG(statbuf.st_mode)) {
fprintf(stderr, "%s: %s: not a regular file\n", argv[0], argv[optind]);
exit(1);
}
bool isBreakpadUpload = [options->type isEqualToString:kBreakpadSymbolType];
bool hasCodeFile = options->codeFile != nil;
bool hasDebugID = options->debugID != nil;
if (isBreakpadUpload && (hasCodeFile || hasDebugID)) {
fprintf(stderr, "\n");
fprintf(stderr,
"%s: -c and -i should only be specified for non-breakpad "
"symbol upload types.\n",
argv[0]);
fprintf(stderr, "\n");
Usage(argc, argv);
exit(1);
}
if (!isBreakpadUpload && hasCodeFile && !hasDebugID &&
([options->type isEqualToString:kMachOSymbolType] ||
[options->type isEqualToString:kDSYMSymbolType])) {
DumpSymbols dump_symbols(SYMBOLS_AND_FILES | INLINES, false);
if (dump_symbols.Read(argv[optind])) {
std::string identifier = dump_symbols.Identifier();
if (identifier.empty()) {
fprintf(stderr, "\n");
fprintf(stderr,
"%s: Unable to determine debug-id. Please specify with '-i'.\n",
argv[0]);
fprintf(stderr, "\n");
Usage(argc, argv);
exit(1);
}
options->debugID = [NSString stringWithUTF8String:identifier.c_str()];
hasDebugID = true;
}
}
if (!isBreakpadUpload && (!hasCodeFile || !hasDebugID)) {
fprintf(stderr, "\n");
fprintf(stderr,
"%s: -c and -i must be specified for non-breakpad "
"symbol upload types.\n",
argv[0]);
fprintf(stderr, "\n");
Usage(argc, argv);
exit(1);
}
options->symbolsPath = [NSString stringWithUTF8String:argv[optind]];
options->uploadURLStr = [NSString stringWithUTF8String:argv[optind + 1]];
}
//=============================================================================
int main(int argc, const char* argv[]) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
Options options;
bzero(&options, sizeof(Options));
SetupOptions(argc, argv, &options);
Start(&options);
[pool release];
return options.result;
}

View file

@ -0,0 +1,466 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 45;
objects = {
/* Begin PBXBuildFile section */
5B6060BD222716FC0015F0A0 /* HTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B6060BC222716FC0015F0A0 /* HTTPRequest.m */; };
5B6060C02227201B0015F0A0 /* HTTPPutRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B6060BF2227201B0015F0A0 /* HTTPPutRequest.m */; };
5B6060C7222735E50015F0A0 /* HTTPGetRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B6060C6222735E50015F0A0 /* HTTPGetRequest.m */; };
5B6060CA2227374E0015F0A0 /* HTTPSimplePostRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B6060C92227374E0015F0A0 /* HTTPSimplePostRequest.m */; };
5B6060D022273BDA0015F0A0 /* SymbolCollectorClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B6060CF22273BDA0015F0A0 /* SymbolCollectorClient.m */; };
5B97447524D0AA5F000C71F5 /* encoding_util.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B97447424D0AA5F000C71F5 /* encoding_util.m */; };
8B31022C11F0CEBD00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
8DD76F9A0486AA7600D96B5E /* symupload.mm in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* symupload.mm */; settings = {ATTRIBUTES = (); }; };
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
930DA19225ED543A008558E3 /* dump_syms.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA19025ED543A008558E3 /* dump_syms.cc */; };
930DA22C25ED55A9008558E3 /* module.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA21F25ED55A8008558E3 /* module.cc */; };
930DA22D25ED55A9008558E3 /* dwarf_cfi_to_module.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA22325ED55A8008558E3 /* dwarf_cfi_to_module.cc */; };
930DA22E25ED55A9008558E3 /* stabs_to_module.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA22525ED55A8008558E3 /* stabs_to_module.cc */; };
930DA22F25ED55A9008558E3 /* language.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA22625ED55A9008558E3 /* language.cc */; };
930DA23125ED55A9008558E3 /* dwarf_line_to_module.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA22A25ED55A9008558E3 /* dwarf_line_to_module.cc */; };
930DA23225ED55A9008558E3 /* dwarf_cu_to_module.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA22B25ED55A9008558E3 /* dwarf_cu_to_module.cc */; };
930DA23725ED55B6008558E3 /* stabs_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA23625ED55B6008558E3 /* stabs_reader.cc */; };
930DA24225ED55BF008558E3 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA23A25ED55BF008558E3 /* macho_id.cc */; };
930DA24325ED55BF008558E3 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA23C25ED55BF008558E3 /* macho_utilities.cc */; };
930DA24425ED55BF008558E3 /* macho_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA23F25ED55BF008558E3 /* macho_reader.cc */; };
930DA24525ED55BF008558E3 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA24125ED55BF008558E3 /* macho_walker.cc */; };
930DA25C25ED56DB008558E3 /* dwarf_range_list_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA24D25ED56DB008558E3 /* dwarf_range_list_handler.cc */; };
930DA25D25ED56DB008558E3 /* cfi_assembler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA24E25ED56DB008558E3 /* cfi_assembler.cc */; };
930DA25E25ED56DB008558E3 /* elf_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA25225ED56DB008558E3 /* elf_reader.cc */; };
930DA25F25ED56DB008558E3 /* dwarf2diehandler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA25325ED56DB008558E3 /* dwarf2diehandler.cc */; };
930DA26025ED56DB008558E3 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA25625ED56DB008558E3 /* dwarf2reader.cc */; };
930DA26125ED56DB008558E3 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA25925ED56DB008558E3 /* bytereader.cc */; };
930DA26925ED56FF008558E3 /* test_assembler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA26825ED56FF008558E3 /* test_assembler.cc */; };
930DA26E25ED571F008558E3 /* arch_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA26D25ED571F008558E3 /* arch_utilities.cc */; };
930DA27825ED572D008558E3 /* path_helper.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA27125ED572C008558E3 /* path_helper.cc */; };
930DA27925ED572D008558E3 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA27525ED572C008558E3 /* file_id.cc */; };
930DA27A25ED572D008558E3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 930DA27725ED572D008558E3 /* md5.cc */; };
9BC1D49E0B37427A00F2A2B4 /* minidump_upload.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BD836000B0544BA0055103E /* minidump_upload.m */; };
9BD8336A0B03E4080055103E /* HTTPMultipartUpload.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD833680B03E4080055103E /* HTTPMultipartUpload.h */; };
9BD8336B0B03E4080055103E /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BD833690B03E4080055103E /* HTTPMultipartUpload.m */; };
9BD836180B0549F70055103E /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BD833690B03E4080055103E /* HTTPMultipartUpload.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
8DD76F9E0486AA7600D96B5E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
9BD8336A0B03E4080055103E /* HTTPMultipartUpload.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
08FB7796FE84155DC02AAC07 /* symupload.mm */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.objcpp; path = symupload.mm; sourceTree = "<group>"; tabWidth = 2; };
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
5B6060BB222716FC0015F0A0 /* HTTPRequest.h */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = HTTPRequest.h; path = ../../../common/mac/HTTPRequest.h; sourceTree = "<group>"; tabWidth = 2; };
5B6060BC222716FC0015F0A0 /* HTTPRequest.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; name = HTTPRequest.m; path = ../../../common/mac/HTTPRequest.m; sourceTree = "<group>"; tabWidth = 2; };
5B6060BE2227201B0015F0A0 /* HTTPPutRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HTTPPutRequest.h; path = ../../../common/mac/HTTPPutRequest.h; sourceTree = "<group>"; };
5B6060BF2227201B0015F0A0 /* HTTPPutRequest.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; name = HTTPPutRequest.m; path = ../../../common/mac/HTTPPutRequest.m; sourceTree = "<group>"; tabWidth = 2; };
5B6060C22227303A0015F0A0 /* encoding_util.h */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = encoding_util.h; path = ../../../common/mac/encoding_util.h; sourceTree = "<group>"; tabWidth = 2; };
5B6060C5222735E50015F0A0 /* HTTPGetRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HTTPGetRequest.h; path = ../../../common/mac/HTTPGetRequest.h; sourceTree = "<group>"; };
5B6060C6222735E50015F0A0 /* HTTPGetRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = HTTPGetRequest.m; path = ../../../common/mac/HTTPGetRequest.m; sourceTree = "<group>"; };
5B6060C82227374E0015F0A0 /* HTTPSimplePostRequest.h */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = HTTPSimplePostRequest.h; path = ../../../common/mac/HTTPSimplePostRequest.h; sourceTree = "<group>"; tabWidth = 2; };
5B6060C92227374E0015F0A0 /* HTTPSimplePostRequest.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; name = HTTPSimplePostRequest.m; path = ../../../common/mac/HTTPSimplePostRequest.m; sourceTree = "<group>"; tabWidth = 2; };
5B6060CE22273BDA0015F0A0 /* SymbolCollectorClient.h */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = SymbolCollectorClient.h; path = ../../../common/mac/SymbolCollectorClient.h; sourceTree = "<group>"; tabWidth = 2; };
5B6060CF22273BDA0015F0A0 /* SymbolCollectorClient.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; name = SymbolCollectorClient.m; path = ../../../common/mac/SymbolCollectorClient.m; sourceTree = "<group>"; tabWidth = 2; };
5B97447424D0AA5F000C71F5 /* encoding_util.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = encoding_util.m; path = ../../../common/mac/encoding_util.m; sourceTree = "<group>"; };
8B31022B11F0CE6900FCF3E4 /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; };
8B3102B611F0D5CE00FCF3E4 /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; };
8B3102B711F0D5CE00FCF3E4 /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; };
8DD76FA10486AA7600D96B5E /* symupload */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = symupload; sourceTree = BUILT_PRODUCTS_DIR; };
930DA19025ED543A008558E3 /* dump_syms.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dump_syms.cc; path = ../../../common/mac/dump_syms.cc; sourceTree = "<group>"; };
930DA19125ED543A008558E3 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = "<group>"; };
930DA21F25ED55A8008558E3 /* module.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = module.cc; path = ../../../common/module.cc; sourceTree = "<group>"; };
930DA22025ED55A8008558E3 /* module.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = module.h; path = ../../../common/module.h; sourceTree = "<group>"; };
930DA22125ED55A8008558E3 /* dwarf_cfi_to_module.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf_cfi_to_module.h; path = ../../../common/dwarf_cfi_to_module.h; sourceTree = "<group>"; };
930DA22225ED55A8008558E3 /* dwarf_cu_to_module.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf_cu_to_module.h; path = ../../../common/dwarf_cu_to_module.h; sourceTree = "<group>"; };
930DA22325ED55A8008558E3 /* dwarf_cfi_to_module.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf_cfi_to_module.cc; path = ../../../common/dwarf_cfi_to_module.cc; sourceTree = "<group>"; };
930DA22425ED55A8008558E3 /* language.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = language.h; path = ../../../common/language.h; sourceTree = "<group>"; };
930DA22525ED55A8008558E3 /* stabs_to_module.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = stabs_to_module.cc; path = ../../../common/stabs_to_module.cc; sourceTree = "<group>"; };
930DA22625ED55A9008558E3 /* language.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = language.cc; path = ../../../common/language.cc; sourceTree = "<group>"; };
930DA22725ED55A9008558E3 /* dwarf_line_to_module.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf_line_to_module.h; path = ../../../common/dwarf_line_to_module.h; sourceTree = "<group>"; };
930DA22825ED55A9008558E3 /* stabs_to_module.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stabs_to_module.h; path = ../../../common/stabs_to_module.h; sourceTree = "<group>"; };
930DA22A25ED55A9008558E3 /* dwarf_line_to_module.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf_line_to_module.cc; path = ../../../common/dwarf_line_to_module.cc; sourceTree = "<group>"; };
930DA22B25ED55A9008558E3 /* dwarf_cu_to_module.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf_cu_to_module.cc; path = ../../../common/dwarf_cu_to_module.cc; sourceTree = "<group>"; };
930DA23525ED55B6008558E3 /* stabs_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stabs_reader.h; path = ../../../common/stabs_reader.h; sourceTree = "<group>"; };
930DA23625ED55B6008558E3 /* stabs_reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = stabs_reader.cc; path = ../../../common/stabs_reader.cc; sourceTree = "<group>"; };
930DA23A25ED55BF008558E3 /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../../common/mac/macho_id.cc; sourceTree = "<group>"; };
930DA23B25ED55BF008558E3 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = "<group>"; };
930DA23C25ED55BF008558E3 /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../../common/mac/macho_utilities.cc; sourceTree = "<group>"; };
930DA23D25ED55BF008558E3 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = "<group>"; };
930DA23E25ED55BF008558E3 /* macho_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_reader.h; path = ../../../common/mac/macho_reader.h; sourceTree = "<group>"; };
930DA23F25ED55BF008558E3 /* macho_reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_reader.cc; path = ../../../common/mac/macho_reader.cc; sourceTree = "<group>"; };
930DA24025ED55BF008558E3 /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../../common/mac/macho_utilities.h; sourceTree = "<group>"; };
930DA24125ED55BF008558E3 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = "<group>"; };
930DA24C25ED56DB008558E3 /* line_state_machine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = line_state_machine.h; path = ../../../common/dwarf/line_state_machine.h; sourceTree = "<group>"; };
930DA24D25ED56DB008558E3 /* dwarf_range_list_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf_range_list_handler.cc; path = ../../../common/dwarf_range_list_handler.cc; sourceTree = "<group>"; };
930DA24E25ED56DB008558E3 /* cfi_assembler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cfi_assembler.cc; path = ../../../common/dwarf/cfi_assembler.cc; sourceTree = "<group>"; };
930DA24F25ED56DB008558E3 /* dwarf2enums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf2enums.h; path = ../../../common/dwarf/dwarf2enums.h; sourceTree = "<group>"; };
930DA25025ED56DB008558E3 /* bytereader-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "bytereader-inl.h"; path = "../../../common/dwarf/bytereader-inl.h"; sourceTree = "<group>"; };
930DA25125ED56DB008558E3 /* dwarf_range_list_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf_range_list_handler.h; path = ../../../common/dwarf_range_list_handler.h; sourceTree = "<group>"; };
930DA25225ED56DB008558E3 /* elf_reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = elf_reader.cc; path = ../../../common/dwarf/elf_reader.cc; sourceTree = "<group>"; };
930DA25325ED56DB008558E3 /* dwarf2diehandler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2diehandler.cc; path = ../../../common/dwarf/dwarf2diehandler.cc; sourceTree = "<group>"; };
930DA25425ED56DB008558E3 /* elf_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = elf_reader.h; path = ../../../common/dwarf/elf_reader.h; sourceTree = "<group>"; };
930DA25525ED56DB008558E3 /* bytereader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bytereader.h; path = ../../../common/dwarf/bytereader.h; sourceTree = "<group>"; };
930DA25625ED56DB008558E3 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = "<group>"; };
930DA25725ED56DB008558E3 /* dwarf2reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf2reader.h; path = ../../../common/dwarf/dwarf2reader.h; sourceTree = "<group>"; };
930DA25825ED56DB008558E3 /* dwarf2diehandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf2diehandler.h; path = ../../../common/dwarf/dwarf2diehandler.h; sourceTree = "<group>"; };
930DA25925ED56DB008558E3 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = "<group>"; };
930DA25A25ED56DB008558E3 /* cfi_assembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cfi_assembler.h; path = ../../../common/dwarf/cfi_assembler.h; sourceTree = "<group>"; };
930DA26725ED56FF008558E3 /* test_assembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = test_assembler.h; path = ../../../common/test_assembler.h; sourceTree = "<group>"; };
930DA26825ED56FF008558E3 /* test_assembler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = test_assembler.cc; path = ../../../common/test_assembler.cc; sourceTree = "<group>"; };
930DA26C25ED571F008558E3 /* arch_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = arch_utilities.h; path = ../../../common/mac/arch_utilities.h; sourceTree = "<group>"; };
930DA26D25ED571F008558E3 /* arch_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = arch_utilities.cc; path = ../../../common/mac/arch_utilities.cc; sourceTree = "<group>"; };
930DA27125ED572C008558E3 /* path_helper.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = path_helper.cc; path = ../../../common/path_helper.cc; sourceTree = "<group>"; };
930DA27225ED572C008558E3 /* byteswap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = byteswap.h; path = ../../../common/mac/byteswap.h; sourceTree = "<group>"; };
930DA27325ED572C008558E3 /* path_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = path_helper.h; path = ../../../common/path_helper.h; sourceTree = "<group>"; };
930DA27425ED572C008558E3 /* byte_cursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = byte_cursor.h; path = ../../../common/byte_cursor.h; sourceTree = "<group>"; };
930DA27525ED572C008558E3 /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../../common/mac/file_id.cc; sourceTree = "<group>"; };
930DA27625ED572C008558E3 /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../../common/mac/file_id.h; sourceTree = "<group>"; };
930DA27725ED572D008558E3 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = md5.cc; path = ../../../common/md5.cc; sourceTree = "<group>"; };
9BD833680B03E4080055103E /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = HTTPMultipartUpload.h; path = ../../../common/mac/HTTPMultipartUpload.h; sourceTree = "<group>"; tabWidth = 2; };
9BD833690B03E4080055103E /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; name = HTTPMultipartUpload.m; path = ../../../common/mac/HTTPMultipartUpload.m; sourceTree = "<group>"; tabWidth = 2; };
9BD835FB0B0544950055103E /* minidump_upload */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_upload; sourceTree = BUILT_PRODUCTS_DIR; };
9BD836000B0544BA0055103E /* minidump_upload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = minidump_upload.m; path = ../../../common/mac/minidump_upload.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8DD76F9B0486AA7600D96B5E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9BD835F90B0544950055103E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8B31022C11F0CEBD00FCF3E4 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
08FB7794FE84155DC02AAC07 /* symupload */ = {
isa = PBXGroup;
children = (
930DA21E25ED5586008558E3 /* dump_syms */,
5B6060CE22273BDA0015F0A0 /* SymbolCollectorClient.h */,
5B6060CF22273BDA0015F0A0 /* SymbolCollectorClient.m */,
5B6060C82227374E0015F0A0 /* HTTPSimplePostRequest.h */,
5B6060C92227374E0015F0A0 /* HTTPSimplePostRequest.m */,
5B6060C5222735E50015F0A0 /* HTTPGetRequest.h */,
5B6060C6222735E50015F0A0 /* HTTPGetRequest.m */,
5B6060C22227303A0015F0A0 /* encoding_util.h */,
5B97447424D0AA5F000C71F5 /* encoding_util.m */,
5B6060BE2227201B0015F0A0 /* HTTPPutRequest.h */,
5B6060BF2227201B0015F0A0 /* HTTPPutRequest.m */,
5B6060BB222716FC0015F0A0 /* HTTPRequest.h */,
5B6060BC222716FC0015F0A0 /* HTTPRequest.m */,
8B31022B11F0CE6900FCF3E4 /* Breakpad.xcconfig */,
8B3102B611F0D5CE00FCF3E4 /* BreakpadDebug.xcconfig */,
8B3102B711F0D5CE00FCF3E4 /* BreakpadRelease.xcconfig */,
08FB7796FE84155DC02AAC07 /* symupload.mm */,
9BD836000B0544BA0055103E /* minidump_upload.m */,
9BD833680B03E4080055103E /* HTTPMultipartUpload.h */,
9BD833690B03E4080055103E /* HTTPMultipartUpload.m */,
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
1AB674ADFE9D54B511CA2CBB /* Products */,
);
name = symupload;
sourceTree = "<group>";
};
08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = {
isa = PBXGroup;
children = (
08FB779EFE84155DC02AAC07 /* Foundation.framework */,
);
name = "External Frameworks and Libraries";
sourceTree = "<group>";
};
1AB674ADFE9D54B511CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
8DD76FA10486AA7600D96B5E /* symupload */,
9BD835FB0B0544950055103E /* minidump_upload */,
);
name = Products;
sourceTree = "<group>";
};
930DA21E25ED5586008558E3 /* dump_syms */ = {
isa = PBXGroup;
children = (
930DA23A25ED55BF008558E3 /* macho_id.cc */,
930DA23B25ED55BF008558E3 /* macho_id.h */,
930DA23F25ED55BF008558E3 /* macho_reader.cc */,
930DA23E25ED55BF008558E3 /* macho_reader.h */,
930DA23C25ED55BF008558E3 /* macho_utilities.cc */,
930DA24025ED55BF008558E3 /* macho_utilities.h */,
930DA24125ED55BF008558E3 /* macho_walker.cc */,
930DA23D25ED55BF008558E3 /* macho_walker.h */,
930DA19025ED543A008558E3 /* dump_syms.cc */,
930DA19125ED543A008558E3 /* dump_syms.h */,
930DA23625ED55B6008558E3 /* stabs_reader.cc */,
930DA23525ED55B6008558E3 /* stabs_reader.h */,
930DA22325ED55A8008558E3 /* dwarf_cfi_to_module.cc */,
930DA22125ED55A8008558E3 /* dwarf_cfi_to_module.h */,
930DA22B25ED55A9008558E3 /* dwarf_cu_to_module.cc */,
930DA22225ED55A8008558E3 /* dwarf_cu_to_module.h */,
930DA22A25ED55A9008558E3 /* dwarf_line_to_module.cc */,
930DA22725ED55A9008558E3 /* dwarf_line_to_module.h */,
930DA22625ED55A9008558E3 /* language.cc */,
930DA22425ED55A8008558E3 /* language.h */,
930DA21F25ED55A8008558E3 /* module.cc */,
930DA22025ED55A8008558E3 /* module.h */,
930DA22525ED55A8008558E3 /* stabs_to_module.cc */,
930DA22825ED55A9008558E3 /* stabs_to_module.h */,
930DA25025ED56DB008558E3 /* bytereader-inl.h */,
930DA25925ED56DB008558E3 /* bytereader.cc */,
930DA25525ED56DB008558E3 /* bytereader.h */,
930DA24E25ED56DB008558E3 /* cfi_assembler.cc */,
930DA25A25ED56DB008558E3 /* cfi_assembler.h */,
930DA24D25ED56DB008558E3 /* dwarf_range_list_handler.cc */,
930DA25125ED56DB008558E3 /* dwarf_range_list_handler.h */,
930DA25325ED56DB008558E3 /* dwarf2diehandler.cc */,
930DA25825ED56DB008558E3 /* dwarf2diehandler.h */,
930DA24F25ED56DB008558E3 /* dwarf2enums.h */,
930DA25625ED56DB008558E3 /* dwarf2reader.cc */,
930DA25725ED56DB008558E3 /* dwarf2reader.h */,
930DA25225ED56DB008558E3 /* elf_reader.cc */,
930DA25425ED56DB008558E3 /* elf_reader.h */,
930DA24C25ED56DB008558E3 /* line_state_machine.h */,
930DA26825ED56FF008558E3 /* test_assembler.cc */,
930DA26725ED56FF008558E3 /* test_assembler.h */,
930DA26D25ED571F008558E3 /* arch_utilities.cc */,
930DA26C25ED571F008558E3 /* arch_utilities.h */,
930DA27425ED572C008558E3 /* byte_cursor.h */,
930DA27225ED572C008558E3 /* byteswap.h */,
930DA27525ED572C008558E3 /* file_id.cc */,
930DA27625ED572C008558E3 /* file_id.h */,
930DA27725ED572D008558E3 /* md5.cc */,
930DA27125ED572C008558E3 /* path_helper.cc */,
930DA27325ED572C008558E3 /* path_helper.h */,
);
name = dump_syms;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
8DD76F960486AA7600D96B5E /* symupload */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "symupload" */;
buildPhases = (
8DD76F990486AA7600D96B5E /* Sources */,
8DD76F9B0486AA7600D96B5E /* Frameworks */,
8DD76F9E0486AA7600D96B5E /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = symupload;
productInstallPath = "$(HOME)/bin";
productName = symupload;
productReference = 8DD76FA10486AA7600D96B5E /* symupload */;
productType = "com.apple.product-type.tool";
};
9BD835FA0B0544950055103E /* minidump_upload */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9BD836020B0544BB0055103E /* Build configuration list for PBXNativeTarget "minidump_upload" */;
buildPhases = (
9BD835F80B0544950055103E /* Sources */,
9BD835F90B0544950055103E /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = minidump_upload;
productName = minidump_upload;
productReference = 9BD835FB0B0544950055103E /* minidump_upload */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
};
buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "symupload" */;
compatibilityVersion = "Xcode 3.1";
developmentRegion = en;
hasScannedForEncodings = 1;
knownRegions = (
en,
);
mainGroup = 08FB7794FE84155DC02AAC07 /* symupload */;
projectDirPath = "";
projectRoot = "";
targets = (
8DD76F960486AA7600D96B5E /* symupload */,
9BD835FA0B0544950055103E /* minidump_upload */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
8DD76F990486AA7600D96B5E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8DD76F9A0486AA7600D96B5E /* symupload.mm in Sources */,
930DA19225ED543A008558E3 /* dump_syms.cc in Sources */,
930DA24525ED55BF008558E3 /* macho_walker.cc in Sources */,
930DA22E25ED55A9008558E3 /* stabs_to_module.cc in Sources */,
5B6060CA2227374E0015F0A0 /* HTTPSimplePostRequest.m in Sources */,
930DA25F25ED56DB008558E3 /* dwarf2diehandler.cc in Sources */,
930DA27825ED572D008558E3 /* path_helper.cc in Sources */,
930DA27A25ED572D008558E3 /* md5.cc in Sources */,
930DA22D25ED55A9008558E3 /* dwarf_cfi_to_module.cc in Sources */,
930DA24425ED55BF008558E3 /* macho_reader.cc in Sources */,
930DA24325ED55BF008558E3 /* macho_utilities.cc in Sources */,
5B6060D022273BDA0015F0A0 /* SymbolCollectorClient.m in Sources */,
5B6060C7222735E50015F0A0 /* HTTPGetRequest.m in Sources */,
930DA27925ED572D008558E3 /* file_id.cc in Sources */,
930DA26925ED56FF008558E3 /* test_assembler.cc in Sources */,
930DA22F25ED55A9008558E3 /* language.cc in Sources */,
930DA25E25ED56DB008558E3 /* elf_reader.cc in Sources */,
930DA26E25ED571F008558E3 /* arch_utilities.cc in Sources */,
930DA24225ED55BF008558E3 /* macho_id.cc in Sources */,
5B6060C02227201B0015F0A0 /* HTTPPutRequest.m in Sources */,
930DA25C25ED56DB008558E3 /* dwarf_range_list_handler.cc in Sources */,
5B6060BD222716FC0015F0A0 /* HTTPRequest.m in Sources */,
930DA25D25ED56DB008558E3 /* cfi_assembler.cc in Sources */,
930DA23225ED55A9008558E3 /* dwarf_cu_to_module.cc in Sources */,
930DA23125ED55A9008558E3 /* dwarf_line_to_module.cc in Sources */,
930DA26125ED56DB008558E3 /* bytereader.cc in Sources */,
930DA22C25ED55A9008558E3 /* module.cc in Sources */,
5B97447524D0AA5F000C71F5 /* encoding_util.m in Sources */,
930DA23725ED55B6008558E3 /* stabs_reader.cc in Sources */,
9BD8336B0B03E4080055103E /* HTTPMultipartUpload.m in Sources */,
930DA26025ED56DB008558E3 /* dwarf2reader.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9BD835F80B0544950055103E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9BD836180B0549F70055103E /* HTTPMultipartUpload.m in Sources */,
9BC1D49E0B37427A00F2A2B4 /* minidump_upload.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1DEB927508733DD40010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
GCC_PREPROCESSOR_DEFINITIONS = HAVE_MACH_O_NLIST_H;
HEADER_SEARCH_PATHS = (
../../..,
../../../common/mac/include,
../../../third_party/musl/include/,
);
PRODUCT_NAME = symupload;
};
name = Debug;
};
1DEB927608733DD40010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
NDEBUG,
HAVE_MACH_O_NLIST_H,
);
HEADER_SEARCH_PATHS = (
../../..,
../../../common/mac/include,
../../../third_party/musl/include/,
);
PRODUCT_NAME = symupload;
};
name = Release;
};
1DEB927908733DD40010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8B3102B611F0D5CE00FCF3E4 /* BreakpadDebug.xcconfig */;
buildSettings = {
};
name = Debug;
};
1DEB927A08733DD40010E9CD /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8B3102B711F0D5CE00FCF3E4 /* BreakpadRelease.xcconfig */;
buildSettings = {
};
name = Release;
};
9BD836030B0544BB0055103E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = ../../..;
PRODUCT_NAME = minidump_upload;
};
name = Debug;
};
9BD836040B0544BB0055103E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = ../../..;
PRODUCT_NAME = minidump_upload;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "symupload" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB927508733DD40010E9CD /* Debug */,
1DEB927608733DD40010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "symupload" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB927908733DD40010E9CD /* Debug */,
1DEB927A08733DD40010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9BD836020B0544BB0055103E /* Build configuration list for PBXNativeTarget "minidump_upload" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9BD836030B0544BB0055103E /* Debug */,
9BD836040B0544BB0055103E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
}

View file

@ -0,0 +1,66 @@
/* Copyright 2014 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 <mach-o/arch.h>
#include <mach-o/loader.h>
#include <mach/machine.h>
// Go/Cgo does not support #define constants, so turn them into symbols
// that are reachable from Go.
#ifndef CPU_TYPE_ARM64
#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
#endif
#ifndef CPU_SUBTYPE_ARM64_ALL
#define CPU_SUBTYPE_ARM64_ALL 0
#endif
#ifndef CPU_SUBTYPE_ARM64_E
#define CPU_SUBTYPE_ARM64_E 2
#endif
const cpu_type_t kCPU_TYPE_ARM = CPU_TYPE_ARM;
const cpu_type_t kCPU_TYPE_ARM64 = CPU_TYPE_ARM64;
const cpu_subtype_t kCPU_SUBTYPE_ARM64_ALL = CPU_SUBTYPE_ARM64_ALL;
const cpu_subtype_t kCPU_SUBTYPE_ARM64_E = CPU_SUBTYPE_ARM64_E;
const cpu_subtype_t kCPU_SUBTYPE_ARM_V7S = CPU_SUBTYPE_ARM_V7S;
const char* GetNXArchInfoName(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
const NXArchInfo* arch_info = NXGetArchInfoFromCpuType(cpu_type, cpu_subtype);
if (!arch_info)
return 0;
return arch_info->name;
}
const uint32_t kMachHeaderFtypeDylib = MH_DYLIB;
const uint32_t kMachHeaderFtypeBundle = MH_BUNDLE;
const uint32_t kMachHeaderFtypeExe = MH_EXECUTE;
const uint32_t kMachHeaderFtypeDylinker = MH_DYLINKER;

View file

@ -0,0 +1,68 @@
/* Copyright 2014 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.
*/
package main
import (
"debug/macho"
)
/*
#include "arch_constants.h"
*/
import "C"
// getArchStringFromHeader takes a MachO FileHeader and returns a string that
// represents the CPU type and subtype.
// This function is a Go version of src/common/mac/arch_utilities.cc:BreakpadGetArchInfoFromCpuType().
func getArchStringFromHeader(header macho.FileHeader) string {
// TODO(rsesek): As of 10.9.4, OS X doesn't list these in /usr/include/mach/machine.h.
if header.Cpu == C.kCPU_TYPE_ARM64 && header.SubCpu == C.kCPU_SUBTYPE_ARM64_ALL {
return "arm64"
}
if header.Cpu == C.kCPU_TYPE_ARM64 && header.SubCpu == C.kCPU_SUBTYPE_ARM64_E {
return "arm64e"
}
if header.Cpu == C.kCPU_TYPE_ARM && header.SubCpu == C.kCPU_SUBTYPE_ARM_V7S {
return "armv7s"
}
cstr := C.GetNXArchInfoName(C.cpu_type_t(header.Cpu), C.cpu_subtype_t(header.SubCpu))
if cstr == nil {
return ""
}
return C.GoString(cstr)
}
const (
MachODylib macho.Type = C.kMachHeaderFtypeDylib
MachOBundle = C.kMachHeaderFtypeBundle
MachOExe = C.kMachHeaderFtypeExe
MachODylinker = C.kMachHeaderFtypeDylinker
)

View file

@ -0,0 +1,3 @@
module upload_system_symbols
go 1.17

View file

@ -0,0 +1,484 @@
/* Copyright 2014 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.
*/
/*
Tool upload_system_symbols generates and uploads Breakpad symbol files for OS X system libraries.
This tool shells out to the dump_syms and symupload Breakpad tools. In its default mode, this
will find all dynamic libraries on the system, run dump_syms to create the Breakpad symbol files,
and then upload them to Google's crash infrastructure.
The tool can also be used to only dump libraries or upload from a directory. See -help for more
information.
Both i386 and x86_64 architectures will be dumped and uploaded.
*/
package main
import (
"debug/macho"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"regexp"
"strings"
"sync"
"time"
)
var (
breakpadTools = flag.String("breakpad-tools", "out/Release/", "Path to the Breakpad tools directory, containing dump_syms and symupload.")
uploadOnlyPath = flag.String("upload-from", "", "Upload a directory of symbol files that has been dumped independently.")
dumpOnlyPath = flag.String("dump-to", "", "Dump the symbols to the specified directory, but do not upload them.")
systemRoot = flag.String("system-root", "", "Path to the root of the Mac OS X system whose symbols will be dumped.")
dumpArchitecture = flag.String("arch", "", "The CPU architecture for which symbols should be dumped. If not specified, dumps all architectures.")
apiKey = flag.String("api-key", "", "API key to use. If this is present, the `sym-upload-v2` protocol is used.\nSee https://chromium.googlesource.com/breakpad/breakpad/+/HEAD/docs/sym_upload_v2_protocol.md or\n`symupload`'s help for more information.")
)
var (
// pathsToScan are the subpaths in the systemRoot that should be scanned for shared libraries.
pathsToScan = []string{
"/System/Library/Frameworks",
"/System/Library/PrivateFrameworks",
"/usr/lib",
}
// optionalPathsToScan is just like pathsToScan, but the paths are permitted to be absent.
optionalPathsToScan = []string{
// Gone in 10.15.
"/Library/QuickTime",
// Not present in dumped dyld_shared_caches
"/System/Library/Components",
}
// uploadServersV1 are the list of servers to which symbols should be
// uploaded when using the V1 protocol.
uploadServersV1 = []string{
"https://clients2.google.com/cr/symbol",
"https://clients2.google.com/cr/staging_symbol",
}
// uploadServersV2 are the list of servers to which symbols should be
// uploaded when using the V2 protocol.
uploadServersV2 = []string{
"https://staging-crashsymbolcollector-pa.googleapis.com",
"https://prod-crashsymbolcollector-pa.googleapis.com",
}
// uploadServers are the list of servers that should be used, accounting
// for whether v1 or v2 protocol is used.
uploadServers = uploadServersV1
// blacklistRegexps match paths that should be excluded from dumping.
blacklistRegexps = []*regexp.Regexp{
regexp.MustCompile(`/System/Library/Frameworks/Python\.framework/`),
regexp.MustCompile(`/System/Library/Frameworks/Ruby\.framework/`),
regexp.MustCompile(`_profile\.dylib$`),
regexp.MustCompile(`_debug\.dylib$`),
regexp.MustCompile(`\.a$`),
regexp.MustCompile(`\.dat$`),
}
)
func main() {
flag.Parse()
log.SetFlags(0)
// If `apiKey` is set, we're using the v2 protocol.
if len(*apiKey) > 0 {
uploadServers = uploadServersV2
}
var uq *UploadQueue
if *uploadOnlyPath != "" {
// -upload-from specified, so handle that case early.
uq = StartUploadQueue()
uploadFromDirectory(*uploadOnlyPath, uq)
uq.Wait()
return
}
if *systemRoot == "" {
log.Fatal("Need a -system-root to dump symbols for")
}
if *dumpOnlyPath != "" {
// -dump-to specified, so make sure that the path is a directory.
if fi, err := os.Stat(*dumpOnlyPath); err != nil {
log.Fatalf("-dump-to location: %v", err)
} else if !fi.IsDir() {
log.Fatal("-dump-to location is not a directory")
}
}
dumpPath := *dumpOnlyPath
if *dumpOnlyPath == "" {
// If -dump-to was not specified, then run the upload pipeline and create
// a temporary dump output directory.
uq = StartUploadQueue()
if p, err := ioutil.TempDir("", "upload_system_symbols"); err != nil {
log.Fatalf("Failed to create temporary directory: %v", err)
} else {
dumpPath = p
defer os.RemoveAll(p)
}
}
dq := StartDumpQueue(*systemRoot, dumpPath, uq)
dq.Wait()
if uq != nil {
uq.Wait()
}
}
// manglePath reduces an absolute filesystem path to a string suitable as the
// base for a file name which encodes some of the original path. The result
// concatenates the leading initial from each path component except the last to
// the last path component; for example /System/Library/Frameworks/AppKit
// becomes SLFAppKit.
// Assumes ASCII.
func manglePath(path string) string {
components := strings.Split(path, "/")
n := len(components)
builder := strings.Builder{}
for i, component := range components {
if len(component) == 0 {
continue
}
if i < n-1 {
builder.WriteString(component[:1])
} else {
builder.WriteString(component)
}
}
return builder.String()
}
type WorkerPool struct {
wg sync.WaitGroup
}
// StartWorkerPool will launch numWorkers goroutines all running workerFunc.
// When workerFunc exits, the goroutine will terminate.
func StartWorkerPool(numWorkers int, workerFunc func()) *WorkerPool {
p := new(WorkerPool)
for i := 0; i < numWorkers; i++ {
p.wg.Add(1)
go func() {
workerFunc()
p.wg.Done()
}()
}
return p
}
// Wait for all the workers in the pool to complete the workerFunc.
func (p *WorkerPool) Wait() {
p.wg.Wait()
}
type UploadQueue struct {
*WorkerPool
queue chan string
}
// StartUploadQueue creates a new worker pool and queue, to which paths to
// Breakpad symbol files may be sent for uploading.
func StartUploadQueue() *UploadQueue {
uq := &UploadQueue{
queue: make(chan string, 10),
}
uq.WorkerPool = StartWorkerPool(5, uq.worker)
return uq
}
// Upload enqueues the contents of filepath to be uploaded.
func (uq *UploadQueue) Upload(filepath string) {
uq.queue <- filepath
}
// Done tells the queue that no more files need to be uploaded. This must be
// called before WorkerPool.Wait.
func (uq *UploadQueue) Done() {
close(uq.queue)
}
func (uq *UploadQueue) runSymUpload(symfile, server string) *exec.Cmd {
symUpload := path.Join(*breakpadTools, "symupload")
args := []string{symfile, server}
if len(*apiKey) > 0 {
args = append([]string{"-p", "sym-upload-v2", "-k", *apiKey}, args...)
}
return exec.Command(symUpload, args...)
}
func (uq *UploadQueue) worker() {
for symfile := range uq.queue {
for _, server := range uploadServers {
for i := 0; i < 3; i++ { // Give each upload 3 attempts to succeed.
cmd := uq.runSymUpload(symfile, server)
if output, err := cmd.Output(); err == nil {
// Success. No retry needed.
fmt.Printf("Uploaded %s to %s\n", symfile, server)
break
} else if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 2 && *apiKey != "" {
// Exit code 2 in protocol v2 means the file already exists on the server.
// No point retrying.
fmt.Printf("File %s already exists on %s\n", symfile, server)
break
} else {
log.Printf("Error running symupload(%s, %s), attempt %d: %v: %s\n", symfile, server, i, err, output)
time.Sleep(1 * time.Second)
}
}
}
}
}
type DumpQueue struct {
*WorkerPool
dumpPath string
queue chan dumpRequest
uq *UploadQueue
}
type dumpRequest struct {
path string
arch string
}
// StartDumpQueue creates a new worker pool to find all the Mach-O libraries in
// root and dump their symbols to dumpPath. If an UploadQueue is passed, the
// path to the symbol file will be enqueued there, too.
func StartDumpQueue(root, dumpPath string, uq *UploadQueue) *DumpQueue {
dq := &DumpQueue{
dumpPath: dumpPath,
queue: make(chan dumpRequest),
uq: uq,
}
dq.WorkerPool = StartWorkerPool(12, dq.worker)
findLibsInRoot(root, dq)
return dq
}
// DumpSymbols enqueues the filepath to have its symbols dumped in the specified
// architecture.
func (dq *DumpQueue) DumpSymbols(filepath string, arch string) {
dq.queue <- dumpRequest{
path: filepath,
arch: arch,
}
}
func (dq *DumpQueue) Wait() {
dq.WorkerPool.Wait()
if dq.uq != nil {
dq.uq.Done()
}
}
func (dq *DumpQueue) done() {
close(dq.queue)
}
func (dq *DumpQueue) worker() {
dumpSyms := path.Join(*breakpadTools, "dump_syms")
for req := range dq.queue {
filebase := path.Join(dq.dumpPath, manglePath(req.path))
symfile := fmt.Sprintf("%s_%s.sym", filebase, req.arch)
f, err := os.Create(symfile)
if err != nil {
log.Fatalf("Error creating symbol file: %v", err)
}
cmd := exec.Command(dumpSyms, "-a", req.arch, req.path)
cmd.Stdout = f
err = cmd.Run()
f.Close()
if err != nil {
os.Remove(symfile)
log.Printf("Error running dump_syms(%s, %s): %v\n", req.arch, req.path, err)
} else if dq.uq != nil {
dq.uq.Upload(symfile)
}
}
}
// uploadFromDirectory handles the upload-only case and merely uploads all files in
// a directory.
func uploadFromDirectory(directory string, uq *UploadQueue) {
d, err := os.Open(directory)
if err != nil {
log.Fatalf("Could not open directory to upload: %v", err)
}
defer d.Close()
entries, err := d.Readdirnames(0)
if err != nil {
log.Fatalf("Could not read directory: %v", err)
}
for _, entry := range entries {
uq.Upload(path.Join(directory, entry))
}
uq.Done()
}
// findQueue is an implementation detail of the DumpQueue that finds all the
// Mach-O files and their architectures.
type findQueue struct {
*WorkerPool
queue chan string
dq *DumpQueue
}
// findLibsInRoot looks in all the pathsToScan in the root and manages the
// interaction between findQueue and DumpQueue.
func findLibsInRoot(root string, dq *DumpQueue) {
fq := &findQueue{
queue: make(chan string, 10),
dq: dq,
}
fq.WorkerPool = StartWorkerPool(12, fq.worker)
for _, p := range pathsToScan {
fq.findLibsInPath(path.Join(root, p), true)
}
for _, p := range optionalPathsToScan {
fq.findLibsInPath(path.Join(root, p), false)
}
close(fq.queue)
fq.Wait()
dq.done()
}
// findLibsInPath recursively walks the directory tree, sending file paths to
// test for being Mach-O to the findQueue.
func (fq *findQueue) findLibsInPath(loc string, mustExist bool) {
d, err := os.Open(loc)
if err != nil {
if !mustExist && os.IsNotExist(err) {
return
}
log.Fatalf("Could not open %s: %v", loc, err)
}
defer d.Close()
for {
fis, err := d.Readdir(100)
if err != nil && err != io.EOF {
log.Fatalf("Error reading directory %s: %v", loc, err)
}
for _, fi := range fis {
fp := path.Join(loc, fi.Name())
if fi.IsDir() {
fq.findLibsInPath(fp, true)
continue
} else if fi.Mode()&os.ModeSymlink != 0 {
continue
}
// Test the blacklist in the worker to not slow down this main loop.
fq.queue <- fp
}
if err == io.EOF {
break
}
}
}
func (fq *findQueue) worker() {
for fp := range fq.queue {
blacklisted := false
for _, re := range blacklistRegexps {
blacklisted = blacklisted || re.MatchString(fp)
}
if blacklisted {
continue
}
f, err := os.Open(fp)
if err != nil {
log.Printf("%s: %v", fp, err)
continue
}
fatFile, err := macho.NewFatFile(f)
if err == nil {
// The file is fat, so dump its architectures.
for _, fatArch := range fatFile.Arches {
fq.dumpMachOFile(fp, fatArch.File)
}
fatFile.Close()
} else if err == macho.ErrNotFat {
// The file isn't fat but may still be MachO.
thinFile, err := macho.NewFile(f)
if err != nil {
log.Printf("%s: %v", fp, err)
continue
}
fq.dumpMachOFile(fp, thinFile)
thinFile.Close()
} else {
f.Close()
}
}
}
func (fq *findQueue) dumpMachOFile(fp string, image *macho.File) {
if image.Type != MachODylib && image.Type != MachOBundle && image.Type != MachODylinker {
return
}
arch := getArchStringFromHeader(image.FileHeader)
if arch == "" {
// Don't know about this architecture type.
return
}
if (*dumpArchitecture != "" && *dumpArchitecture == arch) || *dumpArchitecture == "" {
fq.dq.DumpSymbols(fp, arch)
}
}

View file

@ -0,0 +1,114 @@
#!/bin/bash
# Copyright 2023 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.
# Finds the dyld_shared_cache on a system, extracts it, and dumps the symbols
# in Breakpad format to the directory passed as the first argument
# The script must be in the same directory as `dump_syms`,
# `upload_system_symbols` and `dsc_extractor` binaries.
# Exits with 0 if all supported architectures for this OS version were found and
# dumped, and nonzero otherwise.
set -ex
if [[ $# -ne 1 ]]; then
echo "usage: $0 <destination_directory>" >& 2
exit 1
fi
destination_dir="$1"
dir="$(dirname "$0")"
dir="$(cd "${dir}"; pwd)"
major_version=$(sw_vers -productVersion | cut -d . -f 1)
if [[ "${major_version}" -lt 13 ]]; then
dsc_directory="/System/Library/dyld"
else
dsc_directory="/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld"
fi
working_dir=$(mktemp -d)
mkdir "${destination_dir}"
trap 'rm -rf "${working_dir}" "${destination_dir}"' EXIT
architectures=(x86_64h)
missing_architectures=()
# macOS >= 13 on arm64 still has a x86_64 cache for Rosetta.
if [[ "${major_version}" -lt 13 ]] || [[ $(uname -p) == "arm" ]]; then
architectures+=( x86_64 )
fi
if [[ "${major_version}" -ge 11 ]]; then
architectures+=( arm64e )
fi
for arch in "${architectures[@]}"; do
cache="${dsc_directory}/dyld_shared_cache_${arch}"
if [[ ! -f "${cache}" ]]; then
missing_architectures+=("${arch}")
continue
fi
"${dir}/dsc_extractor" \
"${cache}" \
"${working_dir}/${arch}"
"${dir}/upload_system_symbols" \
--breakpad-tools="${dir}" \
--system-root="${working_dir}/${arch}" \
--dump-to="${destination_dir}"
done
if [[ "${#missing_architectures[@]}" -eq "${#architectures[@]}" ]]; then
echo "Couldn't locate dyld_shared_cache for any architectures" >& 2
echo "in ${dsc_directory}. Exiting." >& 2
exit 1
fi
rm -rf "${working_dir}"
# We have results now, so let's keep `destination_dir`.
trap '' EXIT
"${dir}/upload_system_symbols" \
--breakpad-tools="${dir}" \
--system-root=/ \
--dump-to="${destination_dir}"
set +x
echo
echo "Dumped!"
echo "To upload, run:"
echo
echo "'${dir}/upload_system_symbols'" \\
echo " --breakpad-tools='${dir}'" \\
echo " --api-key=<YOUR API KEY>" \\
echo " --upload-from='${destination_dir}'"
if [[ "${#missing_architectures[@]}" -gt 0 ]]; then
echo "dyld_shared_cache not found for architecture(s):" >& 2
echo " " "${missing_architectures[@]}" >& 2
echo "You'll need to get symbols for them elsewhere." >& 2
exit 1
fi

View file

@ -0,0 +1,165 @@
#!/usr/bin/env python3
# Copyright 2016 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.
"""Convert gclient's DEPS file to repo's manifest xml file."""
import argparse
import os
import sys
REMOTES = {
'chromium': 'https://chromium.googlesource.com/',
'github': 'https://github.com/',
}
REVIEWS = {
'chromium': 'https://chromium-review.googlesource.com',
}
MANIFEST_HEAD = """<?xml version='1.0' encoding='UTF-8'?>
<!-- AUTOGENERATED BY %(prog)s; DO NOT EDIT -->
<manifest>
<default revision='refs/heads/main'
remote='chromium'
sync-c='true'
sync-j='8' />
"""
MANIFEST_REMOTE = """
<remote name='%(name)s'
fetch='%(fetch)s'
review='%(review)s' />
"""
MANIFEST_PROJECT = """
<project path='%(path)s'
name='%(name)s'
revision='%(revision)s'
remote='%(remote)s' />
"""
MANIFEST_TAIL = """
</manifest>
"""
def ConvertDepsToManifest(deps, manifest):
"""Convert the |deps| file to the |manifest|."""
# Load the DEPS file data.
ctx = {}
with open(deps, 'rb') as file:
exec(compile(file.read(), deps, 'exec'), ctx)
new_contents = ''
# Write out the common header.
data = {
'prog': os.path.basename(__file__),
}
new_contents += MANIFEST_HEAD % data
# Write out the <remote> sections.
for name, fetch in REMOTES.items():
data = {
'name': name,
'fetch': fetch,
'review': REVIEWS.get(name, ''),
}
new_contents += MANIFEST_REMOTE % data
# Write out the main repo itself.
data = {
'path': 'src',
'name': 'breakpad/breakpad',
'revision': 'refs/heads/main',
'remote': 'chromium',
}
new_contents += MANIFEST_PROJECT % data
# Write out the <project> sections.
for path, url in ctx['deps'].items():
for name, fetch in REMOTES.items():
if url.startswith(fetch):
remote = name
break
else:
raise ValueError('Unknown DEPS remote: %s: %s' % (path, url))
# The DEPS url will look like:
# https://chromium.googlesource.com/external/gyp/@e8ab0833a42691cd2
remote_path, rev = url.split('@')
remote_path = remote_path[len(fetch):]
# If it's not a revision, assume it's a tag. Repo wants full ref names.
if len(rev) != 40:
rev = 'refs/tags/%s' % rev
data = {
'path': path,
'name': remote_path,
'revision': rev,
'remote': remote,
}
new_contents += MANIFEST_PROJECT % data
# Write out the common footer.
new_contents += MANIFEST_TAIL
# See if the manifest has actually changed contents to avoid thrashing.
try:
old_contents = open(manifest).read()
except IOError:
# In case the file doesn't exist yet.
old_contents = ''
if old_contents != new_contents:
print('Updating %s due to changed %s' % (manifest, deps))
with open(manifest, 'w') as fp:
fp.write(new_contents)
def GetParser():
"""Return a CLI parser."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('deps',
help='The DEPS file to convert')
parser.add_argument('manifest',
help='The manifest xml to generate')
return parser
def main(argv):
"""The main func!"""
parser = GetParser()
opts = parser.parse_args(argv)
ConvertDepsToManifest(opts.deps, opts.manifest)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View file

@ -0,0 +1,202 @@
#!/usr/bin/env python3
# Copyright 2012 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.
"""Normalizes and de-duplicates paths within Breakpad symbol files.
When using DWARF for storing debug symbols, some file information will be
stored relative to the current working directory of the current compilation
unit, and may be further relativized based upon how the file was #included.
This helper can be used to parse the Breakpad symbol file generated from such
DWARF files and normalize and de-duplicate the FILE records found within,
updating any references to the FILE records in the other record types.
"""
import ntpath
import optparse
import os
import posixpath
import sys
class BreakpadParseError(Exception):
"""Unsupported Breakpad symbol record exception class."""
pass
class SymbolFileParser(object):
"""Parser for Breakpad symbol files.
The format of these files is documented at
https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
"""
def __init__(self, input_stream, output_stream, ignored_prefixes=None,
path_handler=os.path):
"""Inits a SymbolFileParser to read symbol records from |input_stream| and
write the processed output to |output_stream|.
|ignored_prefixes| contains a list of optional path prefixes that
should be stripped from the final, normalized path outputs.
For example, if the Breakpad symbol file had all paths starting with a
common prefix, such as:
FILE 1 /b/build/src/foo.cc
FILE 2 /b/build/src/bar.cc
Then adding "/b/build/src" as an ignored prefix would result in an output
file that contained:
FILE 1 foo.cc
FILE 2 bar.cc
Note that |ignored_prefixes| does not necessarily contain file system
paths, as the contents of the DWARF DW_AT_comp_dir attribute is dependent
upon the host system and compiler, and may contain additional information
such as hostname or compiler version.
"""
self.unique_files = {}
self.duplicate_files = {}
self.input_stream = input_stream
self.output_stream = output_stream
self.ignored_prefixes = ignored_prefixes or []
self.path_handler = path_handler
def Process(self):
"""Processes the Breakpad symbol file."""
for line in self.input_stream:
parsed = self._ParseRecord(line.rstrip())
if parsed:
self.output_stream.write(parsed + '\n')
def _ParseRecord(self, record):
"""Parses a single Breakpad symbol record - a single line from the symbol
file.
Returns:
The modified string to write to the output file, or None if no line
should be written.
"""
record_type = record.partition(' ')[0]
if record_type == 'FILE':
return self._ParseFileRecord(record)
elif self._IsLineRecord(record_type):
return self._ParseLineRecord(record)
else:
# Simply pass the record through unaltered.
return record
def _NormalizePath(self, path):
"""Normalizes a file path to its canonical form.
As this may not execute on the machine or file system originally
responsible for compilation, it may be necessary to further correct paths
for symlinks, junctions, or other such file system indirections.
Returns:
A unique, canonical representation for the the file path.
"""
return self.path_handler.normpath(path)
def _AdjustPath(self, path):
"""Adjusts the supplied path after performing path de-duplication.
This may be used to perform secondary adjustments, such as removing a
common prefix, such as "/D/build", or replacing the file system path with
information from the version control system.
Returns:
The actual path to use when writing the FILE record.
"""
return path[len(next(filter(path.startswith,
self.ignored_prefixes + ['']))):]
def _ParseFileRecord(self, file_record):
"""Parses and corrects a FILE record."""
file_info = file_record[5:].split(' ', 3)
if len(file_info) > 2:
raise BreakpadParseError('Unsupported FILE record: ' + file_record)
file_index = int(file_info[0])
file_name = self._NormalizePath(file_info[1])
existing_file_index = self.unique_files.get(file_name)
if existing_file_index is None:
self.unique_files[file_name] = file_index
file_info[1] = self._AdjustPath(file_name)
return 'FILE ' + ' '.join(file_info)
else:
self.duplicate_files[file_index] = existing_file_index
return None
def _IsLineRecord(self, record_type):
"""Determines if the current record type is a Line record"""
try:
line = int(record_type, 16)
except (ValueError, TypeError):
return False
return True
def _ParseLineRecord(self, line_record):
"""Parses and corrects a Line record."""
line_info = line_record.split(' ', 5)
if len(line_info) > 4:
raise BreakpadParseError('Unsupported Line record: ' + line_record)
file_index = int(line_info[3])
line_info[3] = str(self.duplicate_files.get(file_index, file_index))
return ' '.join(line_info)
def main():
option_parser = optparse.OptionParser()
option_parser.add_option("-p", "--prefix",
action="append", dest="prefixes", type="string",
default=[],
help="A path prefix that should be removed from "
"all FILE lines. May be repeated to specify "
"multiple prefixes.")
option_parser.add_option("-t", "--path_type",
action="store", type="choice", dest="path_handler",
choices=['win32', 'posix'],
help="Indicates how file paths should be "
"interpreted. The default is to treat paths "
"the same as the OS running Python (eg: "
"os.path)")
options, args = option_parser.parse_args()
if args:
option_parser.error('Unknown argument: %s' % args)
path_handler = { 'win32': ntpath,
'posix': posixpath }.get(options.path_handler, os.path)
try:
symbol_parser = SymbolFileParser(sys.stdin, sys.stdout, options.prefixes,
path_handler)
symbol_parser.Process()
except BreakpadParseError as e:
print >> sys.stderr, 'Got an error while processing symbol file'
print >> sys.stderr, str(e)
return 1
return 0
if __name__ == '__main__':
sys.exit(main())

View file

@ -0,0 +1,136 @@
#!/usr/bin/env python3
# Copyright 2012 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.
"""Unit tests for filter_syms.py"""
import io
import ntpath
import os
import sys
import unittest
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(ROOT_DIR, '..'))
# In root
import filter_syms
class FilterSysmsTest(unittest.TestCase):
def assertParsed(self, input_data, ignored_prefixes, expected):
input_io = io.StringIO(input_data)
output_io = io.StringIO()
parser = filter_syms.SymbolFileParser(input_io, output_io,
ignored_prefixes, ntpath)
parser.Process()
self.assertEqual(output_io.getvalue(), expected)
def testDuplicateFiles(self):
"""Tests that duplicate files in FILE records are correctly removed and
that Line records are updated."""
INPUT = \
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
INFO CODE_ID FFFFFFFF module1.exe
FILE 1 foo/../file1_1.cc
FILE 2 bar/../file1_1.cc
FILE 3 baz/../file1_1.cc
FUNC 1000 c 0 Function1_1
1000 8 45 2
1008 4 46 3
100c 4 44 1
"""
EXPECTED_OUTPUT = \
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
INFO CODE_ID FFFFFFFF module1.exe
FILE 1 file1_1.cc
FUNC 1000 c 0 Function1_1
1000 8 45 1
1008 4 46 1
100c 4 44 1
"""
self.assertParsed(INPUT, [], EXPECTED_OUTPUT)
def testIgnoredPrefix(self):
"""Tests that prefixes in FILE records are correctly removed."""
INPUT = \
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
INFO CODE_ID FFFFFFFF module1.exe
FILE 1 /src/build/foo/../file1_1.cc
FILE 2 /src/build/bar/../file1_2.cc
FILE 3 /src/build/baz/../file1_2.cc
FUNC 1000 c 0 Function1_1
1000 8 45 2
1008 4 46 3
100c 4 44 1
"""
EXPECTED_OUTPUT = \
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
INFO CODE_ID FFFFFFFF module1.exe
FILE 1 file1_1.cc
FILE 2 file1_2.cc
FUNC 1000 c 0 Function1_1
1000 8 45 2
1008 4 46 2
100c 4 44 1
"""
IGNORED_PREFIXES = ['\\src\\build\\']
self.assertParsed(INPUT, IGNORED_PREFIXES, EXPECTED_OUTPUT)
def testIgnoredPrefixesDuplicateFiles(self):
"""Tests that de-duplication of FILE records happens BEFORE prefixes
are removed."""
INPUT = \
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
INFO CODE_ID FFFFFFFF module1.exe
FILE 1 /src/build/foo/../file1_1.cc
FILE 2 /src/build/bar/../file1_2.cc
FILE 3 D:/src/build2/baz/../file1_2.cc
FUNC 1000 c 0 Function1_1
1000 8 45 2
1008 4 46 3
100c 4 44 1
"""
EXPECTED_OUTPUT = \
"""MODULE windows x86 111111111111111111111111111111111 module1.pdb
INFO CODE_ID FFFFFFFF module1.exe
FILE 1 file1_1.cc
FILE 2 file1_2.cc
FILE 3 file1_2.cc
FUNC 1000 c 0 Function1_1
1000 8 45 2
1008 4 46 3
100c 4 44 1
"""
IGNORED_PREFIXES = ['\\src\\build\\', 'D:\\src\\build2\\']
self.assertParsed(INPUT, IGNORED_PREFIXES, EXPECTED_OUTPUT)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,63 @@
# Copyright 2007 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.
# Author: Alfred Peng
CXX=CC
CC=cc
CXXFLAGS=-g -xs -xdebugformat=stabs -I../../.. -I../../../common/solaris -lelf -ldemangle -D_REENTRANT
.PHONY:all clean
BIN=dump_syms
all:$(BIN)
DUMP_OBJ=dump_symbols.o guid_creator.o dump_syms.o file_id.o md5.o
dump_syms:$(DUMP_OBJ)
$(CXX) $(CXXFLAGS) -o $@ $^
dump_symbols.o:../../../common/solaris/dump_symbols.cc
$(CXX) $(CXXFLAGS) -c $^
guid_creator.o:../../../common/solaris/guid_creator.cc
$(CXX) $(CXXFLAGS) -c $^
file_id.o:../../../common/solaris/file_id.cc
$(CXX) $(CXXFLAGS) -c $^
md5.o:../../../common/md5.cc
$(CXX) $(CXXFLAGS) -c $^
test:all
./run_regtest.sh
clean:
rm -f $(BIN) $(DUMP_OBJ)

View file

@ -0,0 +1,57 @@
// Copyright 2007 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.
// Author: Alfred Peng
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <string>
#include <cstdio>
#include "common/solaris/dump_symbols.h"
using namespace google_breakpad;
int main(int argc, char** argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <binary-with-stab-symbol>\n", argv[0]);
return 1;
}
const char* binary = argv[1];
DumpSymbols dumper;
if (!dumper.WriteSymbolFile(binary, fileno(stdout))) {
fprintf(stderr, "Failed to write symbol file.\n");
return 1;
}
return 0;
}

View file

@ -0,0 +1,50 @@
#!/bin/sh
# Copyright 2007 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.
./dump_syms testdata/dump_syms_regtest.o > testdata/dump_syms_regtest.new
status=$?
if [ $status -ne 0 ] ; then
echo "FAIL, dump_syms failed"
exit $status
fi
diff -u testdata/dump_syms_regtest.new testdata/dump_syms_regtest.sym > \
testdata/dump_syms_regtest.diff
status=$?
if [ $status -eq 0 ] ; then
rm testdata/dump_syms_regtest.diff testdata/dump_syms_regtest.new
echo "PASS"
else
echo "FAIL, see testdata/dump_syms_regtest.[new|diff]"
fi
exit $status

View file

@ -0,0 +1,67 @@
// Copyright 2007 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.
// ./dump_syms dump_syms_regtest.pdb > dump_syms_regtest.sym
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
namespace google_breakpad {
class C {
public:
C() : member_(1) {}
virtual ~C() {}
void set_member(int value) { member_ = value; }
int member() const { return member_; }
void f() { member_ = g(); }
virtual int g() { return 2; }
static char* h(const C& that) { return 0; }
private:
int member_;
};
static int i() {
return 3;
}
} // namespace google_breakpad
int main(int argc, char** argv) {
google_breakpad::C object;
object.set_member(google_breakpad::i());
object.f();
int value = object.g();
char* nothing = object.h(object);
return 0;
}

View file

@ -0,0 +1,129 @@
Debugging Stab table -- 104 entries
0: .stabs "dump_syms_regtest.cc",N_UNDF,0x0,0x67,0x71c
1: .stabs "/export/home/alfred/cvs/breakpad/google-breakpad20070927/src/tools/solaris/dump_syms/testdata/",N_SO,0x0,0x0,0x0
2: .stabs "dump_syms_regtest.cc",N_SO,0x0,0x4,0x0
3: .stabs "",N_OBJ,0x0,0x0,0x0
4: .stabs "",N_OBJ,0x0,0x0,0x0
5: .stabs "V=9.0;DBG_GEN=5.0.8;dm;cd;backend;ptf;ptx;ptk;s;g;R=5.8<<Sun C++ 5.8 Patch 121018-07 2006/11/01 (ccfe)>>;G=.XAB6Z2hOiL$Gl1b.;A=2",N_OPT,0x0,0x0,0x46fcb88e
6: .stabs "dump_syms_regtest.cc",N_SOL,0x0,0x0,0x0
7: .stabs "char:t(0,1)=bsc1;0;8",N_ISYM,0x0,0x0,0x0
8: .stabs "short:t(0,2)=bs2;0;16",N_ISYM,0x0,0x0,0x0
9: .stabs "int:t(0,3)=bs4;0;32",N_ISYM,0x0,0x0,0x0
10: .stabs "long:t(0,4)=bs4;0;32",N_ISYM,0x0,0x0,0x0
11: .stabs "long long:t(0,5)=bs8;0;64",N_ISYM,0x0,0x0,0x0
12: .stabs "unsigned char:t(0,6)=buc1;0;8",N_ISYM,0x0,0x0,0x0
13: .stabs "unsigned short:t(0,7)=bu2;0;16",N_ISYM,0x0,0x0,0x0
14: .stabs "unsigned:t(0,8)=bu4;0;32",N_ISYM,0x0,0x0,0x0
15: .stabs "unsigned long:t(0,9)=bu4;0;32",N_ISYM,0x0,0x0,0x0
16: .stabs "unsigned long long:t(0,10)=bu8;0;64",N_ISYM,0x0,0x0,0x0
17: .stabs "signed char:t(0,11)=bsc1;0;8",N_ISYM,0x0,0x0,0x0
18: .stabs "wchar_t:t(0,12)=buc4;0;32",N_ISYM,0x0,0x0,0x0
19: .stabs "void:t(0,13)=bs0;0;0",N_ISYM,0x0,0x0,0x0
20: .stabs "float:t(0,14)=R1;4",N_ISYM,0x0,0x0,0x0
21: .stabs "double:t(0,15)=R2;8",N_ISYM,0x0,0x0,0x0
22: .stabs "long double:t(0,16)=R6;12",N_ISYM,0x0,0x0,0x0
23: .stabs "...:t(0,17)=buv4;0;32",N_ISYM,0x0,0x0,0x0
24: .stabs "bool:t(0,18)=bub1;0;8",N_ISYM,0x0,0x0,0x0
25: .stabs "__1nPgoogle_breakpad_:T(0,19)=Yn0google_breakpad;",N_ISYM,0x0,0x0,0x0
26: .stabs "nBC(0,19):U(0,20)",N_ESYM,0x0,0x0,0x0
27: .stabs "nBC(0,19):T(0,20)=Yc8C;;AcHmember_:(0,3),32,32;;Cc2t6M_v K2c2T6M_v CcKset_member6Mi_v CcGmember6kM_i CcBf6M_v K3cBg6M_i GcBh6Frk1_pc;;;2 0;;;;110;",N_ESYM,0x0,0x8,0x0
28: .stabs "main:F(0,3);(0,3);(0,21)=*(0,22)=*(0,1)",N_FUN,0x0,0x38,0x0
29: .stabs "main",N_MAIN,0x0,0x0,0x0
30: .stabs "argc:p(0,3)",N_PSYM,0x0,0x4,0x8
31: .stabs "argv:p(0,21)",N_PSYM,0x0,0x4,0xc
32: .stabn N_LBRAC,0x0,0x1,0x12
33: .stabs "object:(0,20)",N_LSYM,0x0,0x8,0xfffffff4
34: .stabs "value:(0,3)",N_LSYM,0x0,0x4,0xfffffff0
35: .stabs "nothing:(0,22)",N_LSYM,0x0,0x4,0xffffffec
36: .stabn N_SLINE,0x0,0x39,0x12
37: .stabs "object:2",N_CONSTRUCT,0x0,0xc,0x12
38: .stabn N_SLINE,0x2,0x3a,0x1e
39: .stabn N_SLINE,0x0,0x3b,0x36
40: .stabn N_SLINE,0x0,0x3c,0x42
41: .stabn N_SLINE,0x0,0x3d,0x57
42: .stabn N_SLINE,0x0,0x3f,0x6c
43: .stabs "2:0",N_DESTRUCT,0x0,0xc,0x73
44: .stabn N_SLINE,0xfffffffe,0x40,0x9c
45: .stabn N_RBRAC,0x0,0x1,0x9c
46: .stabs "__1cPgoogle_breakpadBi6F_i_:f(0,3)",N_FUN,0x0,0x32,0x0
47: .stabn N_LBRAC,0x0,0x1,0x6
48: .stabn N_SLINE,0x0,0x33,0x6
49: .stabn N_SLINE,0x0,0x34,0x10
50: .stabn N_RBRAC,0x0,0x1,0x10
51: .stabs "__1cPgoogle_breakpadBC2t6M_v_:F(0,13);(0,23)=*(0,20)",N_FUN,0x0,0x24,0x0
52: .stabs "this:p(0,23)",N_PSYM,0x0,0x4,0x8
53: .stabn N_LBRAC,0x0,0x1,0x3
54: .stabn N_SLINE,0x0,0x24,0x25
55: .stabn N_RBRAC,0x0,0x1,0x25
56: .stabs "__1cPgoogle_breakpadBC2T6M_v_:F(0,13);(0,23)",N_FUN,0x0,0x25,0x0
57: .stabs "this:p(0,23)",N_PSYM,0x0,0x4,0x8
58: .stabn N_LBRAC,0x0,0x1,0x3
59: .stabn N_SLINE,0x0,0x25,0x3
60: .stabn N_RBRAC,0x0,0x1,0x3
61: .stabs "__1cPgoogle_breakpadBCKset_member6Mi_v_:F(0,13);(0,23);(0,3)",N_FUN,0x0,0x27,0x0
62: .stabs "this:p(0,23)",N_PSYM,0x0,0x4,0x8
63: .stabs "value:p(0,3)",N_PSYM,0x0,0x4,0xc
64: .stabn N_LBRAC,0x0,0x1,0x3
65: .stabn N_SLINE,0x0,0x27,0x3
66: .stabn N_SLINE,0x0,0x27,0xc
67: .stabn N_RBRAC,0x0,0x1,0xc
68: .stabs "__1cPgoogle_breakpadBCBf6M_v_:F(0,13);(0,23)",N_FUN,0x0,0x2a,0x0
69: .stabs "this:p(0,23)",N_PSYM,0x0,0x4,0x8
70: .stabn N_LBRAC,0x0,0x1,0x3
71: .stabn N_SLINE,0x0,0x2a,0x3
72: .stabn N_SLINE,0x0,0x2a,0x1d
73: .stabn N_RBRAC,0x0,0x1,0x1d
74: .stabs "__1cPgoogle_breakpadBCBg6M_i_:F(0,3);(0,23)",N_FUN,0x0,0x2b,0x0
75: .stabs "this:p(0,23)",N_PSYM,0x0,0x4,0x8
76: .stabn N_LBRAC,0x0,0x1,0x6
77: .stabn N_SLINE,0x0,0x2b,0x6
78: .stabn N_SLINE,0x0,0x2b,0x10
79: .stabn N_RBRAC,0x0,0x1,0x10
80: .stabs "__1cPgoogle_breakpadBCBh6Frk1_pc_:F(0,22);(0,24)=&(0,25)=k(0,20)",N_FUN,0x0,0x2c,0x0
81: .stabs "that:p(0,24)",N_PSYM,0x0,0x4,0x8
82: .stabn N_LBRAC,0x0,0x1,0x6
83: .stabn N_SLINE,0x0,0x2c,0x6
84: .stabn N_SLINE,0x0,0x2c,0x10
85: .stabn N_RBRAC,0x0,0x1,0x10
86: .stabs "__1cPgoogle_breakpadBC2T5B6M_v_:F(0,13);(0,23)",N_FUN,0x0,0x25,0x0
87: .stabs "this:p(0,23)",N_PSYM,0x0,0x4,0x8
88: .stabn N_LBRAC,0x0,0x1,0x3
89: .stabn N_SLINE,0x0,0x25,0xf
90: .stabn N_RBRAC,0x0,0x1,0xf
91: .stabs "__SLIP.DELETER__A:f(0,13);(0,23);(0,3)",N_FUN,0x0,0x25,0x0
92: .stabs "this:p(0,23)",N_PSYM,0x0,0x4,0x8
93: .stabs "delete:p(0,3)",N_PSYM,0x0,0x4,0xc
94: .stabn N_LBRAC,0x0,0x1,0x3
95: .stabn N_LBRAC,0x0,0x2,0x3
96: .stabn N_RBRAC,0x0,0x2,0x28
97: .stabn N_RBRAC,0x0,0x1,0x28
98: .stabs "true:l(0,18);1",N_LSYM,0x0,0x4,0x0
99: .stabs "false:l(0,18);0",N_LSYM,0x0,0x4,0x0
100: .stabs "__1c2k6Fpv_v_:P(0,13);(0,26)=*(0,13)",N_FUN,0x0,0x0,0x0
101: .stabs "__1cPgoogle_breakpadBC2t5B6M_v_:F__1cPgoogle_breakpadBC2t6M_v_",N_ALIAS,0x0,0x0,0x0
102: .stabs "cbD__RTTI__1nPgoogle_breakpadBC_(0,19):YR(0,20)",N_LSYM,0x0,0x0,0x0
103: .stabn N_ENDM,0x0,0x0,0x0
Index Stab table -- 17 entries
0: .stabs "dump_syms_regtest.cc",N_UNDF,0x0,0x10,0x3b1
1: .stabs "/export/home/alfred/cvs/breakpad/google-breakpad20070927/src/tools/solaris/dump_syms/testdata/",N_SO,0x0,0x0,0x0
2: .stabs "dump_syms_regtest.cc",N_SO,0x0,0x4,0x0
3: .stabs "/export/home/alfred/cvs/breakpad/google-breakpad20070927/src/tools/solaris/dump_syms/testdata",N_OBJ,0x0,0x0,0x0
4: .stabs "dump_syms_regtest.o",N_OBJ,0x0,0x0,0x0
5: .stabs "V=9.0;DBG_GEN=5.0.8;dm;cd;backend;ptf;ptx;ptk;s;g;R=5.8<<Sun C++ 5.8 Patch 121018-07 2006/11/01 (ccfe)>>;G=.XAB6Z2hOiL$Gl1b.;A=2",N_OPT,0x0,0x0,0x46fcb88e
6: .stabs "/export/home/alfred/cvs/breakpad/google-breakpad20070927/src/tools/solaris/dump_syms/testdata/; /ws/on10-tools-prc/SUNWspro/SS11/prod/bin/CC -g -xs -xdebugformat=stabs -I../../.. -I../../../common/solaris -D_REENTRANT -xs dump_syms_regtest.cc -Qoption ccfe -prefix -Qoption ccfe .XAB6Z2hOiL\$Gl1b.",N_CMDLINE,0x0,0x0,0x0
7: .stabs "__1nPgoogle_breakpadBC_:U",N_ESYM,0x0,0x0,0x0
8: .stabs "main",N_MAIN,0x0,0x0,0x0
9: .stabs "main",N_FUN,0x0,0x0,0x0
10: .stabs "__1cPgoogle_breakpadBC2t6M_v_",N_FUN,0x0,0x0,0x0
11: .stabs "__1cPgoogle_breakpadBC2T6M_v_",N_FUN,0x0,0x0,0x0
12: .stabs "__1cPgoogle_breakpadBCKset_member6Mi_v_",N_FUN,0x0,0x0,0x0
13: .stabs "__1cPgoogle_breakpadBCBf6M_v_",N_FUN,0x0,0x0,0x0
14: .stabs "__1cPgoogle_breakpadBCBg6M_i_",N_FUN,0x0,0x0,0x0
15: .stabs "__1cPgoogle_breakpadBCBh6Frk1_pc_",N_FUN,0x0,0x0,0x0
16: .stabs "__1cPgoogle_breakpadBC2T5B6M_v_",N_FUN,0x0,0x0,0x0

View file

@ -0,0 +1,33 @@
MODULE solaris x86 3DC8191474338D8587339B5FB3E2C62A0 dump_syms_regtest.o
FILE 0 dump_syms_regtest.cc
FUNC 0 156 0 main
12 18 57 0
1e 12 58 0
36 24 59 0
42 12 60 0
57 21 61 0
6c 21 63 0
9c 48 64 0
FUNC 0 16 0 int google_breakpad::i()
6 6 51 0
10 10 52 0
FUNC 0 37 0 google_breakpad::C::C()
25 37 36 0
FUNC 0 3 0 google_breakpad::C::~C()
3 3 37 0
FUNC 0 12 0 void google_breakpad::C::set_member(int)
3 3 39 0
c 9 39 0
FUNC 0 29 0 void google_breakpad::C::f()
3 3 42 0
1d 26 42 0
FUNC 0 16 0 int google_breakpad::C::g()
6 6 43 0
10 10 43 0
FUNC 0 16 0 char*google_breakpad::C::h(const google_breakpad::C&)
6 6 44 0
10 10 44 0
FUNC 0 15 0 google_breakpad::C::~C #Nvariant 1()
f 15 37 0
FUNC 0 0 0 __SLIP.DELETER__A
FUNC 0 0 0 void operator delete(void*)

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,770 @@
// Copyright 2007 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.
// ms_symbol_server_converter.cc: Obtain symbol files from a Microsoft
// symbol server, and convert them to Breakpad's dumped format.
//
// See ms_symbol_server_converter.h for documentation.
//
// Author: Mark Mentovai
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <windows.h>
#include <dbghelp.h>
#include <pathcch.h>
#include <cassert>
#include <cstdio>
#include "tools/windows/converter/ms_symbol_server_converter.h"
#include "common/windows/pdb_source_line_writer.h"
#include "common/windows/pe_source_line_writer.h"
#include "common/windows/string_utils-inl.h"
// SYMOPT_NO_PROMPTS is not defined in earlier platform SDKs. Define it
// in that case, in the event that this code is used with a newer version
// of DbgHelp at runtime that recognizes the option. The presence of this
// bit in the symbol options should not harm earlier versions of DbgHelp.
#ifndef SYMOPT_NO_PROMPTS
#define SYMOPT_NO_PROMPTS 0x00080000
#endif // SYMOPT_NO_PROMPTS
namespace {
std::wstring GetExeDirectory() {
wchar_t directory[MAX_PATH];
// Get path to this process exe.
DWORD result = GetModuleFileName(/*hModule=*/nullptr, directory, MAX_PATH);
if (result <= 0 || result == MAX_PATH) {
fprintf(stderr,
"GetExeDirectory: failed to get path to process exe.\n");
return L"";
}
HRESULT hr = PathCchRemoveFileSpec(directory, result + 1);
if (hr != S_OK) {
fprintf(stderr,
"GetExeDirectory: failed to remove basename from path '%ls'.\n",
directory);
return L"";
}
return std::wstring(directory);
}
} // namespace
namespace google_breakpad {
// Use sscanf_s if it is available, to quench the warning about scanf being
// deprecated. Use scanf where sscanf_is not available. Note that the
// parameters passed to sscanf and sscanf_s are only compatible as long as
// fields of type c, C, s, S, and [ are not used.
#if _MSC_VER >= 1400 // MSVC 2005/8
#define SSCANF sscanf_s
#else // _MSC_VER >= 1400
#define SSCANF sscanf
#endif // _MSC_VER >= 1400
bool GUIDOrSignatureIdentifier::InitializeFromString(
const string& identifier) {
type_ = TYPE_NONE;
size_t length = identifier.length();
if (length > 32 && length <= 40) {
// GUID
if (SSCANF(identifier.c_str(),
"%08X%04hX%04hX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%X",
&guid_.Data1, &guid_.Data2, &guid_.Data3,
&guid_.Data4[0], &guid_.Data4[1],
&guid_.Data4[2], &guid_.Data4[3],
&guid_.Data4[4], &guid_.Data4[5],
&guid_.Data4[6], &guid_.Data4[7],
&age_) != 12) {
return false;
}
type_ = TYPE_GUID;
} else if (length > 8 && length <= 15) {
// Signature
if (SSCANF(identifier.c_str(), "%08X%x", &signature_, &age_) != 2) {
return false;
}
type_ = TYPE_SIGNATURE;
} else {
return false;
}
return true;
}
#undef SSCANF
MSSymbolServerConverter::MSSymbolServerConverter(
const string& local_cache,
const vector<string>& symbol_servers,
bool trace_symsrv)
: symbol_path_(),
fail_dns_(false),
fail_timeout_(false),
fail_http_https_redir_(false),
fail_not_found_(false),
trace_symsrv_(trace_symsrv) {
// Setting local_cache can be done without verifying that it exists because
// SymSrv will create it if it is missing - any creation failures will occur
// at that time, so there's nothing to check here, making it safe to
// assign this in the constructor.
assert(symbol_servers.size() > 0);
#if !defined(NDEBUG)
// These are characters that are interpreted as having special meanings in
// symbol_path_.
const char kInvalidCharacters[] = "*;";
assert(local_cache.find_first_of(kInvalidCharacters) == string::npos);
#endif // !defined(NDEBUG)
for (vector<string>::const_iterator symbol_server = symbol_servers.begin();
symbol_server != symbol_servers.end();
++symbol_server) {
// The symbol path format is explained by
// http://msdn.microsoft.com/library/en-us/debug/base/using_symsrv.asp .
// "srv*" is the same as "symsrv*symsrv.dll*", which means that
// symsrv.dll is to be responsible for locating symbols. symsrv.dll
// interprets the rest of the string as a series of symbol stores separated
// by '*'. "srv*local_cache*symbol_server" means to check local_cache
// first for the symbol file, and if it is not found there, to check
// symbol_server. Symbol files found on the symbol server will be placed
// in the local cache, decompressed.
//
// Multiple specifications in this format may be presented, separated by
// semicolons.
assert((*symbol_server).find_first_of(kInvalidCharacters) == string::npos);
symbol_path_ += "srv*" + local_cache + "*" + *symbol_server + ";";
}
// Strip the trailing semicolon.
symbol_path_.erase(symbol_path_.length() - 1);
}
// A stack-based class that manages SymInitialize and SymCleanup calls.
class AutoSymSrv {
public:
AutoSymSrv() : initialized_(false) {}
~AutoSymSrv() {
if (!Cleanup()) {
// Print the error message here, because destructors have no return
// value.
fprintf(stderr, "~AutoSymSrv: SymCleanup: error %lu\n", GetLastError());
}
}
bool Initialize(HANDLE process, char* path, bool invade_process) {
process_ = process;
// TODO(nbilling): Figure out why dbghelp.dll is being loaded from
// system32/SysWOW64 before exe folder.
// Attempt to locate and load dbghelp.dll beside the process exe. This is
// somewhat of a workaround to loader delay load behavior that is occurring
// when we call into symsrv APIs. dbghelp.dll must be loaded from beside
// the process exe so that we are guaranteed to find symsrv.dll alongside
// dbghelp.dll (a security requirement of dbghelp.dll) and so that the
// symsrv.dll file that is loaded has a symsrv.yes file alongside it (a
// requirement of symsrv.dll when accessing Microsoft-owned symbol
// servers).
// 'static local' because we don't care about the value but we need the
// initialization to happen exactly once.
static HMODULE dbghelp_module = [] () -> HMODULE {
std::wstring exe_directory = GetExeDirectory();
if (exe_directory.empty()) {
return nullptr;
}
std::wstring dbghelp_path = exe_directory + L"\\dbghelp.dll";
return LoadLibrary(dbghelp_path.c_str());
}();
if (dbghelp_module == nullptr) {
fprintf(stderr,
"AutoSymSrv::Initialize: failed to load dbghelp.dll beside exe.");
return false;
}
initialized_ = SymInitialize(process, path, invade_process) == TRUE;
return initialized_;
}
bool Cleanup() {
if (initialized_) {
if (SymCleanup(process_)) {
initialized_ = false;
return true;
}
return false;
}
return true;
}
private:
HANDLE process_;
bool initialized_;
};
// A stack-based class that "owns" a pathname and deletes it when destroyed,
// unless told not to by having its Release() method called. Early deletions
// are supported by calling Delete().
class AutoDeleter {
public:
explicit AutoDeleter(const string& path) : path_(path) {}
~AutoDeleter() {
int error;
if ((error = Delete()) != 0) {
// Print the error message here, because destructors have no return
// value.
fprintf(stderr, "~AutoDeleter: Delete: error %d for %s\n",
error, path_.c_str());
}
}
int Delete() {
if (path_.empty())
return 0;
int error = remove(path_.c_str());
Release();
return error;
}
void Release() {
path_.clear();
}
private:
string path_;
};
MSSymbolServerConverter::LocateResult
MSSymbolServerConverter::LocateFile(const string& debug_or_code_file,
const string& debug_or_code_id,
const string& version,
string* file_name) {
assert(file_name);
file_name->clear();
GUIDOrSignatureIdentifier identifier;
if (!identifier.InitializeFromString(debug_or_code_id)) {
fprintf(stderr,
"LocateFile: Unparseable identifier for %s %s %s\n",
debug_or_code_file.c_str(),
debug_or_code_id.c_str(),
version.c_str());
return LOCATE_FAILURE;
}
HANDLE process = GetCurrentProcess(); // CloseHandle is not needed.
AutoSymSrv symsrv;
if (!symsrv.Initialize(process,
const_cast<char*>(symbol_path_.c_str()),
false)) {
fprintf(stderr, "LocateFile: SymInitialize: error %lu for %s %s %s\n",
GetLastError(),
debug_or_code_file.c_str(),
debug_or_code_id.c_str(),
version.c_str());
return LOCATE_FAILURE;
}
if (!SymRegisterCallback64(process, SymCallback,
reinterpret_cast<ULONG64>(this))) {
fprintf(stderr,
"LocateFile: SymRegisterCallback64: error %lu for %s %s %s\n",
GetLastError(),
debug_or_code_file.c_str(),
debug_or_code_id.c_str(),
version.c_str());
return LOCATE_FAILURE;
}
// SYMOPT_DEBUG arranges for SymCallback to be called with additional
// debugging information. This is used to determine the nature of failures.
DWORD options = SymGetOptions() | SYMOPT_DEBUG | SYMOPT_NO_PROMPTS |
SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_SECURE;
SymSetOptions(options);
// SymCallback will set these as needed inisde the SymFindFileInPath call.
fail_dns_ = false;
fail_timeout_ = false;
fail_not_found_ = false;
// Do the lookup.
char path[MAX_PATH];
if (!SymFindFileInPath(
process, NULL,
const_cast<char*>(debug_or_code_file.c_str()),
const_cast<void*>(identifier.guid_or_signature_pointer()),
identifier.age(), 0,
identifier.type() == GUIDOrSignatureIdentifier::TYPE_GUID ?
SSRVOPT_GUIDPTR : SSRVOPT_DWORDPTR,
path, SymFindFileInPathCallback, this)) {
DWORD error = GetLastError();
if (error == ERROR_FILE_NOT_FOUND) {
// This can be returned for a number of reasons. Use the crumbs
// collected by SymCallback to determine which one is relevant.
// These errors are possibly transient.
if (fail_dns_ || fail_timeout_) {
return LOCATE_RETRY;
}
if (fail_http_https_redir_) {
return LOCATE_HTTP_HTTPS_REDIR;
}
// This is an authoritiative file-not-found message.
if (fail_not_found_) {
fprintf(stderr,
"LocateFile: SymFindFileInPath: LOCATE_NOT_FOUND error "
"for %s %s %s\n",
debug_or_code_file.c_str(),
debug_or_code_id.c_str(),
version.c_str());
return LOCATE_NOT_FOUND;
}
// If the error is FILE_NOT_FOUND but none of the known error
// conditions are matched, fall through to LOCATE_FAILURE.
}
fprintf(stderr,
"LocateFile: SymFindFileInPath: error %lu for %s %s %s\n",
error,
debug_or_code_file.c_str(),
debug_or_code_id.c_str(),
version.c_str());
return LOCATE_FAILURE;
}
// Making sure path is null-terminated.
path[MAX_PATH - 1] = '\0';
// The AutoDeleter ensures that the file is only kept when returning
// LOCATE_SUCCESS.
AutoDeleter deleter(path);
// Do the cleanup here even though it will happen when symsrv goes out of
// scope, to allow it to influence the return value.
if (!symsrv.Cleanup()) {
fprintf(stderr, "LocateFile: SymCleanup: error %lu for %s %s %s\n",
GetLastError(),
debug_or_code_file.c_str(),
debug_or_code_id.c_str(),
version.c_str());
return LOCATE_FAILURE;
}
deleter.Release();
printf("Downloaded: %s\n", path);
*file_name = path;
return LOCATE_SUCCESS;
}
MSSymbolServerConverter::LocateResult
MSSymbolServerConverter::LocatePEFile(const MissingSymbolInfo& missing,
string* pe_file) {
return LocateFile(missing.code_file, missing.code_identifier,
missing.version, pe_file);
}
MSSymbolServerConverter::LocateResult
MSSymbolServerConverter::LocateSymbolFile(const MissingSymbolInfo& missing,
string* symbol_file) {
return LocateFile(missing.debug_file, missing.debug_identifier,
missing.version, symbol_file);
}
// static
BOOL CALLBACK MSSymbolServerConverter::SymCallback(HANDLE process,
ULONG action,
ULONG64 data,
ULONG64 context) {
MSSymbolServerConverter* self =
reinterpret_cast<MSSymbolServerConverter*>(context);
switch (action) {
case CBA_EVENT: {
IMAGEHLP_CBA_EVENT* cba_event =
reinterpret_cast<IMAGEHLP_CBA_EVENT*>(data);
// Put the string into a string object to be able to use string::find
// for substring matching. This is important because the not-found
// message does not use the entire string but is appended to the URL
// that SymSrv attempted to retrieve.
string desc(cba_event->desc);
if (self->trace_symsrv_) {
fprintf(stderr, "LocateFile: SymCallback: action desc '%s'\n",
desc.c_str());
}
// desc_action maps strings (in desc) to boolean pointers that are to
// be set to true if the string matches.
struct desc_action {
const char* desc; // The substring to match.
bool* action; // On match, this pointer will be set to true.
};
static const desc_action desc_actions[] = {
// When a DNS error occurs, it could be indiciative of network
// problems.
{"SYMSRV: The server name or address could not be resolved\n",
&self->fail_dns_},
// This message is produced if no connection is opened.
{"SYMSRV: A connection with the server could not be established\n",
&self->fail_timeout_},
// This message is produced if a connection is established but the
// server fails to respond to the HTTP request.
{"SYMSRV: The operation timed out\n", &self->fail_timeout_},
// This message is produced if the server is redirecting us from http
// to https. When this happens SymSrv will fail and we need to use
// the https URL in our call-- we've made a mistake.
{"ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR\n",
&self->fail_http_https_redir_},
// This message is produced when the requested file is not found,
// even if one or more of the above messages are also produced.
// It's trapped to distinguish between not-found and unknown-failure
// conditions. Note that this message will not be produced if a
// connection is established and the server begins to respond to the
// HTTP request but does not finish transmitting the file.
{" not found\n", &self->fail_not_found_}};
for (int desc_action_index = 0;
desc_action_index <
static_cast<int>(sizeof(desc_actions) / sizeof(desc_action));
++desc_action_index) {
if (desc.find(desc_actions[desc_action_index].desc) != string::npos) {
*(desc_actions[desc_action_index].action) = true;
break;
}
}
break;
}
}
// This function is a mere fly on the wall. Treat everything as unhandled.
return FALSE;
}
// static
BOOL CALLBACK MSSymbolServerConverter::SymFindFileInPathCallback(
const char* filename, void* context) {
// FALSE ends the search, indicating that the located symbol file is
// satisfactory.
return FALSE;
}
MSSymbolServerConverter::LocateResult
MSSymbolServerConverter::LocateAndConvertSymbolFile(
const MissingSymbolInfo& missing,
bool keep_symbol_file,
bool keep_pe_file,
string* converted_symbol_file,
string* symbol_file,
string* out_pe_file) {
assert(converted_symbol_file);
converted_symbol_file->clear();
if (symbol_file) {
symbol_file->clear();
}
string pdb_file;
LocateResult result = LocateSymbolFile(missing, &pdb_file);
if (result != LOCATE_SUCCESS) {
fprintf(stderr, "Fallback to PE-only symbol generation for: %s\n",
missing.debug_file.c_str());
return LocateAndConvertPEFile(missing, keep_pe_file, converted_symbol_file,
out_pe_file);
}
if (symbol_file && keep_symbol_file) {
*symbol_file = pdb_file;
}
// The conversion of a symbol file for a Windows 64-bit module requires
// loading of the executable file. If there is no executable file, convert
// using only the PDB file. Without an executable file, the conversion will
// fail for 64-bit modules but it should succeed for 32-bit modules.
string pe_file;
result = LocatePEFile(missing, &pe_file);
if (result != LOCATE_SUCCESS) {
fprintf(stderr, "WARNING: Could not download: %s\n", pe_file.c_str());
}
if (out_pe_file && keep_pe_file) {
*out_pe_file = pe_file;
}
// Conversion may fail because the file is corrupt. If a broken file is
// kept in the local cache, LocateSymbolFile will not hit the network again
// to attempt to locate it. To guard against problems like this, the
// symbol file in the local cache will be removed if conversion fails.
AutoDeleter pdb_deleter(pdb_file);
AutoDeleter pe_deleter(pe_file);
// Be sure that it's a .pdb file, since we'll be replacing .pdb with .sym
// for the converted file's name.
string pdb_extension = pdb_file.substr(pdb_file.length() - 4);
// strcasecmp is called _stricmp here.
if (_stricmp(pdb_extension.c_str(), ".pdb") != 0) {
fprintf(stderr, "LocateAndConvertSymbolFile: "
"no .pdb extension for %s %s %s %s\n",
missing.debug_file.c_str(),
missing.debug_identifier.c_str(),
missing.version.c_str(),
pdb_file.c_str());
return LOCATE_FAILURE;
}
PDBSourceLineWriter writer;
wstring pe_file_w;
if (!WindowsStringUtils::safe_mbstowcs(pe_file, &pe_file_w)) {
fprintf(stderr,
"LocateAndConvertSymbolFile: "
"WindowsStringUtils::safe_mbstowcs failed for %s\n",
pe_file.c_str());
return LOCATE_FAILURE;
}
wstring pdb_file_w;
if (!WindowsStringUtils::safe_mbstowcs(pdb_file, &pdb_file_w)) {
fprintf(stderr,
"LocateAndConvertSymbolFile: "
"WindowsStringUtils::safe_mbstowcs failed for %ws\n",
pdb_file_w.c_str());
return LOCATE_FAILURE;
}
if (!writer.Open(pdb_file_w, PDBSourceLineWriter::PDB_FILE)) {
fprintf(stderr,
"ERROR: PDBSourceLineWriter::Open failed for %s %s %s %ws\n",
missing.debug_file.c_str(), missing.debug_identifier.c_str(),
missing.version.c_str(), pdb_file_w.c_str());
return LOCATE_FAILURE;
}
if (!writer.SetCodeFile(pe_file_w)) {
fprintf(stderr,
"ERROR: PDBSourceLineWriter::SetCodeFile failed for %s %s %s %ws\n",
missing.debug_file.c_str(), missing.debug_identifier.c_str(),
missing.version.c_str(), pe_file_w.c_str());
return LOCATE_FAILURE;
}
*converted_symbol_file = pdb_file.substr(0, pdb_file.length() - 4) + ".sym";
FILE* converted_output = NULL;
#if _MSC_VER >= 1400 // MSVC 2005/8
errno_t err;
if ((err = fopen_s(&converted_output, converted_symbol_file->c_str(), "w"))
!= 0) {
#else // _MSC_VER >= 1400
// fopen_s and errno_t were introduced in MSVC8. Use fopen for earlier
// environments. Don't use fopen with MSVC8 and later, because it's
// deprecated. fopen does not provide reliable error codes, so just use
// -1 in the event of a failure.
int err;
if (!(converted_output = fopen(converted_symbol_file->c_str(), "w"))) {
err = -1;
#endif // _MSC_VER >= 1400
fprintf(stderr, "LocateAndConvertSymbolFile: "
"fopen_s: error %d for %s %s %s %s\n",
err,
missing.debug_file.c_str(),
missing.debug_identifier.c_str(),
missing.version.c_str(),
converted_symbol_file->c_str());
return LOCATE_FAILURE;
}
AutoDeleter sym_deleter(*converted_symbol_file);
bool success = writer.WriteSymbols(converted_output);
fclose(converted_output);
if (!success) {
fprintf(stderr, "LocateAndConvertSymbolFile: "
"PDBSourceLineWriter::WriteMap failed for %s %s %s %s\n",
missing.debug_file.c_str(),
missing.debug_identifier.c_str(),
missing.version.c_str(),
pdb_file.c_str());
return LOCATE_FAILURE;
}
if (keep_symbol_file) {
pdb_deleter.Release();
}
if (keep_pe_file) {
pe_deleter.Release();
}
sym_deleter.Release();
return LOCATE_SUCCESS;
}
MSSymbolServerConverter::LocateResult
MSSymbolServerConverter::LocateAndConvertPEFile(
const MissingSymbolInfo& missing,
bool keep_pe_file,
string* converted_symbol_file,
string* out_pe_file) {
assert(converted_symbol_file);
converted_symbol_file->clear();
string pe_file;
MSSymbolServerConverter::LocateResult result = LocatePEFile(missing,
&pe_file);
if (result != LOCATE_SUCCESS) {
fprintf(stderr, "WARNING: Could not download: %s\n", pe_file.c_str());
return result;
}
if (out_pe_file && keep_pe_file) {
*out_pe_file = pe_file;
}
// Conversion may fail because the file is corrupt. If a broken file is
// kept in the local cache, LocatePEFile will not hit the network again
// to attempt to locate it. To guard against problems like this, the
// PE file in the local cache will be removed if conversion fails.
AutoDeleter pe_deleter(pe_file);
// Be sure that it's a .exe or .dll file, since we'll be replacing extension
// with .sym for the converted file's name.
string pe_extension = pe_file.substr(pe_file.length() - 4);
// strcasecmp is called _stricmp here.
if (_stricmp(pe_extension.c_str(), ".exe") != 0 &&
_stricmp(pe_extension.c_str(), ".dll") != 0) {
fprintf(stderr, "LocateAndConvertPEFile: "
"no .dll/.exe extension for %s %s %s %s\n",
missing.debug_file.c_str(),
missing.debug_identifier.c_str(),
missing.version.c_str(),
pe_file.c_str());
return LOCATE_FAILURE;
}
*converted_symbol_file = pe_file.substr(0, pe_file.length() - 4) + ".sym";
FILE* converted_output = NULL;
#if _MSC_VER >= 1400 // MSVC 2005/8
errno_t err;
if ((err = fopen_s(&converted_output, converted_symbol_file->c_str(), "w"))
!= 0) {
#else // _MSC_VER >= 1400
// fopen_s and errno_t were introduced in MSVC8. Use fopen for earlier
// environments. Don't use fopen with MSVC8 and later, because it's
// deprecated. fopen does not provide reliable error codes, so just use
// -1 in the event of a failure.
int err;
if (!(converted_output = fopen(converted_symbol_file->c_str(), "w"))) {
err = -1;
#endif // _MSC_VER >= 1400
fprintf(stderr, "LocateAndConvertPEFile: "
"fopen_s: error %d for %s %s %s %s\n",
err,
missing.debug_file.c_str(),
missing.debug_identifier.c_str(),
missing.version.c_str(),
converted_symbol_file->c_str());
return LOCATE_FAILURE;
}
AutoDeleter sym_deleter(*converted_symbol_file);
wstring pe_file_w;
if (!WindowsStringUtils::safe_mbstowcs(pe_file, &pe_file_w)) {
fprintf(stderr,
"LocateAndConvertPEFile: "
"WindowsStringUtils::safe_mbstowcs failed for %s\n",
pe_file.c_str());
return LOCATE_FAILURE;
}
PESourceLineWriter writer(pe_file_w);
PDBModuleInfo module_info;
if (!writer.GetModuleInfo(&module_info)) {
fprintf(stderr, "LocateAndConvertPEFile: "
"PESourceLineWriter::GetModuleInfo failed for %s %s %s %s\n",
missing.debug_file.c_str(),
missing.debug_identifier.c_str(),
missing.version.c_str(),
pe_file.c_str());
return LOCATE_FAILURE;
}
if (module_info.cpu.compare(L"x86_64") != 0) {
// This module is not x64 so we cannot generate Breakpad symbols from the
// PE alone. Don't delete PE-- no need to retry download.
pe_deleter.Release();
return LOCATE_FAILURE;
}
bool success = writer.WriteSymbols(converted_output);
fclose(converted_output);
if (!success) {
fprintf(stderr, "LocateAndConvertPEFile: "
"PESourceLineWriter::WriteMap failed for %s %s %s %s\n",
missing.debug_file.c_str(),
missing.debug_identifier.c_str(),
missing.version.c_str(),
pe_file.c_str());
return LOCATE_FAILURE;
}
if (keep_pe_file) {
pe_deleter.Release();
}
sym_deleter.Release();
return LOCATE_SUCCESS;
}
} // namespace google_breakpad

View file

@ -0,0 +1,242 @@
// Copyright 2007 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.
// ms_symbol_server_converter.h: Obtain symbol files from a Microsoft
// symbol server, and convert them to Breakpad's dumped format.
//
// At runtime, MSSymbolServerConverter and code that it calls depend on being
// able to locate suitable versions of dbghelp.dll and symsrv.dll. For best
// results, place these files in the same directory as the executable.
// dbghelp.dll and symsrv.dll as supplied with Debugging Tools for Windows are
// both redistributable, as indicated by the package's redist.txt file.
//
// When connecting to Microsoft's symbol server at
// http://msdl.microsoft.com/download/symbols/, which provides access to
// symbols for the operating system itself, symsrv.dll requires agreement to
// Microsoft's "Terms of Use for Microsoft Symbols and Binaries." Because this
// library places the symbol engine into a promptless mode, the dialog with the
// terms will not appear, and use of Microsoft's symbol server will not be
// possible. To indicate agreement to the terms, create a file called
// symsrv.yes in the same directory as symsrv.dll. (Note that symsrv.dll will
// also recognize a symsrv.no file as indicating that you do not accept the
// terms; the .yes file takes priority over the .no file.) The terms of use
// are contained within symsrv.dll; they were formerly available online at
// http://www.microsoft.com/whdc/devtools/debugging/symsrvTOU2.mspx , but
// do not appear to be available online any longer as of January, 2007. It is
// possible to view the terms from within WinDbg (Debugging Tools for Windows)
// by removing any symsrv.yes and symsrv.no files from WinDbg's directory,
// setting the symbol path to include Microsoft's symbol server (.sympath), and
// attempting to load symbols from their server (.reload).
//
// This code has been tested with dbghelp.dll 6.5.3.7 and symsrv.dll 6.5.3.8,
// included with Microsoft Visual Studio 8 in Common7/IDE. This has also been
// tested with dbghelp.dll and symsrv.dll versions 6.6.7.5 and 6.12.2.633,
// included with the same versions of Debugging Tools for Windows, available at
// http://www.microsoft.com/whdc/devtools/debugging/ .
//
// Author: Mark Mentovai
#ifndef TOOLS_WINDOWS_MS_SYMBOL_SERVER_CONVERTER_H_
#define TOOLS_WINDOWS_MS_SYMBOL_SERVER_CONVERTER_H_
#include <windows.h>
#include <string>
#include <vector>
namespace google_breakpad {
using std::string;
using std::vector;
// MissingSymbolInfo contains the subset of the information in the processor's
// CodeModule structure relevant to obtaining a missing symbol file. Only
// debug_file and debug_identifier are relevant in actually obtaining the
// missing file; the other fields are for convenience.
struct MissingSymbolInfo {
string code_file;
string code_identifier;
string debug_file;
string debug_identifier;
string version;
};
class GUIDOrSignatureIdentifier {
public:
enum GUIDOrSignatureType {
TYPE_NONE = 0,
TYPE_GUID,
TYPE_SIGNATURE
};
GUIDOrSignatureIdentifier() : type_(TYPE_NONE) {}
// Converts |identifier|, a debug_identifier-formatted string, into its
// component fields: either a GUID and age, or signature and age. If
// successful, sets the relevant fields in the object, including the type
// field, and returns true. On error, returns false.
bool InitializeFromString(const string& identifier);
GUIDOrSignatureType type() const { return type_; }
GUID guid() const { return guid_; }
DWORD signature() const { return signature_; }
int age() const { return age_; }
const void* guid_or_signature_pointer() const { return &guid_; }
private:
GUIDOrSignatureType type_;
// An identifier contains either a 128-bit uuid or a 32-bit signature.
union {
GUID guid_;
DWORD signature_;
};
// All identifiers used here have age fields, which indicate a specific
// revision given a uuid or signature.
int age_;
};
class MSSymbolServerConverter {
public:
enum LocateResult {
LOCATE_FAILURE = 0,
LOCATE_NOT_FOUND, // Authoritative: the file is not present.
LOCATE_RETRY, // Transient (network?) error, try again later.
LOCATE_SUCCESS,
LOCATE_HTTP_HTTPS_REDIR
};
// Create a new object. local_cache is the location (pathname) of a local
// symbol store used to hold downloaded and converted symbol files. This
// directory will be created by LocateSymbolFile when it successfully
// retrieves a symbol file. symbol_servers contains a list of locations (URLs
// or pathnames) of the upstream symbol server stores, given in order of
// preference, with the first string in the vector identifying the first
// store to try. The vector must contain at least one string. None of the
// strings passed to this constructor may contain asterisk ('*') or semicolon
// (';') characters, as the symbol engine uses these characters as separators.
// If |trace_symsrv| is set then callbacks from SymSrv will be logged to
// stderr.
MSSymbolServerConverter(const string& local_cache,
const vector<string>& symbol_servers,
bool trace_symsrv);
// Locates the PE file (DLL or EXE) specified by the identifying information
// in |missing|, by checking the symbol stores identified when the object
// was created. When returning LOCATE_SUCCESS, pe_file is set to
// the pathname of the decompressed PE file as it is stored in the
// local cache.
LocateResult LocatePEFile(const MissingSymbolInfo& missing, string* pe_file);
// Locates the symbol file specified by the identifying information in
// |missing|, by checking the symbol stores identified when the object
// was created. When returning LOCATE_SUCCESS, symbol_file is set to
// the pathname of the decompressed symbol file as it is stored in the
// local cache.
LocateResult LocateSymbolFile(const MissingSymbolInfo& missing,
string* symbol_file);
// Calls LocateSymbolFile and converts the returned symbol file to the
// dumped-symbol format, storing it adjacent to the symbol file. The
// only conversion supported is from pdb files. Returns the return
// value of LocateSymbolFile, or if LocateSymbolFile succeeds but
// conversion fails, returns LOCATE_FAILURE. The pathname to the
// pdb file and to the converted symbol file are returned in
// |converted_symbol_file|, |symbol_file|, and |pe_file|. |symbol_file| and
// |pe_file| are optional and may be NULL. If only the converted symbol file
// is desired, set |keep_symbol_file| and |keep_pe_file| to false to indicate
// that the original symbol file (pdb) and executable file (exe, dll) should
// be deleted after conversion.
LocateResult LocateAndConvertSymbolFile(const MissingSymbolInfo& missing,
bool keep_symbol_file,
bool keep_pe_file,
string* converted_symbol_file,
string* symbol_file,
string* pe_file);
// Calls LocatePEFile and converts the returned PE file to the
// dumped-symbol format, storing it adjacent to the PE file. The
// only conversion supported is from PE files. Returns the return
// value of LocatePEFile, or if LocatePEFile succeeds but
// conversion fails, returns LOCATE_FAILURE. The pathname to the
// PE file and to the converted symbol file are returned in
// |converted_symbol_file| and |pe_file|. |pe_file| is optional and may be
// NULL. If only the converted symbol file is desired, set |keep_pe_file|
// to false to indicate that the executable file (exe, dll) should be deleted
// after conversion.
// NOTE: Currrently only supports x64 PEs.
LocateResult LocateAndConvertPEFile(const MissingSymbolInfo& missing,
bool keep_pe_file,
string* converted_symbol_file,
string* pe_file);
private:
// Locates the PDB or PE file (DLL or EXE) specified by the identifying
// information in |debug_or_code_file| and |debug_or_code_id|, by checking
// the symbol stores identified when the object was created. When
// returning LOCATE_SUCCESS, file_name is set to the pathname of the
// decompressed PDB or PE file file as it is stored in the local cache.
LocateResult LocateFile(const string& debug_or_code_file,
const string& debug_or_code_id,
const string& version, string* file_name);
// Called by various SymSrv functions to report status as progress is made
// and to allow the callback to influence processing. Messages sent to this
// callback can be used to distinguish between the various failure modes
// that SymFindFileInPath might encounter.
static BOOL CALLBACK SymCallback(HANDLE process, ULONG action, ULONG64 data,
ULONG64 context);
// Called by SymFindFileInPath (in LocateSymbolFile) after a candidate
// symbol file is located, when it's present in the local cache.
// SymFindFileInPath actually seems to accept NULL for a callback function
// and behave properly for our needs in that case, but the documentation
// doesn't mention it, so this little callback is provided.
static BOOL CALLBACK SymFindFileInPathCallback(const char* filename,
void* context);
// The search path used by SymSrv, built based on the arguments to the
// constructor.
string symbol_path_;
// SymCallback will set at least one of these failure variables if
// SymFindFileInPath fails for an expected reason.
bool fail_dns_; // DNS failures (fail_not_found_ will also be set).
bool fail_timeout_; // Timeouts (fail_not_found_ will also be set).
bool fail_http_https_redir_; // Bad URL-- we should be using HTTPS.
bool fail_not_found_; // The file could not be found. If this is the only
// fail_* member set, then it is authoritative.
// If set then callbacks from SymSrv will be logged to stderr.
bool trace_symsrv_;
};
} // namespace google_breakpad
#endif // TOOLS_WINDOWS_MS_SYMBOL_SERVER_CONVERTER_H_

View file

@ -0,0 +1,33 @@
@if "%ECHOON%"=="" @echo off
SETLOCAL
REM ******************************************************************
REM Please, make sure to run this in an Elevated Command Prompt.
REM Usage:
REM configure.cmd
REM ******************************************************************
REM ******************************************************************
REM Initialize
REM ******************************************************************
SET SCRIPT_LOCATION=%~dp0
REM ******************************************************************
REM Go to script location
REM ******************************************************************
pushd %SCRIPT_LOCATION%
REM ******************************************************************
REM Register msdia140.dll.
REM ******************************************************************
SET MSG=Failed to register msdia140.dll. Make sure to run this in elevated command prompt.
%systemroot%\SysWoW64\regsvr32.exe /s msdia140.dll & if errorlevel 1 echo %MSG% & goto :fail
:success
echo Configuration was successful.
ENDLOCAL
exit /b 0
:fail
ENDLOCAL
exit /b 1

View file

@ -0,0 +1,908 @@
// 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.
#pragma comment(lib, "winhttp.lib")
#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "diaguids.lib")
#pragma comment(lib, "imagehlp.lib")
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <cassert>
#include <cstdio>
#include <ctime>
#include <map>
#include <regex>
#include <string>
#include <vector>
#include "common/windows/http_upload.h"
#include "common/windows/string_utils-inl.h"
#include "common/windows/sym_upload_v2_protocol.h"
#include "tools/windows/converter/ms_symbol_server_converter.h"
#include "tools/windows/converter_exe/escaping.h"
#include "tools/windows/converter_exe/http_download.h"
#include "tools/windows/converter_exe/tokenizer.h"
using strings::WebSafeBase64Unescape;
using strings::WebSafeBase64Escape;
namespace {
using std::map;
using std::string;
using std::vector;
using std::wstring;
using crash::HTTPDownload;
using crash::Tokenizer;
using google_breakpad::HTTPUpload;
using google_breakpad::MissingSymbolInfo;
using google_breakpad::MSSymbolServerConverter;
using google_breakpad::WindowsStringUtils;
const char* kMissingStringDelimiters = "|";
const char* kLocalCachePath = "c:\\symbols";
const char* kNoExeMSSSServer = "http://msdl.microsoft.com/download/symbols/";
const wchar_t* kSymbolUploadTypeBreakpad = L"BREAKPAD";
const wchar_t* kSymbolUploadTypePE = L"PE";
const wchar_t* kSymbolUploadTypePDB = L"PDB";
const wchar_t* kConverterProductName = L"WinSymConv";
// Windows stdio doesn't do line buffering. Use this function to flush after
// writing to stdout and stderr so that a log will be available if the
// converter crashes.
static int FprintfFlush(FILE* file, const char* format, ...) {
va_list arguments;
va_start(arguments, format);
int retval = vfprintf(file, format, arguments);
va_end(arguments);
fflush(file);
return retval;
}
static string CurrentDateAndTime() {
const string kUnknownDateAndTime = R"(????-??-?? ??:??:??)";
time_t current_time;
time(&current_time);
// localtime_s is safer but is only available in MSVC8. Use localtime
// in earlier environments.
struct tm* time_pointer;
#if _MSC_VER >= 1400 // MSVC 2005/8
struct tm time_struct;
time_pointer =& time_struct;
if (localtime_s(time_pointer,& current_time) != 0) {
return kUnknownDateAndTime;
}
#else // _MSC_VER >= 1400
time_pointer = localtime(&current_time);
if (!time_pointer) {
return kUnknownDateAndTime;
}
#endif // _MSC_VER >= 1400
char buffer[256];
if (!strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", time_pointer)) {
return kUnknownDateAndTime;
}
return string(buffer);
}
// ParseMissingString turns |missing_string| into a MissingSymbolInfo
// structure. It returns true on success, and false if no such conversion
// is possible.
static bool ParseMissingString(const string& missing_string,
MissingSymbolInfo* missing_info) {
assert(missing_info);
vector<string> tokens;
Tokenizer::Tokenize(kMissingStringDelimiters, missing_string,& tokens);
if (tokens.size() != 5) {
return false;
}
missing_info->debug_file = tokens[0];
missing_info->debug_identifier = tokens[1];
missing_info->version = tokens[2];
missing_info->code_file = tokens[3];
missing_info->code_identifier = tokens[4];
return true;
}
// StringMapToWStringMap takes each element in a map that associates
// (narrow) strings to strings and converts the keys and values to wstrings.
// Returns true on success and false on failure, printing an error message.
static bool StringMapToWStringMap(const map<string, string>& smap,
map<wstring, wstring>* wsmap) {
assert(wsmap);
wsmap->clear();
for (map<string, string>::const_iterator iterator = smap.begin();
iterator != smap.end();
++iterator) {
wstring key;
if (!WindowsStringUtils::safe_mbstowcs(iterator->first,& key)) {
FprintfFlush(stderr,
"StringMapToWStringMap: safe_mbstowcs failed for key %s\n",
iterator->first.c_str());
return false;
}
wstring value;
if (!WindowsStringUtils::safe_mbstowcs(iterator->second,& value)) {
FprintfFlush(stderr, "StringMapToWStringMap: safe_mbstowcs failed "
"for value %s\n",
iterator->second.c_str());
return false;
}
wsmap->insert(make_pair(key, value));
}
return true;
}
// MissingSymbolInfoToParameters turns a MissingSymbolInfo structure into a
// map of parameters suitable for passing to HTTPDownload or HTTPUpload.
// Returns true on success and false on failure, printing an error message.
static bool MissingSymbolInfoToParameters(const MissingSymbolInfo& missing_info,
map<wstring, wstring>* wparameters) {
assert(wparameters);
map<string, string> parameters;
string encoded_param;
// Indicate the params are encoded.
parameters["encoded"] = "true"; // The string value here does not matter.
WebSafeBase64Escape(missing_info.code_file,& encoded_param);
parameters["code_file"] = encoded_param;
WebSafeBase64Escape(missing_info.code_identifier,& encoded_param);
parameters["code_identifier"] = encoded_param;
WebSafeBase64Escape(missing_info.debug_file,& encoded_param);
parameters["debug_file"] = encoded_param;
WebSafeBase64Escape(missing_info.debug_identifier,& encoded_param);
parameters["debug_identifier"] = encoded_param;
if (!missing_info.version.empty()) {
// The version is optional.
WebSafeBase64Escape(missing_info.version,& encoded_param);
parameters["version"] = encoded_param;
}
WebSafeBase64Escape("WinSymConv",& encoded_param);
parameters["product"] = encoded_param;
if (!StringMapToWStringMap(parameters, wparameters)) {
// StringMapToWStringMap will have printed an error.
return false;
}
return true;
}
// UploadSymbolFile sends |converted_file| as identified by |debug_file| and
// |debug_identifier|, to the symbol server rooted at |upload_symbol_url|.
// Returns true on success and false on failure, printing an error message.
static bool UploadSymbolFile(const wstring& upload_symbol_url,
const wstring& api_key,
const string& debug_file,
const string& debug_identifier,
const string& symbol_file,
const wstring& symbol_type) {
wstring debug_file_w;
if (!WindowsStringUtils::safe_mbstowcs(debug_file, &debug_file_w)) {
FprintfFlush(stderr, "UploadSymbolFile: safe_mbstowcs failed for %s\n",
symbol_file.c_str());
return false;
}
wstring debug_id_w;
if (!WindowsStringUtils::safe_mbstowcs(debug_identifier, &debug_id_w)) {
FprintfFlush(stderr, "UploadSymbolFile: safe_mbstowcs failed for %s\n",
symbol_file.c_str());
return false;
}
wstring symbol_file_w;
if (!WindowsStringUtils::safe_mbstowcs(symbol_file, &symbol_file_w)) {
FprintfFlush(stderr, "UploadSymbolFile: safe_mbstowcs failed for %s\n",
symbol_file.c_str());
return false;
}
int timeout_ms = 60 * 1000;
FprintfFlush(stderr, "Uploading %s\n", symbol_file.c_str());
if (!google_breakpad::SymUploadV2ProtocolSend(
upload_symbol_url.c_str(), api_key.c_str(), &timeout_ms, debug_file_w,
debug_id_w, symbol_file_w, symbol_type, kConverterProductName,
/*force=*/true)) {
FprintfFlush(stderr,
"UploadSymbolFile: HTTPUpload::SendRequest failed "
"for %s %s\n",
debug_file.c_str(), debug_identifier.c_str());
return false;
}
return true;
}
// SendFetchFailedPing informs the symbol server based at
// |fetch_symbol_failure_url| that the symbol file identified by
// |missing_info| could authoritatively not be located. Returns
// true on success and false on failure.
static bool SendFetchFailedPing(const wstring& fetch_symbol_failure_url,
const MissingSymbolInfo& missing_info) {
map<wstring, wstring> parameters;
if (!MissingSymbolInfoToParameters(missing_info,& parameters)) {
// MissingSymbolInfoToParameters or a callee will have printed an error.
return false;
}
string content;
if (!HTTPDownload::Download(fetch_symbol_failure_url,
& parameters,
& content,
NULL)) {
FprintfFlush(stderr, "SendFetchFailedPing: HTTPDownload::Download failed "
"for %s %s %s\n",
missing_info.debug_file.c_str(),
missing_info.debug_identifier.c_str(),
missing_info.version.c_str());
return false;
}
return true;
}
// Returns true if it's safe to make an external request for the symbol
// file described in missing_info. It's considered safe to make an
// external request unless the symbol file's debug_file string matches
// the given blacklist regular expression.
// The debug_file name is used from the MissingSymbolInfo struct,
// matched against the blacklist_regex.
static bool SafeToMakeExternalRequest(const MissingSymbolInfo& missing_info,
std::regex blacklist_regex) {
string file_name = missing_info.debug_file;
// Use regex_search because we want to match substrings.
if (std::regex_search(file_name, blacklist_regex)) {
FprintfFlush(stderr, "Not safe to make external request for file %s\n",
file_name.c_str());
return false;
}
return true;
}
// Converter options derived from command line parameters.
struct ConverterOptions {
ConverterOptions()
: report_fetch_failures(true), trace_symsrv(false), keep_files(false) {}
~ConverterOptions() {
}
// Names of MS Symbol Supplier Servers that are internal to Google, and may
// have symbols for any request.
vector<string> full_internal_msss_servers;
// Names of MS Symbol Supplier Servers that are internal to Google, and
// shouldn't be checked for symbols for any .exe files.
vector<string> full_external_msss_servers;
// Names of MS Symbol Supplier Servers that are external to Google, and may
// have symbols for any request.
vector<string> no_exe_internal_msss_servers;
// Names of MS Symbol Supplier Servers that are external to Google, and
// shouldn't be checked for symbols for any .exe files.
vector<string> no_exe_external_msss_servers;
// Temporary local storage for symbols.
string local_cache_path;
// URL for uploading symbols.
wstring upload_symbols_url;
// API key to use when uploading symbols.
wstring api_key;
// URL to fetch list of missing symbols.
wstring missing_symbols_url;
// URL to report symbol fetch failure.
wstring fetch_symbol_failure_url;
// Are symbol fetch failures reported.
bool report_fetch_failures;
// File containing the list of missing symbols. Fetch failures are not
// reported if such file is provided.
string missing_symbols_file;
// Regex used to blacklist files to prevent external symbol requests.
// Owned and cleaned up by this struct.
std::regex blacklist_regex;
// If set then SymSrv callbacks are logged to stderr.
bool trace_symsrv;
// If set then Breakpad/PE/PDB files won't be deleted after processing.
bool keep_files;
private:
// DISABLE_COPY_AND_ASSIGN
ConverterOptions(const ConverterOptions&);
ConverterOptions& operator=(const ConverterOptions&);
};
// ConverMissingSymbolFile takes a single MissingSymbolInfo structure and
// attempts to locate it from the symbol servers provided in the
// |options.*_msss_servers| arguments. "Full" servers are those that will be
// queried for all symbol files; "No-EXE" servers will only be queried for
// modules whose missing symbol data indicates are not main program executables.
// Results will be sent to the |options.upload_symbols_url| on success or
// |options.fetch_symbol_failure_url| on failure, and the local cache will be
// stored at |options.local_cache_path|. Because nothing can be done even in
// the event of a failure, this function returns no value, although it
// may result in error messages being printed.
static void ConvertMissingSymbolFile(const MissingSymbolInfo& missing_info,
const ConverterOptions& options) {
string time_string = CurrentDateAndTime();
FprintfFlush(stdout, "converter: %s: attempting %s %s %s\n",
time_string.c_str(),
missing_info.debug_file.c_str(),
missing_info.debug_identifier.c_str(),
missing_info.version.c_str());
// The first lookup is always to internal symbol servers.
// Always ask the symbol servers identified as "full."
vector<string> msss_servers = options.full_internal_msss_servers;
// If the file is not an .exe file, also ask an additional set of symbol
// servers, such as Microsoft's public symbol server.
bool is_exe = false;
if (missing_info.code_file.length() >= 4) {
string code_extension =
missing_info.code_file.substr(missing_info.code_file.size() - 4);
// Firefox is a special case: .dll-only servers should be consulted for
// its symbols. This enables us to get its symbols from Mozilla's
// symbol server when crashes occur in Google extension code hosted by a
// Firefox process.
if (_stricmp(code_extension.c_str(), ".exe") == 0 &&
_stricmp(missing_info.code_file.c_str(), "firefox.exe") != 0) {
is_exe = true;
}
}
if (!is_exe) {
msss_servers.insert(msss_servers.end(),
options.no_exe_internal_msss_servers.begin(),
options.no_exe_internal_msss_servers.end());
}
// If there are any suitable internal symbol servers, make a request.
MSSymbolServerConverter::LocateResult located =
MSSymbolServerConverter::LOCATE_FAILURE;
string converted_file;
string symbol_file;
string pe_file;
if (msss_servers.size() > 0) {
// Attempt to fetch the symbol file and convert it.
FprintfFlush(stderr, "Making internal request for %s (%s)\n",
missing_info.debug_file.c_str(),
missing_info.debug_identifier.c_str());
MSSymbolServerConverter converter(options.local_cache_path, msss_servers,
options.trace_symsrv);
located = converter.LocateAndConvertSymbolFile(
missing_info,
/*keep_symbol_file=*/true,
/*keep_pe_file=*/true, &converted_file, &symbol_file, &pe_file);
switch (located) {
case MSSymbolServerConverter::LOCATE_SUCCESS:
FprintfFlush(stderr, "LocateResult = LOCATE_SUCCESS\n");
// Upload it. Don't bother checking the return value. If this
// succeeds, it should disappear from the missing symbol list.
// If it fails, something will print an error message indicating
// the cause of the failure, and the item will remain on the
// missing symbol list.
UploadSymbolFile(options.upload_symbols_url, options.api_key,
missing_info.debug_file, missing_info.debug_identifier,
converted_file, kSymbolUploadTypeBreakpad);
if (!options.keep_files)
remove(converted_file.c_str());
// Upload PDB/PE if we have them
if (!symbol_file.empty()) {
UploadSymbolFile(options.upload_symbols_url, options.api_key,
missing_info.debug_file,
missing_info.debug_identifier, symbol_file,
kSymbolUploadTypePDB);
if (!options.keep_files)
remove(symbol_file.c_str());
}
if (!pe_file.empty()) {
UploadSymbolFile(options.upload_symbols_url, options.api_key,
missing_info.code_file,
missing_info.debug_identifier, pe_file,
kSymbolUploadTypePE);
if (!options.keep_files)
remove(pe_file.c_str());
}
// Note: this does leave some directories behind that could be
// cleaned up. The directories inside options.local_cache_path for
// debug_file/debug_identifier can be removed at this point.
break;
case MSSymbolServerConverter::LOCATE_NOT_FOUND:
FprintfFlush(stderr, "LocateResult = LOCATE_NOT_FOUND\n");
// The symbol file definitively did not exist. Fall through,
// so we can attempt an external query if it's safe to do so.
break;
case MSSymbolServerConverter::LOCATE_RETRY:
FprintfFlush(stderr, "LocateResult = LOCATE_RETRY\n");
// Fall through in case we should make an external request.
// If not, or if an external request fails in the same way,
// we'll leave the entry in the symbol file list and
// try again on a future pass. Print a message so that there's
// a record.
break;
case MSSymbolServerConverter::LOCATE_HTTP_HTTPS_REDIR:
FprintfFlush(
stderr,
"LocateResult = LOCATE_HTTP_HTTPS_REDIR\n"
"One of the specified URLs is using HTTP, which causes a redirect "
"from the server to HTTPS, which causes the SymSrv lookup to "
"fail.\n"
"This URL must be replaced with the correct HTTPS URL.\n");
break;
case MSSymbolServerConverter::LOCATE_FAILURE:
FprintfFlush(stderr, "LocateResult = LOCATE_FAILURE\n");
// LocateAndConvertSymbolFile printed an error message.
break;
default:
FprintfFlush(
stderr,
"FATAL: Unexpected return value '%d' from "
"LocateAndConvertSymbolFile()\n",
located);
assert(0);
break;
}
} else {
// No suitable internal symbol servers. This is fine because the converter
// is mainly used for downloading and converting of external symbols.
}
// Make a request to an external server if the internal request didn't
// succeed, and it's safe to do so.
if (located != MSSymbolServerConverter::LOCATE_SUCCESS &&
SafeToMakeExternalRequest(missing_info, options.blacklist_regex)) {
msss_servers = options.full_external_msss_servers;
if (!is_exe) {
msss_servers.insert(msss_servers.end(),
options.no_exe_external_msss_servers.begin(),
options.no_exe_external_msss_servers.end());
}
if (msss_servers.size() > 0) {
FprintfFlush(stderr, "Making external request for %s (%s)\n",
missing_info.debug_file.c_str(),
missing_info.debug_identifier.c_str());
MSSymbolServerConverter external_converter(
options.local_cache_path, msss_servers, options.trace_symsrv);
located = external_converter.LocateAndConvertSymbolFile(
missing_info,
/*keep_symbol_file=*/true,
/*keep_pe_file=*/true, &converted_file, &symbol_file, &pe_file);
} else {
FprintfFlush(stderr, "ERROR: No suitable external symbol servers.\n");
}
}
// Final handling for this symbol file is based on the result from the
// external request (if performed above), or on the result from the
// previous internal lookup.
switch (located) {
case MSSymbolServerConverter::LOCATE_SUCCESS:
FprintfFlush(stderr, "LocateResult = LOCATE_SUCCESS\n");
// Upload it. Don't bother checking the return value. If this
// succeeds, it should disappear from the missing symbol list.
// If it fails, something will print an error message indicating
// the cause of the failure, and the item will remain on the
// missing symbol list.
UploadSymbolFile(options.upload_symbols_url, options.api_key,
missing_info.debug_file, missing_info.debug_identifier,
converted_file, kSymbolUploadTypeBreakpad);
if (!options.keep_files)
remove(converted_file.c_str());
// Upload PDB/PE if we have them
if (!symbol_file.empty()) {
UploadSymbolFile(options.upload_symbols_url, options.api_key,
missing_info.debug_file, missing_info.debug_identifier,
symbol_file, kSymbolUploadTypePDB);
if (!options.keep_files)
remove(symbol_file.c_str());
}
if (!pe_file.empty()) {
UploadSymbolFile(options.upload_symbols_url, options.api_key,
missing_info.code_file, missing_info.debug_identifier,
pe_file, kSymbolUploadTypePE);
if (!options.keep_files)
remove(pe_file.c_str());
}
// Note: this does leave some directories behind that could be
// cleaned up. The directories inside options.local_cache_path for
// debug_file/debug_identifier can be removed at this point.
break;
case MSSymbolServerConverter::LOCATE_NOT_FOUND:
// The symbol file definitively didn't exist. Inform the server.
// If this fails, something will print an error message indicating
// the cause of the failure, but there's really nothing more to
// do. If this succeeds, the entry should be removed from the
// missing symbols list.
if (!options.report_fetch_failures) {
FprintfFlush(stderr, "SendFetchFailedPing skipped\n");
} else if (SendFetchFailedPing(options.fetch_symbol_failure_url,
missing_info)) {
FprintfFlush(stderr, "SendFetchFailedPing succeeded\n");
} else {
FprintfFlush(stderr, "SendFetchFailedPing failed\n");
}
break;
case MSSymbolServerConverter::LOCATE_RETRY:
FprintfFlush(stderr, "LocateResult = LOCATE_RETRY\n");
// Nothing to do but leave the entry in the symbol file list and
// try again on a future pass. Print a message so that there's
// a record.
FprintfFlush(stderr, "ConvertMissingSymbolFile: deferring retry "
"for %s %s %s\n",
missing_info.debug_file.c_str(),
missing_info.debug_identifier.c_str(),
missing_info.version.c_str());
break;
case MSSymbolServerConverter::LOCATE_HTTP_HTTPS_REDIR:
FprintfFlush(
stderr,
"LocateResult = LOCATE_HTTP_HTTPS_REDIR\n"
"One of the specified URLs is using HTTP, which causes a redirect "
"from the server to HTTPS, which causes the SymSrv lookup to fail.\n"
"This URL must be replaced with the correct HTTPS URL.\n");
break;
case MSSymbolServerConverter::LOCATE_FAILURE:
FprintfFlush(stderr, "LocateResult = LOCATE_FAILURE\n");
// LocateAndConvertSymbolFile printed an error message.
// This is due to a bad debug file name, so fetch failed.
if (!options.report_fetch_failures) {
FprintfFlush(stderr, "SendFetchFailedPing skipped\n");
} else if (SendFetchFailedPing(options.fetch_symbol_failure_url,
missing_info)) {
FprintfFlush(stderr, "SendFetchFailedPing succeeded\n");
} else {
FprintfFlush(stderr, "SendFetchFailedPing failed\n");
}
break;
default:
FprintfFlush(
stderr,
"FATAL: Unexpected return value '%d' from "
"LocateAndConvertSymbolFile()\n",
located);
assert(0);
break;
}
}
// Reads the contents of file |file_name| and populates |contents|.
// Returns true on success.
static bool ReadFile(string file_name, string* contents) {
char buffer[1024 * 8];
FILE* fp = fopen(file_name.c_str(), "rt");
if (!fp) {
return false;
}
contents->clear();
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
contents->append(buffer);
}
fclose(fp);
return true;
}
// ConvertMissingSymbolsList obtains a missing symbol list from
// |options.missing_symbols_url| or |options.missing_symbols_file| and calls
// ConvertMissingSymbolFile for each missing symbol file in the list.
static bool ConvertMissingSymbolsList(const ConverterOptions& options) {
// Set param to indicate requesting for encoded response.
map<wstring, wstring> parameters;
parameters[L"product"] = kConverterProductName;
parameters[L"encoded"] = L"true";
// Get the missing symbol list.
string missing_symbol_list;
if (!options.missing_symbols_file.empty()) {
if (!ReadFile(options.missing_symbols_file,& missing_symbol_list)) {
return false;
}
} else if (!HTTPDownload::Download(options.missing_symbols_url,& parameters,
& missing_symbol_list, NULL)) {
return false;
}
// Tokenize the content into a vector.
vector<string> missing_symbol_lines;
Tokenizer::Tokenize("\n", missing_symbol_list,& missing_symbol_lines);
FprintfFlush(stderr, "Found %d missing symbol files in list.\n",
missing_symbol_lines.size() - 1); // last line is empty.
int convert_attempts = 0;
for (vector<string>::const_iterator iterator = missing_symbol_lines.begin();
iterator != missing_symbol_lines.end();
++iterator) {
// Decode symbol line.
const string& encoded_line = *iterator;
// Skip lines that are blank.
if (encoded_line.empty()) {
continue;
}
string line;
if (!WebSafeBase64Unescape(encoded_line,& line)) {
// If decoding fails, assume the line is not encoded.
// This is helpful when the program connects to a debug server without
// encoding.
line = encoded_line;
}
FprintfFlush(stderr, "\nLine: %s\n", line.c_str());
// Turn each element into a MissingSymbolInfo structure.
MissingSymbolInfo missing_info;
if (!ParseMissingString(line,& missing_info)) {
FprintfFlush(stderr, "ConvertMissingSymbols: ParseMissingString failed "
"for %s from %ws\n",
line.c_str(), options.missing_symbols_url.c_str());
continue;
}
++convert_attempts;
ConvertMissingSymbolFile(missing_info, options);
}
// Say something reassuring, since ConvertMissingSymbolFile was never called
// and therefore never reported any progress.
if (convert_attempts == 0) {
string current_time = CurrentDateAndTime();
FprintfFlush(stdout, "converter: %s: nothing to convert\n",
current_time.c_str());
}
return true;
}
// usage prints the usage message. It returns 1 as a convenience, to be used
// as a return value from main.
static int usage(const char* program_name) {
FprintfFlush(
stderr,
"usage: %s [options]\n"
" -f <full_msss_server> MS servers to ask for all symbols\n"
" -n <no_exe_msss_server> same, but prevent asking for EXEs\n"
" -l <local_cache_path> Temporary local storage for symbols\n"
" -s <upload_url> URL for uploading symbols\n"
" -k <api_key> API key to use when uploading symbols\n"
" -m <missing_symbols_url> URL to fetch list of missing symbols\n"
" -mf <missing_symbols_file> File containing the list of missing\n"
" symbols. Fetch failures are not\n"
" reported if such file is provided.\n"
" -t <fetch_failure_url> URL to report symbol fetch failure\n"
" -b <regex> Regex used to blacklist files to\n"
" prevent external symbol requests\n"
" -tss If set then SymSrv callbacks will be\n"
" traced to stderr.\n"
" -keep-files If set then don't delete Breakpad/PE/\n"
" PDB files after conversion.\n"
" Note that any server specified by -f or -n that starts with \\filer\n"
" will be treated as internal, and all others as external.\n",
program_name);
return 1;
}
// "Internal" servers consist only of those whose names start with
// the literal string "\\filer\".
static bool IsInternalServer(const string& server_name) {
if (server_name.find("\\\\filer\\") == 0) {
return true;
}
return false;
}
// Adds a server with the given name to the list of internal or external
// servers, as appropriate.
static void AddServer(const string& server_name,
vector<string>* internal_servers,
vector<string>* external_servers) {
if (IsInternalServer(server_name)) {
internal_servers->push_back(server_name);
} else {
external_servers->push_back(server_name);
}
}
} // namespace
int main(int argc, char** argv) {
string time_string = CurrentDateAndTime();
FprintfFlush(stdout, "converter: %s: starting\n", time_string.c_str());
ConverterOptions options;
options.report_fetch_failures = true;
string blacklist_regex_str;
bool have_any_msss_servers = false;
for (int argi = 1; argi < argc; argi++) {
string option = argv[argi];
if (option == "-tss") {
printf("Tracing SymSrv callbacks to stderr.\n");
options.trace_symsrv = true;
continue;
} else if (option == "-keep-files") {
printf("Keeping Breakpad/PE/PDB files after conversion.\n");
options.keep_files = true;
continue;
}
string value = argv[++argi];
if (option == "-f") {
AddServer(value,& options.full_internal_msss_servers,
& options.full_external_msss_servers);
have_any_msss_servers = true;
} else if (option == "-n") {
AddServer(value,& options.no_exe_internal_msss_servers,
& options.no_exe_external_msss_servers);
have_any_msss_servers = true;
} else if (option == "-l") {
if (!options.local_cache_path.empty()) {
return usage(argv[0]);
}
options.local_cache_path = value;
} else if (option == "-s") {
if (!WindowsStringUtils::safe_mbstowcs(value,
& options.upload_symbols_url)) {
FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
value.c_str());
return 1;
}
} else if (option == "-k") {
if (!WindowsStringUtils::safe_mbstowcs(value, &options.api_key)) {
FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
value.c_str());
return 1;
}
} else if (option == "-m") {
if (!WindowsStringUtils::safe_mbstowcs(value,
& options.missing_symbols_url)) {
FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
value.c_str());
return 1;
}
} else if (option == "-mf") {
options.missing_symbols_file = value;
printf("Getting the list of missing symbols from a file. Fetch failures"
" will not be reported.\n");
options.report_fetch_failures = false;
} else if (option == "-t") {
if (!WindowsStringUtils::safe_mbstowcs(
value,
& options.fetch_symbol_failure_url)) {
FprintfFlush(stderr, "main: safe_mbstowcs failed for %s\n",
value.c_str());
return 1;
}
} else if (option == "-b") {
blacklist_regex_str = value;
} else {
return usage(argv[0]);
}
}
if (blacklist_regex_str.empty()) {
FprintfFlush(stderr, "No blacklist specified.\n");
return usage(argv[0]);
}
// Compile the blacklist regular expression for later use.
options.blacklist_regex = std::regex(blacklist_regex_str.c_str(),
std::regex_constants::icase);
// Set the defaults. If the user specified any MSSS servers, don't use
// any default.
if (!have_any_msss_servers) {
AddServer(kNoExeMSSSServer,& options.no_exe_internal_msss_servers,
& options.no_exe_external_msss_servers);
}
if (options.local_cache_path.empty()) {
options.local_cache_path = kLocalCachePath;
}
if (options.upload_symbols_url.empty()) {
FprintfFlush(stderr, "No upload symbols URL specified.\n");
return usage(argv[0]);
}
if (options.api_key.empty()) {
FprintfFlush(stderr, "No API key specified.\n");
return usage(argv[0]);
}
if (options.missing_symbols_url.empty() &&
options.missing_symbols_file.empty()) {
FprintfFlush(stderr, "No missing symbols URL or file specified.\n");
return usage(argv[0]);
}
if (options.fetch_symbol_failure_url.empty()) {
FprintfFlush(stderr, "No fetch symbol failure URL specified.\n");
return usage(argv[0]);
}
FprintfFlush(stdout,
"# of Symbol Servers (int/ext): %d/%d full, %d/%d no_exe\n",
options.full_internal_msss_servers.size(),
options.full_external_msss_servers.size(),
options.no_exe_internal_msss_servers.size(),
options.no_exe_external_msss_servers.size());
if (!ConvertMissingSymbolsList(options)) {
return 1;
}
time_string = CurrentDateAndTime();
FprintfFlush(stdout, "converter: %s: finished\n", time_string.c_str());
return 0;
}

View file

@ -0,0 +1,761 @@
// 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.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include "tools/windows/converter_exe/escaping.h"
#include <assert.h>
#define kApb kAsciiPropertyBits
const unsigned char kAsciiPropertyBits[256] = {
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x00
0x40, 0x68, 0x48, 0x48, 0x48, 0x48, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x10
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, // 0x20
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x84, 0x84, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05, // 0x40
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x50
0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x05, // 0x60
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x70
0x05, 0x05, 0x05, 0x10, 0x10, 0x10, 0x10, 0x40,
};
// Use !! to suppress the warning C4800 of forcing 'int' to 'bool'.
static inline bool ascii_isspace(unsigned char c) { return !!(kApb[c] & 0x08); }
///////////////////////////////////
// scoped_array
///////////////////////////////////
// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
// with new [] and the destructor deletes objects with delete [].
//
// As with scoped_ptr<C>, a scoped_array<C> either points to an object
// or is NULL. A scoped_array<C> owns the object that it points to.
// scoped_array<T> is thread-compatible, and once you index into it,
// the returned objects have only the threadsafety guarantees of T.
//
// Size: sizeof(scoped_array<C>) == sizeof(C*)
template <class C>
class scoped_array {
public:
// The element type
typedef C element_type;
// Constructor. Defaults to intializing with NULL.
// There is no way to create an uninitialized scoped_array.
// The input parameter must be allocated with new [].
explicit scoped_array(C* p = NULL) : array_(p) { }
// Destructor. If there is a C object, delete it.
// We don't need to test ptr_ == NULL because C++ does that for us.
~scoped_array() {
enum { type_must_be_complete = sizeof(C) };
delete[] array_;
}
// Reset. Deletes the current owned object, if any.
// Then takes ownership of a new object, if given.
// this->reset(this->get()) works.
void reset(C* p = NULL) {
if (p != array_) {
enum { type_must_be_complete = sizeof(C) };
delete[] array_;
array_ = p;
}
}
// Get one element of the current object.
// Will assert() if there is no current object, or index i is negative.
C& operator[](std::ptrdiff_t i) const {
assert(i >= 0);
assert(array_ != NULL);
return array_[i];
}
// Get a pointer to the zeroth element of the current object.
// If there is no current object, return NULL.
C* get() const {
return array_;
}
// Comparison operators.
// These return whether a scoped_array and a raw pointer refer to
// the same array, not just to two different but equal arrays.
bool operator==(const C* p) const { return array_ == p; }
bool operator!=(const C* p) const { return array_ != p; }
// Swap two scoped arrays.
void swap(scoped_array& p2) {
C* tmp = array_;
array_ = p2.array_;
p2.array_ = tmp;
}
// Release an array.
// The return value is the current pointer held by this object.
// If this object holds a NULL pointer, the return value is NULL.
// After this operation, this object will hold a NULL pointer,
// and will not own the object any more.
C* release() {
C* retVal = array_;
array_ = NULL;
return retVal;
}
private:
C* array_;
// Forbid comparison of different scoped_array types.
template <class C2> bool operator==(scoped_array<C2> const& p2) const;
template <class C2> bool operator!=(scoped_array<C2> const& p2) const;
// Disallow evil constructors
scoped_array(const scoped_array&);
void operator=(const scoped_array&);
};
///////////////////////////////////
// Escape methods
///////////////////////////////////
namespace strings {
// Return a mutable char* pointing to a string's internal buffer,
// which may not be null-terminated. Writing through this pointer will
// modify the string.
//
// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the
// next call to a string method that invalidates iterators.
//
// As of 2006-04, there is no standard-blessed way of getting a
// mutable reference to a string's internal buffer. However, issue 530
// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530)
// proposes this as the method. According to Matt Austern, this should
// already work on all current implementations.
inline char* string_as_array(string* str) {
// DO NOT USE const_cast<char*>(str->data())! See the unittest for why.
return str->empty() ? NULL : &*str->begin();
}
int CalculateBase64EscapedLen(int input_len, bool do_padding) {
// these formulae were copied from comments that used to go with the base64
// encoding functions
int intermediate_result = 8 * input_len + 5;
assert(intermediate_result > 0); // make sure we didn't overflow
int len = intermediate_result / 6;
if (do_padding) len = ((len + 3) / 4) * 4;
return len;
}
// Base64Escape does padding, so this calculation includes padding.
int CalculateBase64EscapedLen(int input_len) {
return CalculateBase64EscapedLen(input_len, true);
}
// ----------------------------------------------------------------------
// int Base64Unescape() - base64 decoder
// int Base64Escape() - base64 encoder
// int WebSafeBase64Unescape() - Google's variation of base64 decoder
// int WebSafeBase64Escape() - Google's variation of base64 encoder
//
// Check out
// http://www.cis.ohio-state.edu/htbin/rfc/rfc2045.html for formal
// description, but what we care about is that...
// Take the encoded stuff in groups of 4 characters and turn each
// character into a code 0 to 63 thus:
// A-Z map to 0 to 25
// a-z map to 26 to 51
// 0-9 map to 52 to 61
// +(- for WebSafe) maps to 62
// /(_ for WebSafe) maps to 63
// There will be four numbers, all less than 64 which can be represented
// by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
// Arrange the 6 digit binary numbers into three bytes as such:
// aaaaaabb bbbbcccc ccdddddd
// Equals signs (one or two) are used at the end of the encoded block to
// indicate that the text was not an integer multiple of three bytes long.
// ----------------------------------------------------------------------
int Base64UnescapeInternal(const char *src, int szsrc,
char *dest, int szdest,
const signed char* unbase64) {
static const char kPad64 = '=';
int decode = 0;
int destidx = 0;
int state = 0;
unsigned int ch = 0;
unsigned int temp = 0;
// The GET_INPUT macro gets the next input character, skipping
// over any whitespace, and stopping when we reach the end of the
// string or when we read any non-data character. The arguments are
// an arbitrary identifier (used as a label for goto) and the number
// of data bytes that must remain in the input to avoid aborting the
// loop.
#define GET_INPUT(label, remain) \
label: \
--szsrc; \
ch = *src++; \
decode = unbase64[ch]; \
if (decode < 0) { \
if (ascii_isspace((char)ch) && szsrc >= remain) \
goto label; \
state = 4 - remain; \
break; \
}
// if dest is null, we're just checking to see if it's legal input
// rather than producing output. (I suspect this could just be done
// with a regexp...). We duplicate the loop so this test can be
// outside it instead of in every iteration.
if (dest) {
// This loop consumes 4 input bytes and produces 3 output bytes
// per iteration. We can't know at the start that there is enough
// data left in the string for a full iteration, so the loop may
// break out in the middle; if so 'state' will be set to the
// number of input bytes read.
while (szsrc >= 4) {
// We'll start by optimistically assuming that the next four
// bytes of the string (src[0..3]) are four good data bytes
// (that is, no nulls, whitespace, padding chars, or illegal
// chars). We need to test src[0..2] for nulls individually
// before constructing temp to preserve the property that we
// never read past a null in the string (no matter how long
// szsrc claims the string is).
if (!src[0] || !src[1] || !src[2] ||
(temp = ((unbase64[static_cast<int>(src[0])] << 18) |
(unbase64[static_cast<int>(src[1])] << 12) |
(unbase64[static_cast<int>(src[2])] << 6) |
(unbase64[static_cast<int>(src[3])]))) & 0x80000000) {
// Iff any of those four characters was bad (null, illegal,
// whitespace, padding), then temp's high bit will be set
// (because unbase64[] is -1 for all bad characters).
//
// We'll back up and resort to the slower decoder, which knows
// how to handle those cases.
GET_INPUT(first, 4);
temp = decode;
GET_INPUT(second, 3);
temp = (temp << 6) | decode;
GET_INPUT(third, 2);
temp = (temp << 6) | decode;
GET_INPUT(fourth, 1);
temp = (temp << 6) | decode;
} else {
// We really did have four good data bytes, so advance four
// characters in the string.
szsrc -= 4;
src += 4;
decode = -1;
ch = '\0';
}
// temp has 24 bits of input, so write that out as three bytes.
if (destidx+3 > szdest) return -1;
dest[destidx+2] = (char)temp;
temp >>= 8;
dest[destidx+1] = (char)temp;
temp >>= 8;
dest[destidx] = (char)temp;
destidx += 3;
}
} else {
while (szsrc >= 4) {
if (!src[0] || !src[1] || !src[2] ||
(temp = ((unbase64[static_cast<int>(src[0])] << 18) |
(unbase64[static_cast<int>(src[1])] << 12) |
(unbase64[static_cast<int>(src[2])] << 6) |
(unbase64[static_cast<int>(src[3])]))) & 0x80000000) {
GET_INPUT(first_no_dest, 4);
GET_INPUT(second_no_dest, 3);
GET_INPUT(third_no_dest, 2);
GET_INPUT(fourth_no_dest, 1);
} else {
szsrc -= 4;
src += 4;
decode = -1;
ch = '\0';
}
destidx += 3;
}
}
#undef GET_INPUT
// if the loop terminated because we read a bad character, return
// now.
if (decode < 0 && ch != '\0' && ch != kPad64 && !ascii_isspace((char)ch))
return -1;
if (ch == kPad64) {
// if we stopped by hitting an '=', un-read that character -- we'll
// look at it again when we count to check for the proper number of
// equals signs at the end.
++szsrc;
--src;
} else {
// This loop consumes 1 input byte per iteration. It's used to
// clean up the 0-3 input bytes remaining when the first, faster
// loop finishes. 'temp' contains the data from 'state' input
// characters read by the first loop.
while (szsrc > 0) {
--szsrc;
ch = *src++;
decode = unbase64[ch];
if (decode < 0) {
if (ascii_isspace((char)ch)) {
continue;
} else if (ch == '\0') {
break;
} else if (ch == kPad64) {
// back up one character; we'll read it again when we check
// for the correct number of equals signs at the end.
++szsrc;
--src;
break;
} else {
return -1;
}
}
// Each input character gives us six bits of output.
temp = (temp << 6) | decode;
++state;
if (state == 4) {
// If we've accumulated 24 bits of output, write that out as
// three bytes.
if (dest) {
if (destidx+3 > szdest) return -1;
dest[destidx+2] = (char)temp;
temp >>= 8;
dest[destidx+1] = (char)temp;
temp >>= 8;
dest[destidx] = (char)temp;
}
destidx += 3;
state = 0;
temp = 0;
}
}
}
// Process the leftover data contained in 'temp' at the end of the input.
int expected_equals = 0;
switch (state) {
case 0:
// Nothing left over; output is a multiple of 3 bytes.
break;
case 1:
// Bad input; we have 6 bits left over.
return -1;
case 2:
// Produce one more output byte from the 12 input bits we have left.
if (dest) {
if (destidx+1 > szdest) return -1;
temp >>= 4;
dest[destidx] = (char)temp;
}
++destidx;
expected_equals = 2;
break;
case 3:
// Produce two more output bytes from the 18 input bits we have left.
if (dest) {
if (destidx+2 > szdest) return -1;
temp >>= 2;
dest[destidx+1] = (char)temp;
temp >>= 8;
dest[destidx] = (char)temp;
}
destidx += 2;
expected_equals = 1;
break;
default:
// state should have no other values at this point.
fprintf(stdout, "This can't happen; base64 decoder state = %d", state);
}
// The remainder of the string should be all whitespace, mixed with
// exactly 0 equals signs, or exactly 'expected_equals' equals
// signs. (Always accepting 0 equals signs is a google extension
// not covered in the RFC.)
int equals = 0;
while (szsrc > 0 && *src) {
if (*src == kPad64)
++equals;
else if (!ascii_isspace(*src))
return -1;
--szsrc;
++src;
}
return (equals == 0 || equals == expected_equals) ? destidx : -1;
}
int Base64Unescape(const char *src, int szsrc, char *dest, int szdest) {
static const signed char UnBase64[] = {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62/*+*/, -1, -1, -1, 63/*/ */,
52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
60/*8*/, 61/*9*/, -1, -1, -1, -1, -1, -1,
-1, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
23/*X*/, 24/*Y*/, 25/*Z*/, -1, -1, -1, -1, -1,
-1, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
49/*x*/, 50/*y*/, 51/*z*/, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
};
// The above array was generated by the following code
// #include <sys/time.h>
// #include <stdlib.h>
// #include <string.h>
// main()
// {
// static const char Base64[] =
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// char *pos;
// int idx, i, j;
// printf(" ");
// for (i = 0; i < 255; i += 8) {
// for (j = i; j < i + 8; j++) {
// pos = strchr(Base64, j);
// if ((pos == NULL) || (j == 0))
// idx = -1;
// else
// idx = pos - Base64;
// if (idx == -1)
// printf(" %2d, ", idx);
// else
// printf(" %2d/*%c*/,", idx, j);
// }
// printf("\n ");
// }
// }
return Base64UnescapeInternal(src, szsrc, dest, szdest, UnBase64);
}
bool Base64Unescape(const char *src, int slen, string* dest) {
// Determine the size of the output string. Base64 encodes every 3 bytes into
// 4 characters. any leftover chars are added directly for good measure.
// This is documented in the base64 RFC: http://www.ietf.org/rfc/rfc3548.txt
const int dest_len = 3 * (slen / 4) + (slen % 4);
dest->resize(dest_len);
// We are getting the destination buffer by getting the beginning of the
// string and converting it into a char *.
const int len = Base64Unescape(src, slen,
string_as_array(dest), dest->size());
if (len < 0) {
return false;
}
// could be shorter if there was padding
assert(len <= dest_len);
dest->resize(len);
return true;
}
// Base64Escape
//
// NOTE: We have to use an unsigned type for src because code built
// in the the /google tree treats characters as signed unless
// otherwised specified.
//
// TODO(who?): Move this function to use the char* type for "src"
int Base64EscapeInternal(const unsigned char *src, int szsrc,
char *dest, int szdest, const char *base64,
bool do_padding) {
static const char kPad64 = '=';
if (szsrc <= 0) return 0;
char *cur_dest = dest;
const unsigned char *cur_src = src;
// Three bytes of data encodes to four characters of cyphertext.
// So we can pump through three-byte chunks atomically.
while (szsrc > 2) { /* keep going until we have less than 24 bits */
if ((szdest -= 4) < 0) return 0;
cur_dest[0] = base64[cur_src[0] >> 2];
cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)];
cur_dest[2] = base64[((cur_src[1] & 0x0f) << 2) + (cur_src[2] >> 6)];
cur_dest[3] = base64[cur_src[2] & 0x3f];
cur_dest += 4;
cur_src += 3;
szsrc -= 3;
}
/* now deal with the tail (<=2 bytes) */
switch (szsrc) {
case 0:
// Nothing left; nothing more to do.
break;
case 1:
// One byte left: this encodes to two characters, and (optionally)
// two pad characters to round out the four-character cypherblock.
if ((szdest -= 2) < 0) return 0;
cur_dest[0] = base64[cur_src[0] >> 2];
cur_dest[1] = base64[(cur_src[0] & 0x03) << 4];
cur_dest += 2;
if (do_padding) {
if ((szdest -= 2) < 0) return 0;
cur_dest[0] = kPad64;
cur_dest[1] = kPad64;
cur_dest += 2;
}
break;
case 2:
// Two bytes left: this encodes to three characters, and (optionally)
// one pad character to round out the four-character cypherblock.
if ((szdest -= 3) < 0) return 0;
cur_dest[0] = base64[cur_src[0] >> 2];
cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)];
cur_dest[2] = base64[(cur_src[1] & 0x0f) << 2];
cur_dest += 3;
if (do_padding) {
if ((szdest -= 1) < 0) return 0;
cur_dest[0] = kPad64;
cur_dest += 1;
}
break;
default:
// Should not be reached: blocks of 3 bytes are handled
// in the while loop before this switch statement.
fprintf(stderr, "Logic problem? szsrc = %d", szsrc);
assert(false);
break;
}
return (cur_dest - dest);
}
static const char kBase64Chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char kWebSafeBase64Chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
int Base64Escape(const unsigned char *src, int szsrc, char *dest, int szdest) {
return Base64EscapeInternal(src, szsrc, dest, szdest, kBase64Chars, true);
}
void Base64Escape(const unsigned char *src, int szsrc,
string* dest, bool do_padding) {
const int max_escaped_size =
CalculateBase64EscapedLen(szsrc, do_padding);
dest->clear();
dest->resize(max_escaped_size + 1, '\0');
const int escaped_len = Base64EscapeInternal(src, szsrc,
&*dest->begin(), dest->size(),
kBase64Chars,
do_padding);
assert(max_escaped_size <= escaped_len);
dest->resize(escaped_len);
}
void Base64Escape(const string& src, string* dest) {
Base64Escape(reinterpret_cast<const unsigned char*>(src.c_str()),
src.size(), dest, true);
}
////////////////////////////////////////////////////
// WebSafe methods
////////////////////////////////////////////////////
int WebSafeBase64Unescape(const char *src, int szsrc, char *dest, int szdest) {
static const signed char UnBase64[] = {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, 62/*-*/, -1, -1,
52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
60/*8*/, 61/*9*/, -1, -1, -1, -1, -1, -1,
-1, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
23/*X*/, 24/*Y*/, 25/*Z*/, -1, -1, -1, -1, 63/*_*/,
-1, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
49/*x*/, 50/*y*/, 51/*z*/, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
};
// The above array was generated by the following code
// #include <sys/time.h>
// #include <stdlib.h>
// #include <string.h>
// main()
// {
// static const char Base64[] =
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
// char *pos;
// int idx, i, j;
// printf(" ");
// for (i = 0; i < 255; i += 8) {
// for (j = i; j < i + 8; j++) {
// pos = strchr(Base64, j);
// if ((pos == NULL) || (j == 0))
// idx = -1;
// else
// idx = pos - Base64;
// if (idx == -1)
// printf(" %2d, ", idx);
// else
// printf(" %2d/*%c*/,", idx, j);
// }
// printf("\n ");
// }
// }
return Base64UnescapeInternal(src, szsrc, dest, szdest, UnBase64);
}
bool WebSafeBase64Unescape(const char *src, int slen, string* dest) {
int dest_len = 3 * (slen / 4) + (slen % 4);
dest->clear();
dest->resize(dest_len);
int len = WebSafeBase64Unescape(src, slen, &*dest->begin(), dest->size());
if (len < 0) {
dest->clear();
return false;
}
// could be shorter if there was padding
assert(len <= dest_len);
dest->resize(len);
return true;
}
bool WebSafeBase64Unescape(const string& src, string* dest) {
return WebSafeBase64Unescape(src.data(), src.size(), dest);
}
int WebSafeBase64Escape(const unsigned char *src, int szsrc, char *dest,
int szdest, bool do_padding) {
return Base64EscapeInternal(src, szsrc, dest, szdest,
kWebSafeBase64Chars, do_padding);
}
void WebSafeBase64Escape(const unsigned char *src, int szsrc,
string *dest, bool do_padding) {
const int max_escaped_size =
CalculateBase64EscapedLen(szsrc, do_padding);
dest->clear();
dest->resize(max_escaped_size + 1, '\0');
const int escaped_len = Base64EscapeInternal(src, szsrc,
&*dest->begin(), dest->size(),
kWebSafeBase64Chars,
do_padding);
assert(max_escaped_size <= escaped_len);
dest->resize(escaped_len);
}
void WebSafeBase64EscapeInternal(const string& src,
string* dest,
bool do_padding) {
int encoded_len = CalculateBase64EscapedLen(src.size());
scoped_array<char> buf(new char[encoded_len]);
int len = WebSafeBase64Escape(reinterpret_cast<const unsigned char*>(src.c_str()),
src.size(), buf.get(),
encoded_len, do_padding);
dest->assign(buf.get(), len);
}
void WebSafeBase64Escape(const string& src, string* dest) {
WebSafeBase64EscapeInternal(src, dest, false);
}
void WebSafeBase64EscapeWithPadding(const string& src, string* dest) {
WebSafeBase64EscapeInternal(src, dest, true);
}
} // namespace strings

View file

@ -0,0 +1,99 @@
// 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.
//
// Base64 escaping methods to encode/decode strings.
#ifndef TOOLS_WINDOWS_CONVERTER_EXE_ESCAPING_H_
#define TOOLS_WINDOWS_CONVERTER_EXE_ESCAPING_H_
#include <string>
namespace strings {
using std::string;
// ----------------------------------------------------------------------
// Base64Escape()
// WebSafeBase64Escape()
// Encode "src" to "dest" using base64 encoding.
// src is not null terminated, instead specify len.
// 'dest' should have at least CalculateBase64EscapedLen() length.
// RETURNS the length of dest.
// The WebSafe variation use '-' instead of '+' and '_' instead of '/'
// so that we can place the out in the URL or cookies without having
// to escape them. It also has an extra parameter "do_padding",
// which when set to false will prevent padding with "=".
// ----------------------------------------------------------------------
void Base64Escape(const string& src, string* dest);
int Base64Escape(const unsigned char* src, int slen, char* dest, int szdest);
// Encode src into dest with padding.
void Base64Escape(const unsigned char* src, int szsrc,
string* dest, bool do_padding);
int WebSafeBase64Escape(const unsigned char* src, int slen, char* dest,
int szdest, bool do_padding);
// Encode src into dest web-safely without padding.
void WebSafeBase64Escape(const string& src, string* dest);
// Encode src into dest web-safely with padding.
void WebSafeBase64EscapeWithPadding(const string& src, string* dest);
void WebSafeBase64Escape(const unsigned char* src, int szsrc,
string* dest, bool do_padding);
// ----------------------------------------------------------------------
// Base64Unescape()
// WebSafeBase64Unescape()
// Copies "src" to "dest", where src is in base64 and is written to its
// ASCII equivalents. src is not null terminated, instead specify len.
// I recommend that slen<szdest, but we honor szdest anyway.
// RETURNS the length of dest, or -1 if src contains invalid chars.
// The WebSafe variation use '-' instead of '+' and '_' instead of '/'.
// The variations that store into a string clear the string first, and
// return false (with dest empty) if src contains invalid chars; for
// these versions src and dest must be different strings.
// ----------------------------------------------------------------------
int Base64Unescape(const char* src, int slen, char* dest, int szdest);
bool Base64Unescape(const char* src, int slen, string* dest);
inline bool Base64Unescape(const string& src, string* dest) {
return Base64Unescape(src.data(), src.size(), dest);
}
int WebSafeBase64Unescape(const char* src, int slen, char* dest, int szdest);
bool WebSafeBase64Unescape(const char* src, int slen, string* dest);
bool WebSafeBase64Unescape(const string& src, string* dest);
// Return the length to use for the output buffer given to the base64 escape
// routines. Make sure to use the same value for do_padding in both.
// This function may return incorrect results if given input_len values that
// are extremely high, which should happen rarely.
int CalculateBase64EscapedLen(int input_len, bool do_padding);
// Use this version when calling Base64Escape without a do_padding arg.
int CalculateBase64EscapedLen(int input_len);
} // namespace strings
#endif // TOOLS_WINDOWS_CONVERTER_EXE_ESCAPING_H_

View file

@ -0,0 +1,96 @@
// 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 TOOLS_CRASH_CONVERTER_WINDOWS_HTTP_CLIENT_H_
#define TOOLS_CRASH_CONVERTER_WINDOWS_HTTP_CLIENT_H_
#include <tchar.h>
#include <windows.h>
#include <vector>
typedef void* HttpHandle;
namespace crash {
// HttpClient provides an abstract layer for HTTP APIs. The actual
// implementation can be based on either WinHttp or WinInet.
class HttpClient {
public:
enum AccessType {
ACCESS_TYPE_PRECONFIG,
ACCESS_TYPE_DIRECT,
ACCESS_TYPE_PROXY,
};
virtual ~HttpClient() {}
virtual bool CrackUrl(const TCHAR* url,
DWORD flags,
TCHAR* scheme,
size_t scheme_buffer_length,
TCHAR* host,
size_t host_buffer_length,
TCHAR* uri,
size_t uri_buffer_length,
int* port) const = 0;
virtual bool Open(const TCHAR* user_agent,
DWORD access_type,
const TCHAR* proxy_name,
const TCHAR* proxy_bypass,
HttpHandle* session_handle) const = 0;
virtual bool Connect(HttpHandle session_handle,
const TCHAR* server,
int port,
HttpHandle* connection_handle) const = 0;
virtual bool OpenRequest(HttpHandle connection_handle,
const TCHAR* verb,
const TCHAR* uri,
const TCHAR* version,
const TCHAR* referrer,
bool is_secure,
HttpHandle* request_handle) const = 0;
virtual bool SendRequest(HttpHandle request_handle,
const TCHAR* headers,
DWORD headers_length) const = 0;
virtual bool ReceiveResponse(HttpHandle request_handle) const = 0;
virtual bool GetHttpStatusCode(HttpHandle request_handle,
int* status_code) const = 0;
virtual bool GetContentLength(HttpHandle request_handle,
DWORD* content_length) const = 0;
virtual bool ReadData(HttpHandle request_handle,
void* buffer,
DWORD buffer_length,
DWORD* bytes_read) const = 0;
virtual bool Close(HttpHandle handle) const = 0;
static const DWORD kUnknownContentLength = (DWORD)-1;
};
} // namespace crash
#endif // TOOLS_CRASH_CONVERTER_WINDOWS_HTTP_CLIENT_H_

View file

@ -0,0 +1,330 @@
// 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.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <assert.h>
#include <stdio.h>
#include <Windows.h>
#include <WinInet.h>
#include <vector>
#include "tools/windows/converter_exe/http_download.h"
#include "tools/windows/converter_exe/winhttp_client.h"
#include "tools/windows/converter_exe/wininet_client.h"
namespace crash {
static const std::vector<char>::size_type kVectorChunkSize = 4096; // 4 KB
using std::vector;
// Class that atuo closes the contained HttpHandle when the object
// goes out of scope.
class AutoHttpHandle {
public:
AutoHttpHandle() : handle_(NULL) {}
explicit AutoHttpHandle(HttpHandle handle) : handle_(handle) {}
~AutoHttpHandle() {
if (handle_) {
InternetCloseHandle(handle_);
}
}
HttpHandle get() { return handle_; }
HttpHandle* get_handle_addr () { return &handle_; }
private:
HttpHandle handle_;
};
// Template class for auto releasing the contained pointer when
// the object goes out of scope.
template<typename T>
class AutoPtr {
public:
explicit AutoPtr(T* ptr) : ptr_(ptr) {}
~AutoPtr() {
if (ptr_) {
delete ptr_;
}
}
T* get() { return ptr_; }
T* operator -> () { return ptr_; }
private:
T* ptr_;
};
// CheckParameters ensures that the parameters in |parameters| are safe for
// use in an HTTP URL. Returns true if they are, false if unsafe characters
// are present.
static bool CheckParameters(const map<wstring, wstring>* parameters) {
for (map<wstring, wstring>::const_iterator iterator = parameters->begin();
iterator != parameters->end();
++iterator) {
const wstring& key = iterator->first;
if (key.empty()) {
// Disallow empty parameter names.
return false;
}
for (unsigned int i = 0; i < key.size(); ++i) {
wchar_t c = key[i];
if (c < 32 || c == '"' || c == '?' || c == '&' || c > 127) {
return false;
}
}
const wstring& value = iterator->second;
for (unsigned int i = 0; i < value.size(); ++i) {
wchar_t c = value[i];
if (c < 32 || c == '"' || c == '?' || c == '&' || c > 127) {
return false;
}
}
}
return true;
}
HttpClient* HTTPDownload::CreateHttpClient(const wchar_t* url) {
const TCHAR* kHttpApiPolicyEnvironmentVariable = TEXT("USE_WINHTTP");
TCHAR buffer[2] = {0};
HttpClient* http_client = NULL;
if (::GetEnvironmentVariable(kHttpApiPolicyEnvironmentVariable,
buffer,
sizeof(buffer)/sizeof(buffer[0])) > 0) {
fprintf(stdout,
"Environment variable [%ws] is set, use WinHttp\n",
kHttpApiPolicyEnvironmentVariable);
http_client = CreateWinHttpClient(url);
if (http_client == NULL) {
fprintf(stderr, "WinHttpClient not created, Is the protocol HTTPS? "
"Fall back to WinInet API.\n");
}
} else {
fprintf(stderr,
"Environment variable [%ws] is NOT set, use WinInet API\n",
kHttpApiPolicyEnvironmentVariable);
}
if (http_client == NULL) {
return CreateWinInetClient(url);
}
return http_client;
}
// static
bool HTTPDownload::Download(const wstring& url,
const map<wstring, wstring>* parameters,
string *content, int *status_code) {
assert(content);
AutoPtr<HttpClient> http_client(CreateHttpClient(url.c_str()));
if (!http_client.get()) {
fprintf(stderr, "Failed to create any http client.\n");
return false;
}
if (status_code) {
*status_code = 0;
}
wchar_t scheme[16] = {0};
wchar_t host[256] = {0};
wchar_t path[256] = {0};
int port = 0;
if (!http_client->CrackUrl(url.c_str(),
0,
scheme,
sizeof(scheme)/sizeof(scheme[0]),
host,
sizeof(host)/sizeof(host[0]),
path,
sizeof(path)/sizeof(path[0]),
&port)) {
fprintf(stderr,
"HTTPDownload::Download: InternetCrackUrl: error %lu for %ws\n",
GetLastError(), url.c_str());
return false;
}
bool secure = false;
if (_wcsicmp(scheme, L"https") == 0) {
secure = true;
} else if (wcscmp(scheme, L"http") != 0) {
fprintf(stderr,
"HTTPDownload::Download: scheme must be http or https for %ws\n",
url.c_str());
return false;
}
AutoHttpHandle internet;
if (!http_client->Open(NULL, // user agent
HttpClient::ACCESS_TYPE_PRECONFIG,
NULL, // proxy name
NULL, // proxy bypass
internet.get_handle_addr())) {
fprintf(stderr,
"HTTPDownload::Download: Open: error %lu for %ws\n",
GetLastError(), url.c_str());
return false;
}
AutoHttpHandle connection;
if (!http_client->Connect(internet.get(),
host,
port,
connection.get_handle_addr())) {
fprintf(stderr,
"HTTPDownload::Download: InternetConnect: error %lu for %ws\n",
GetLastError(), url.c_str());
return false;
}
wstring request_string = path;
if (parameters) {
// TODO(mmentovai): escape bad characters in parameters instead of
// forbidding them.
if (!CheckParameters(parameters)) {
fprintf(stderr,
"HTTPDownload::Download: invalid characters in parameters\n");
return false;
}
bool added_parameter = false;
for (map<wstring, wstring>::const_iterator iterator = parameters->begin();
iterator != parameters->end();
++iterator) {
request_string.append(added_parameter ? L"&" : L"?");
request_string.append(iterator->first);
request_string.append(L"=");
request_string.append(iterator->second);
added_parameter = true;
}
}
AutoHttpHandle request;
if (!http_client->OpenRequest(connection.get(),
L"GET",
request_string.c_str(),
NULL, // version
NULL, // referer
secure,
request.get_handle_addr())) {
fprintf(stderr,
"HttpClient::OpenRequest: error %lu for %ws, request: %ws\n",
GetLastError(), url.c_str(), request_string.c_str());
return false;
}
if (!http_client->SendRequest(request.get(), NULL, 0)) {
fprintf(stderr,
"HttpClient::SendRequest: error %lu for %ws\n",
GetLastError(), url.c_str());
return false;
}
if (!http_client->ReceiveResponse(request.get())) {
fprintf(stderr,
"HttpClient::ReceiveResponse: error %lu for %ws\n",
GetLastError(), url.c_str());
return false;
}
int http_status = 0;
if (!http_client->GetHttpStatusCode(request.get(), &http_status)) {
fprintf(stderr,
"HttpClient::GetHttpStatusCode: error %lu for %ws\n",
GetLastError(), url.c_str());
return false;
}
if (http_status != 200) {
fprintf(stderr,
"HTTPDownload::Download: HTTP status code %d for %ws\n",
http_status, url.c_str());
return false;
}
DWORD content_length = 0;
vector<char>::size_type buffer_size = 0;
http_client->GetContentLength(request.get(), &content_length);
if (content_length == HttpClient::kUnknownContentLength) {
buffer_size = kVectorChunkSize;
} else {
buffer_size = content_length;
}
if (content_length != 0) {
vector<char> response_buffer = vector<char>(buffer_size+1);
DWORD size_read;
DWORD total_read = 0;
bool read_result;
do {
if (content_length == HttpClient::kUnknownContentLength
&& buffer_size == total_read) {
// The content length wasn't specified in the response header, so we
// have to keep growing the buffer until we're done reading.
buffer_size += kVectorChunkSize;
response_buffer.resize(buffer_size);
}
read_result = !!http_client->ReadData(
request.get(),
&response_buffer[total_read],
static_cast<DWORD>(buffer_size) - total_read,
&size_read);
total_read += size_read;
} while (read_result && (size_read != 0));
if (!read_result) {
fprintf(stderr,
"HttpClient::ReadData: error %lu for %ws\n",
GetLastError(),
url.c_str());
return false;
} else if (size_read != 0) {
fprintf(stderr,
"HttpClient::ReadData: error %lu/%lu for %ws\n",
total_read,
content_length,
url.c_str());
return false;
}
content->assign(&response_buffer[0], total_read);
} else {
content->clear();
}
return true;
}
} // namespace crash

View file

@ -0,0 +1,62 @@
// 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 TOOLS_WINDOWS_CONVERTER_EXE_HTTP_DOWNLOAD_H_
#define TOOLS_WINDOWS_CONVERTER_EXE_HTTP_DOWNLOAD_H_
#include <map>
#include <string>
#include "tools/windows/converter_exe/winhttp_client.h"
namespace crash {
using std::map;
using std::string;
using std::wstring;
class HTTPDownload {
public:
// Retrieves the resource located at |url|, a http or https URL, via WinInet.
// The request is fetched with GET request; the optional |parameters| are
// appended to the URL. Returns true on success, placing the content of the
// retrieved resource in |content|. Returns false on failure. HTTP status
// codes other than 200 cause Download to return false. If |status_code| is
// supplied, it will be set to the value of the HTTP status code, if an HTTP
// transaction occurs. If Download fails before a transaction can occur,
// |status_code| will be set to 0. Any failures will result in messages
// being printed to stderr.
static bool Download(const wstring& url,
const map<wstring, wstring>* parameters,
string *content, int *status_code);
private:
static HttpClient* CreateHttpClient(const wchar_t*);
};
} // namespace crash
#endif // TOOLS_WINDOWS_CONVERTER_EXE_HTTP_DOWNLOAD_H_

View file

@ -0,0 +1,5 @@
msctf.pdb|6A5BABB8E88644C696530BFE3C90F32F2|6.1.7600.16385|msctf.dll|4A5BDFAA109000
imm32.pdb|98F27BA5AEE541ECBEE00CD03AD50FEE2|6.1.7600.16385|imm32.dll|4A5BDF402e000
amd_opencl64.pdb|3D306D0FCCB14F47AF322A5ACDF5EEA81||amd_opencl64.dll|587901FB1E000
igd10iumd64.pdb|B2B72475BB0846D8ADE4344FAE0CCE361 ||igd10iumd64.dll|568D69FBD99000
NvCameraWhitelisting64.pdb|3C364C4D3FBF4180B021D52D469C6DAB1||NvCameraWhitelisting64.dll|5B8ED23485000

View file

@ -0,0 +1,2 @@
See breakpad/tools/windows/converter/ms_symbol_server_converter.h for a
description of this file's function.

View file

@ -0,0 +1,65 @@
// 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.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <assert.h>
#include "tools/windows/converter_exe/tokenizer.h"
namespace crash {
// static
void Tokenizer::Tokenize(const string& delimiters, const string& input,
vector<string>* output) {
assert(output);
output->clear();
string::size_type position = 0; // Where to begin looking for a delimiter
string::size_type new_position; // Position of found delimiter
string token;
while ((new_position = input.find_first_of(delimiters, position)) !=
string::npos) {
token = input.substr(position, new_position - position);
output->push_back(token);
// Next time, begin looking right after this delimiter.
position = new_position + 1;
}
// There are no more delimiters in the string. Take everything from the
// final delimiter up to the end of the string as a token. This may be
// an empty string.
token = input.substr(position);
output->push_back(token);
}
} // namespace crash

View file

@ -0,0 +1,51 @@
// 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 TOOLS_WINDOWS_CONVERTER_EXE_TOKENIZER_H_
#define TOOLS_WINDOWS_CONVERTER_EXE_TOKENIZER_H_
#include <string>
#include <vector>
namespace crash {
using std::string;
using std::vector;
class Tokenizer {
public:
// Splits |input| into a series of tokens delimited in the input string by
// any of the characters in |delimiters|. The tokens are passed back in the
// |output| vector.
static void Tokenize(const string& delimiters, const string& input,
vector<string>* output);
};
} // namespace crash
#endif // TOOLS_WINDOWS_CONVERTER_EXE_TOKENIZER_H_

View file

@ -0,0 +1,311 @@
// 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.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include "tools/windows/converter_exe/winhttp_client.h"
#include <assert.h>
#include <stdlib.h>
#include <windows.h>
#include <winhttp.h>
#include <vector>
namespace crash {
namespace internal {
// This class implements HttpClient based on WinInet APIs.
class WinHttpClient : public HttpClient {
public:
virtual ~WinHttpClient() {}
virtual bool CrackUrl(const TCHAR* url,
DWORD flags,
TCHAR* scheme,
size_t scheme_buffer_length,
TCHAR* host,
size_t host_buffer_length,
TCHAR* uri,
size_t uri_buffer_length,
int* port) const;
virtual bool Open(const TCHAR* user_agent,
DWORD access_type,
const TCHAR* proxy_name,
const TCHAR* proxy_bypass,
HttpHandle* session_handle) const;
virtual bool Connect(HttpHandle session_handle,
const TCHAR* server,
int port,
HttpHandle* connection_handle) const;
virtual bool OpenRequest(HttpHandle connection_handle,
const TCHAR* verb,
const TCHAR* uri,
const TCHAR* version,
const TCHAR* referrer,
bool is_secure,
HttpHandle* request_handle) const;
virtual bool SendRequest(HttpHandle request_handle,
const TCHAR* headers,
DWORD headers_length) const;
virtual bool ReceiveResponse(HttpHandle request_handle) const;
virtual bool GetHttpStatusCode(HttpHandle request_handle,
int* status_code) const;
virtual bool GetContentLength(HttpHandle request_handle,
DWORD* content_length) const;
virtual bool ReadData(HttpHandle request_handle,
void* buffer,
DWORD buffer_length,
DWORD* bytes_read) const;
virtual bool Close(HttpHandle handle) const;
private:
static DWORD MapAccessType(DWORD access_type);
static HINTERNET ToHINTERNET(HttpHandle handle);
static HttpHandle FromHINTERNET(HINTERNET handle);
};
bool WinHttpClient::CrackUrl(const TCHAR* url,
DWORD flags,
TCHAR* scheme,
size_t scheme_buffer_length,
TCHAR* host,
size_t host_buffer_length,
TCHAR* uri,
size_t uri_buffer_length,
int* port) const {
assert(url);
assert(scheme);
assert(host);
assert(uri);
assert(port);
URL_COMPONENTS url_comp = {0};
url_comp.dwStructSize = sizeof(url_comp);
url_comp.lpszScheme = scheme;
url_comp.dwSchemeLength = static_cast<DWORD>(scheme_buffer_length);
url_comp.lpszHostName = host;
url_comp.dwHostNameLength = static_cast<DWORD>(host_buffer_length);
url_comp.lpszUrlPath = uri;
url_comp.dwUrlPathLength = static_cast<DWORD>(uri_buffer_length);
bool result = !!::WinHttpCrackUrl(url, 0, flags, &url_comp);
if (result) {
*port = static_cast<int>(url_comp.nPort);
}
return result;
}
bool WinHttpClient::Open(const TCHAR* user_agent,
DWORD access_type,
const TCHAR* proxy_name,
const TCHAR* proxy_bypass,
HttpHandle* session_handle) const {
*session_handle = FromHINTERNET(::WinHttpOpen(user_agent,
MapAccessType(access_type),
proxy_name,
proxy_bypass,
0));
return !!(*session_handle);
}
bool WinHttpClient::Connect(HttpHandle session_handle,
const TCHAR* server,
int port,
HttpHandle* connection_handle) const {
assert(server);
// Uses NULL user name and password to connect.
*connection_handle = FromHINTERNET(::WinHttpConnect(
ToHINTERNET(session_handle),
server,
static_cast<INTERNET_PORT>(port),
NULL));
return !!(*connection_handle);
}
bool WinHttpClient::OpenRequest(HttpHandle connection_handle,
const TCHAR* verb,
const TCHAR* uri,
const TCHAR* version,
const TCHAR* referrer,
bool is_secure,
HttpHandle* request_handle) const {
assert(connection_handle);
assert(verb);
assert(uri);
assert(request_handle);
*request_handle = FromHINTERNET(::WinHttpOpenRequest(
ToHINTERNET(connection_handle),
verb,
uri,
version,
referrer,
WINHTTP_DEFAULT_ACCEPT_TYPES,
is_secure ? WINHTTP_FLAG_SECURE : 0));
return !!(*request_handle);
}
bool WinHttpClient::SendRequest(HttpHandle request_handle,
const TCHAR* headers,
DWORD headers_length) const {
assert(request_handle);
return !!::WinHttpSendRequest(ToHINTERNET(request_handle),
headers,
headers_length,
NULL,
0,
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH,
NULL);
}
bool WinHttpClient::ReceiveResponse(HttpHandle request_handle) const {
assert(request_handle);
return !!::WinHttpReceiveResponse(ToHINTERNET(request_handle), NULL);
}
bool WinHttpClient::GetHttpStatusCode(HttpHandle request_handle,
int* status_code) const {
TCHAR http_status_string[4] = {0};
DWORD http_status_string_size = sizeof(http_status_string);
if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle),
WINHTTP_QUERY_STATUS_CODE,
WINHTTP_HEADER_NAME_BY_INDEX,
static_cast<void*>(&http_status_string),
&http_status_string_size, 0)) {
return false;
}
*status_code = static_cast<DWORD>(_tcstol(http_status_string, NULL, 10));
return true;
}
bool WinHttpClient::GetContentLength(HttpHandle request_handle,
DWORD* content_length) const {
assert(request_handle);
assert(content_length);
TCHAR content_length_string[11] = {0};
DWORD content_length_string_size = sizeof(content_length_string);
if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle),
WINHTTP_QUERY_CONTENT_LENGTH,
WINHTTP_HEADER_NAME_BY_INDEX,
static_cast<void*>(&content_length_string),
&content_length_string_size, 0)) {
*content_length = kUnknownContentLength;
} else {
*content_length =
static_cast<DWORD>(wcstol(content_length_string, NULL, 10));
}
return true;
}
bool WinHttpClient::ReadData(HttpHandle request_handle,
void* buffer,
DWORD buffer_length,
DWORD* bytes_read) const {
assert(request_handle);
assert(buffer);
assert(bytes_read);
DWORD bytes_read_local = 0;
if (!::WinHttpReadData(ToHINTERNET(request_handle),
buffer,
buffer_length,
&bytes_read_local)) {
return false;
}
*bytes_read = bytes_read_local;
return true;
}
bool WinHttpClient::Close(HttpHandle handle) const {
assert(handle);
return !!::WinHttpCloseHandle(ToHINTERNET(handle));
}
DWORD WinHttpClient::MapAccessType(DWORD access_type) {
switch (static_cast<AccessType>(access_type)) {
case ACCESS_TYPE_PRECONFIG:
default:
return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
case ACCESS_TYPE_DIRECT:
return WINHTTP_ACCESS_TYPE_NO_PROXY;
case ACCESS_TYPE_PROXY:
return WINHTTP_ACCESS_TYPE_NAMED_PROXY;
}
}
HINTERNET WinHttpClient::ToHINTERNET(HttpHandle handle) {
return static_cast<HINTERNET>(handle);
}
HttpHandle WinHttpClient::FromHINTERNET(HINTERNET handle) {
return static_cast<HttpHandle>(handle);
}
} // namespace internal
HttpClient* CreateWinHttpClient(const TCHAR* url) {
assert(url);
internal::WinHttpClient winhttp;
wchar_t scheme[16] = {0};
wchar_t host[256] = {0};
wchar_t path[256] = {0};
int port = 0;
if (!winhttp.CrackUrl(url,
0,
scheme,
sizeof(scheme)/sizeof(scheme[0]),
host,
sizeof(host)/sizeof(host[0]),
path,
sizeof(path)/sizeof(path[0]),
&port)) {
return NULL;
}
if (_wcsicmp(scheme, L"https") == 0) {
// Winhttp under WINE doesn't support wildcard certificates, so avoid
// to use it if the scheme is https. The caller should fall back to
// use wininet if NULL is returned.
return NULL;
}
return new internal::WinHttpClient();
}
} // namespace crash

View file

@ -0,0 +1,40 @@
// 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 TOOLS_WINDOWS_CONVERTER_EXE_WINHTTP_CLIENT_H_
#define TOOLS_WINDOWS_CONVERTER_EXE_WINHTTP_CLIENT_H_
#include "tools/windows/converter_exe/http_client.h"
namespace crash {
HttpClient* CreateWinHttpClient(const TCHAR* url);
} // namespace crash
#endif // TOOLS_WINDOWS_CONVERTER_EXE_WINHTTP_CLIENT_H_

View file

@ -0,0 +1,282 @@
// 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.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include "tools/windows/converter_exe/wininet_client.h"
#include <assert.h>
#include <stdlib.h>
#include <windows.h>
#include <wininet.h>
namespace crash {
namespace internal {
// This class implements HttpClient based on WinInet APIs.
class WinInetClient : public HttpClient {
public:
virtual ~WinInetClient() {}
virtual bool CrackUrl(const TCHAR* url,
DWORD flags,
TCHAR* scheme,
size_t scheme_buffer_length,
TCHAR* host,
size_t host_buffer_length,
TCHAR* uri,
size_t uri_buffer_length,
int* port) const;
virtual bool Open(const TCHAR* user_agent,
DWORD access_type,
const TCHAR* proxy_name,
const TCHAR* proxy_bypass,
HttpHandle* session_handle) const;
virtual bool Connect(HttpHandle session_handle,
const TCHAR* server,
int port,
HttpHandle* connection_handle) const;
virtual bool OpenRequest(HttpHandle connection_handle,
const TCHAR* verb,
const TCHAR* uri,
const TCHAR* version,
const TCHAR* referrer,
bool is_secure,
HttpHandle* request_handle) const;
virtual bool SendRequest(HttpHandle request_handle,
const TCHAR* headers,
DWORD headers_length) const;
virtual bool ReceiveResponse(HttpHandle request_handle) const;
virtual bool GetHttpStatusCode(HttpHandle request_handle,
int* status_code) const;
virtual bool GetContentLength(HttpHandle request_handle,
DWORD* content_length) const;
virtual bool ReadData(HttpHandle request_handle,
void* buffer,
DWORD buffer_length,
DWORD* bytes_read) const;
virtual bool Close(HttpHandle handle) const;
private:
static DWORD MapAccessType(DWORD access_type);
static HINTERNET ToHINTERNET(HttpHandle handle);
static HttpHandle FromHINTERNET(HINTERNET handle);
};
bool WinInetClient::CrackUrl(const TCHAR* url,
DWORD flags,
TCHAR* scheme,
size_t scheme_buffer_length,
TCHAR* host,
size_t host_buffer_length,
TCHAR* uri,
size_t uri_buffer_length,
int* port) const {
assert(url);
assert(scheme);
assert(host);
assert(uri);
assert(port);
URL_COMPONENTS url_comp = {0};
url_comp.dwStructSize = sizeof(url_comp);
url_comp.lpszScheme = scheme;
url_comp.dwSchemeLength = static_cast<DWORD>(scheme_buffer_length);
url_comp.lpszHostName = host;
url_comp.dwHostNameLength = static_cast<DWORD>(host_buffer_length);
url_comp.lpszUrlPath = uri;
url_comp.dwUrlPathLength = static_cast<DWORD>(uri_buffer_length);
bool result = !!::InternetCrackUrl(url, 0, flags, &url_comp);
if (result) {
*port = static_cast<int>(url_comp.nPort);
}
return result;
}
bool WinInetClient::Open(const TCHAR* user_agent,
DWORD access_type,
const TCHAR* proxy_name,
const TCHAR* proxy_bypass,
HttpHandle* session_handle) const {
*session_handle = FromHINTERNET(::InternetOpen(user_agent,
MapAccessType(access_type),
proxy_name,
proxy_bypass,
0));
return !!(*session_handle);
}
bool WinInetClient::Connect(HttpHandle session_handle,
const TCHAR* server,
int port,
HttpHandle* connection_handle) const {
assert(server);
// Uses NULL user name and password to connect. Always uses http service.
*connection_handle = FromHINTERNET(::InternetConnect(
ToHINTERNET(session_handle),
server,
static_cast<INTERNET_PORT>(port),
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0,
0));
return !!(*connection_handle);
}
bool WinInetClient::OpenRequest(HttpHandle connection_handle,
const TCHAR* verb,
const TCHAR* uri,
const TCHAR* version,
const TCHAR* referrer,
bool is_secure,
HttpHandle* request_handle) const {
assert(connection_handle);
assert(verb);
assert(uri);
*request_handle = FromHINTERNET(::HttpOpenRequest(
ToHINTERNET(connection_handle),
verb,
uri,
version,
referrer,
NULL,
is_secure ? INTERNET_FLAG_SECURE : 0,
NULL));
return !!(*request_handle);
}
bool WinInetClient::SendRequest(HttpHandle request_handle,
const TCHAR* headers,
DWORD headers_length) const {
assert(request_handle);
return !!::HttpSendRequest(ToHINTERNET(request_handle),
headers,
headers_length,
NULL,
0);
}
bool WinInetClient::ReceiveResponse(HttpHandle) const {
return true;
}
bool WinInetClient::GetHttpStatusCode(HttpHandle request_handle,
int* status_code) const {
assert(request_handle);
TCHAR http_status_string[4] = {0};
DWORD http_status_string_size = sizeof(http_status_string);
if (!::HttpQueryInfo(ToHINTERNET(request_handle),
HTTP_QUERY_STATUS_CODE,
static_cast<void*>(&http_status_string),
&http_status_string_size,
0)) {
return false;
}
*status_code = _tcstol(http_status_string, NULL, 10);
return true;
}
bool WinInetClient::GetContentLength(HttpHandle request_handle,
DWORD* content_length) const {
assert(request_handle);
assert(content_length);
TCHAR content_length_string[11];
DWORD content_length_string_size = sizeof(content_length_string);
if (!::HttpQueryInfo(ToHINTERNET(request_handle),
HTTP_QUERY_CONTENT_LENGTH,
static_cast<void*>(&content_length_string),
&content_length_string_size,
0)) {
*content_length = kUnknownContentLength;
} else {
*content_length = wcstol(content_length_string, NULL, 10);
}
return true;
}
bool WinInetClient::ReadData(HttpHandle request_handle,
void* buffer,
DWORD buffer_length,
DWORD* bytes_read) const {
assert(request_handle);
assert(buffer);
assert(bytes_read);
DWORD bytes_read_local = 0;
if (!::InternetReadFile(ToHINTERNET(request_handle),
buffer,
buffer_length,
&bytes_read_local)) {
return false;
}
*bytes_read = bytes_read_local;
return true;
}
bool WinInetClient::Close(HttpHandle handle) const {
assert(handle);
return !!::InternetCloseHandle(ToHINTERNET(handle));
}
DWORD WinInetClient::MapAccessType(DWORD access_type) {
switch (static_cast<AccessType>(access_type)) {
case ACCESS_TYPE_PRECONFIG:
default:
return INTERNET_OPEN_TYPE_PRECONFIG;
case ACCESS_TYPE_DIRECT:
return INTERNET_OPEN_TYPE_DIRECT;
case ACCESS_TYPE_PROXY:
return INTERNET_OPEN_TYPE_PROXY;
}
}
HINTERNET WinInetClient::ToHINTERNET(HttpHandle handle) {
return static_cast<HINTERNET>(handle);
}
HttpHandle WinInetClient::FromHINTERNET(HINTERNET handle) {
return static_cast<HttpHandle>(handle);
}
} // namespace internal
HttpClient* CreateWinInetClient(const TCHAR*) {
return new internal::WinInetClient();
}
} // namespace crash

View file

@ -0,0 +1,40 @@
// 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 TOOLS_WINDOWS_CONVERTER_EXE_WININET_CLIENT_H_
#define TOOLS_WINDOWS_CONVERTER_EXE_WININET_CLIENT_H_
#include "tools/windows/converter_exe/http_client.h"
namespace crash {
HttpClient* CreateWinInetClient(const TCHAR* url);
} // namespace crash
#endif // TOOLS_WINDOWS_CONVERTER_EXE_WININET_CLIENT_H_

View file

@ -0,0 +1,86 @@
@if "%ECHOON%"=="" @echo off
SETLOCAL
REM ******************************************************************
REM Usage:
REM winsymconv
REM ******************************************************************
REM ******************************************************************
REM Initialize
REM ******************************************************************
SET SCRIPT_LOCATION=%~dp0
SET DBGHELP_WINHTTP=
SET USE_WINHTTP=
REM ******************************************************************
REM Go to script location
REM ******************************************************************
pushd %SCRIPT_LOCATION%
REM ******************************************************************
REM Make sure the symbol file directory exists
REM ******************************************************************
SET SYMBOL_DIR=%SCRIPT_LOCATION%symbol
if NOT EXIST %SYMBOL_DIR% MKDIR %SYMBOL_DIR%
if NOT EXIST %SYMBOL_DIR% echo Failed to create directory '%SYMBOL_DIR%' & goto :fail
:restart
REM ******************************************************************
REM Convert missing Windows symbols on the staging instance.
REM ******************************************************************
echo Converting missing Windows symbols on staging instance ...
google_converter.exe ^
-n http://msdl.microsoft.com/download/symbols ^
-n http://symbols.mozilla.org/firefox ^
-n http://chromium-browser-symsrv.commondatastorage.googleapis.com ^
-n https://download.amd.com/dir/bin ^
-n https://driver-symbols.nvidia.com ^
-n https://software.intel.com/sites/downloads/symbols ^
-l %SYMBOL_DIR% ^
-s https://clients2.google.com/cr/staging_symbol ^
-m https://clients2.google.com/cr/staging_symbol/missingsymbols ^
-t https://clients2.google.com/cr/staging_symbol/fetchfailed ^
-b "google|chrome|internal|private" ^
> %SCRIPT_LOCATION%last_cycle_staging.txt
REM ******************************************************************
REM Convert missing Windows symbols on the production instance.
REM ******************************************************************
echo Converting missing Windows symbols on production instance ...
google_converter.exe ^
-n http://msdl.microsoft.com/download/symbols ^
-n http://symbols.mozilla.org/firefox ^
-n http://chromium-browser-symsrv.commondatastorage.googleapis.com ^
-n https://download.amd.com/dir/bin ^
-n https://driver-symbols.nvidia.com ^
-n https://software.intel.com/sites/downloads/symbols ^
-l %SYMBOL_DIR% ^
-s https://clients2.google.com/cr/symbol ^
-m https://clients2.google.com/cr/symbol/missingsymbols ^
-t https://clients2.google.com/cr/symbol/fetchfailed ^
-b "google|chrome|internal|private" ^
> %SCRIPT_LOCATION%last_cycle_prod.txt
REM ******************************************************************
REM Sleep for 5 minutes ...
REM ******************************************************************
echo Sleeping for 5 minutes ...
%SCRIPT_LOCATION%sleep.exe 300
REM ******************************************
REM Restart work loop ...
REM ******************************************
goto :restart
:success
ENDLOCAL
exit /b 0
:fail
ENDLOCAL
exit /b 1

View file

@ -0,0 +1,72 @@
@if "%ECHOON%"=="" @echo off
SETLOCAL
REM ******************************************************************
REM Usage:
REM winsymconv_test
REM ******************************************************************
REM ******************************************************************
REM Initialize
REM ******************************************************************
SET SCRIPT_LOCATION=%~dp0
SET DBGHELP_WINHTTP=
SET USE_WINHTTP=
REM ******************************************************************
REM Go to script location
REM ******************************************************************
pushd %SCRIPT_LOCATION%
REM ******************************************************************
REM Make sure the symbol file directory exists
REM ******************************************************************
SET SYMBOL_DIR=%SCRIPT_LOCATION%symbol
if NOT EXIST %SYMBOL_DIR% MKDIR %SYMBOL_DIR%
if NOT EXIST %SYMBOL_DIR% echo Failed to create directory '%SYMBOL_DIR%' & goto :fail
REM ******************************************************************
REM Testing on the staging instance.
REM ******************************************************************
echo Testing on the staging instance ...
google_converter.exe ^
-n http://msdl.microsoft.com/download/symbols ^
-n http://symbols.mozilla.org/firefox ^
-n http://chromium-browser-symsrv.commondatastorage.googleapis.com ^
-n https://download.amd.com/dir/bin ^
-n https://driver-symbols.nvidia.com ^
-n https://software.intel.com/sites/downloads/symbols ^
-l %SYMBOL_DIR% ^
-s https://clients2.google.com/cr/staging_symbol ^
-mf %SCRIPT_LOCATION%missing_symbols_test.txt ^
-t https://clients2.google.com/cr/staging_symbol/fetchfailed ^
-b "google|chrome|internal|private" ^
> %SCRIPT_LOCATION%last_cycle_staging.txt
REM ******************************************************************
REM Testing on the production instance.
REM ******************************************************************
echo Testing on the production instance ...
google_converter.exe ^
-n http://msdl.microsoft.com/download/symbols ^
-n http://symbols.mozilla.org/firefox ^
-n http://chromium-browser-symsrv.commondatastorage.googleapis.com ^
-n https://download.amd.com/dir/bin ^
-n https://driver-symbols.nvidia.com ^
-n https://software.intel.com/sites/downloads/symbols ^
-l %SYMBOL_DIR% ^
-s https://clients2.google.com/cr/symbol ^
-mf %SCRIPT_LOCATION%missing_symbols_test.txt ^
-t https://clients2.google.com/cr/symbol/fetchfailed ^
-b "google|chrome|internal|private" ^
> %SCRIPT_LOCATION%last_cycle_prod.txt
:success
ENDLOCAL
exit /b 0
:fail
ENDLOCAL
exit /b 1

View file

@ -0,0 +1,102 @@
// Copyright 2006 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.
// Windows utility to dump the line number data from a pdb file to
// a text-based format that we can use from the minidump processor.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <stdio.h>
#include <wchar.h>
#include <memory>
#include <string>
#include "common/windows/pdb_source_line_writer.h"
#include "common/windows/pe_source_line_writer.h"
using google_breakpad::PDBSourceLineWriter;
using google_breakpad::PESourceLineWriter;
using std::unique_ptr;
using std::wstring;
int usage(const wchar_t* self) {
fprintf(stderr, "Usage: %ws [--pe] [--i] <file.[pdb|exe|dll]>\n", self);
fprintf(stderr, "Options:\n");
fprintf(stderr,
"--pe:\tRead debugging information from PE file and do "
"not attempt to locate matching PDB file.\n"
"\tThis is only supported for PE32+ (64 bit) PE files.\n");
fprintf(stderr,
"--i:\tOutput INLINE/INLINE_ORIGIN record\n"
"\tThis cannot be used with [--pe].\n");
return 1;
}
int wmain(int argc, wchar_t** argv) {
bool success = false;
bool pe = false;
bool handle_inline = false;
int arg_index = 1;
while (arg_index < argc && wcslen(argv[arg_index]) > 0 &&
wcsncmp(L"--", argv[arg_index], 2) == 0) {
if (wcscmp(L"--pe", argv[arg_index]) == 0) {
pe = true;
} else if (wcscmp(L"--i", argv[arg_index]) == 0) {
handle_inline = true;
}
++arg_index;
}
if ((pe && handle_inline) || arg_index == argc) {
usage(argv[0]);
return 1;
}
wchar_t* file_path = argv[arg_index];
if (pe) {
PESourceLineWriter pe_writer(file_path);
success = pe_writer.WriteSymbols(stdout);
} else {
PDBSourceLineWriter pdb_writer(handle_inline);
if (!pdb_writer.Open(wstring(file_path), PDBSourceLineWriter::ANY_FILE)) {
fprintf(stderr, "Open failed.\n");
return 1;
}
success = pdb_writer.WriteSymbols(stdout);
}
if (!success) {
fprintf(stderr, "WriteSymbols failed.\n");
return 1;
}
return 0;
}

View file

@ -0,0 +1,248 @@
// Copyright 2003 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.
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <Windows.h>
#include <shellapi.h>
#include <string>
#include <utility>
#include "breakpad_googletest_includes.h"
namespace tools {
namespace windows {
namespace dump_syms {
namespace {
// Root names of PDB and dumped symbol files to be regression tested. These are
// specified in complexity of the resulting dumped symbol files.
const wchar_t* kRootNames[] = {
// A PDB file with no OMAP data.
L"dump_syms_regtest",
// A PDB file with OMAP data for an image that has been function-level
// reordered.
L"omap_reorder_funcs",
// A PDB file with OMAP data for an image that had new content injected, all
// of it with source data.
L"omap_stretched_filled",
// A PDB file with OMAP data for an image that had new content injected, but
// without source data.
L"omap_stretched",
// A PDB file with OMAP data for an image that has been basic block reordered.
L"omap_reorder_bbs",
// A 64bit PDB file with no OMAP data.
L"dump_syms_regtest64",
};
const wchar_t* kPEOnlyRootNames[] = {
L"pe_only_symbol_test",
};
void TrimLastComponent(const std::wstring& path,
std::wstring* trimmed,
std::wstring* component) {
size_t len = path.size();
while (len > 0 && path[len - 1] != '\\')
--len;
if (component != NULL)
component->assign(path.c_str() + len, path.c_str() + path.size());
while (len > 0 && path[len - 1] == '\\')
--len;
if (trimmed != NULL)
trimmed->assign(path.c_str(), len);
}
// Get the directory of the current executable.
bool GetSelfDirectory(std::wstring* self_dir) {
std::wstring command_line = GetCommandLineW();
int num_args = 0;
wchar_t** args = NULL;
args = ::CommandLineToArgvW(command_line.c_str(), &num_args);
if (args == NULL)
return false;
*self_dir = args[0];
TrimLastComponent(*self_dir, self_dir, NULL);
return true;
}
void RunCommand(const std::wstring& command_line,
std::string* stdout_string) {
// Create a PIPE for the child process stdout.
HANDLE child_stdout_read = 0;
HANDLE child_stdout_write = 0;
SECURITY_ATTRIBUTES sec_attr_stdout = {};
sec_attr_stdout.nLength = sizeof(sec_attr_stdout);
sec_attr_stdout.bInheritHandle = TRUE;
ASSERT_TRUE(::CreatePipe(&child_stdout_read, &child_stdout_write,
&sec_attr_stdout, 0));
ASSERT_TRUE(::SetHandleInformation(child_stdout_read, HANDLE_FLAG_INHERIT,
0));
// Create a PIPE for the child process stdin.
HANDLE child_stdin_read = 0;
HANDLE child_stdin_write = 0;
SECURITY_ATTRIBUTES sec_attr_stdin = {};
sec_attr_stdin.nLength = sizeof(sec_attr_stdin);
sec_attr_stdin.bInheritHandle = TRUE;
ASSERT_TRUE(::CreatePipe(&child_stdin_read, &child_stdin_write,
&sec_attr_stdin, 0));
ASSERT_TRUE(::SetHandleInformation(child_stdin_write, HANDLE_FLAG_INHERIT,
0));
// Startup the child.
STARTUPINFO startup_info = {};
PROCESS_INFORMATION process_info = {};
startup_info.cb = sizeof(STARTUPINFO);
startup_info.hStdError = NULL;
startup_info.hStdInput = child_stdin_read;
startup_info.hStdOutput = child_stdout_write;
startup_info.dwFlags = STARTF_USESTDHANDLES;
ASSERT_TRUE(::CreateProcessW(NULL, (LPWSTR)command_line.c_str(), NULL, NULL,
TRUE, 0, NULL, NULL,
&startup_info, &process_info));
// Collect the output.
ASSERT_TRUE(::CloseHandle(child_stdout_write));
char buffer[4096] = {};
DWORD bytes_read = 0;
while (::ReadFile(child_stdout_read, buffer, sizeof(buffer), &bytes_read,
NULL) && bytes_read > 0) {
stdout_string->append(buffer, bytes_read);
}
// Wait for the process to finish.
::WaitForSingleObject(process_info.hProcess, INFINITE);
// Shut down all of our handles.
ASSERT_TRUE(::CloseHandle(process_info.hThread));
ASSERT_TRUE(::CloseHandle(process_info.hProcess));
ASSERT_TRUE(::CloseHandle(child_stdin_write));
ASSERT_TRUE(::CloseHandle(child_stdin_read));
ASSERT_TRUE(::CloseHandle(child_stdout_read));
}
void GetFileContents(const std::wstring& path, std::string* content) {
FILE* f = ::_wfopen(path.c_str(), L"rb");
ASSERT_TRUE(f != NULL);
char buffer[4096] = {};
while (true) {
size_t bytes_read = ::fread(buffer, 1, sizeof(buffer), f);
if (bytes_read == 0)
break;
content->append(buffer, bytes_read);
}
}
class DumpSymsRegressionTest : public testing::TestWithParam<const wchar_t*> {
public:
virtual void SetUp() {
std::wstring self_dir;
ASSERT_TRUE(GetSelfDirectory(&self_dir));
dump_syms_exe = self_dir + L"\\dump_syms.exe";
TrimLastComponent(self_dir, &testdata_dir, NULL);
testdata_dir += L"\\testdata";
}
std::wstring dump_syms_exe;
std::wstring testdata_dir;
};
class DumpSymsPEOnlyRegressionTest : public testing::TestWithParam<const wchar_t*> {
public:
virtual void SetUp() {
std::wstring self_dir;
ASSERT_TRUE(GetSelfDirectory(&self_dir));
dump_syms_exe = self_dir + L"\\dump_syms.exe";
TrimLastComponent(self_dir, &testdata_dir, NULL);
testdata_dir += L"\\testdata";
}
std::wstring dump_syms_exe;
std::wstring testdata_dir;
};
} //namespace
TEST_P(DumpSymsRegressionTest, EnsureDumpedSymbolsMatch) {
const wchar_t* root_name = GetParam();
std::wstring root_path = testdata_dir + L"\\" + root_name;
std::wstring sym_path = root_path + L".sym";
std::string expected_symbols;
ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols));
std::wstring pdb_path = root_path + L".pdb";
std::wstring command_line = L"\"" + dump_syms_exe + L"\" \"" +
pdb_path + L"\"";
std::string symbols;
ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols));
EXPECT_EQ(expected_symbols, symbols);
}
INSTANTIATE_TEST_SUITE_P(DumpSyms, DumpSymsRegressionTest,
testing::ValuesIn(kRootNames));
TEST_P(DumpSymsPEOnlyRegressionTest, EnsurePEOnlyDumpedSymbolsMatch) {
const wchar_t* root_name = GetParam();
std::wstring root_path = testdata_dir + L"\\" + root_name;
std::wstring sym_path = root_path + L".sym";
std::string expected_symbols;
ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols));
std::wstring dll_path = root_path + L".dll";
std::wstring command_line = L"\"" + dump_syms_exe + L"\" --pe \"" +
dll_path + L"\"";
std::string symbols;
ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols));
EXPECT_EQ(expected_symbols, symbols);
}
INSTANTIATE_TEST_SUITE_P(PEOnlyDumpSyms, DumpSymsPEOnlyRegressionTest,
testing::ValuesIn(kPEOnlyRootNames));
} // namespace dump_syms
} // namespace windows
} // namespace tools

View file

@ -0,0 +1,52 @@
#!/bin/sh
# Copyright 2006 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.
Release/dump_syms.exe testdata/dump_syms_regtest.pdb | \
tr -d '\015' > \
testdata/dump_syms_regtest.new
status=$?
if [ $status -ne 0 ] ; then
echo "FAIL, dump_syms.exe failed"
exit $status
fi
diff -u testdata/dump_syms_regtest.new testdata/dump_syms_regtest.sym > \
testdata/dump_syms_regtest.diff
status=$?
if [ $status -eq 0 ] ; then
rm testdata/dump_syms_regtest.diff testdata/dump_syms_regtest.new
echo "PASS"
else
echo "FAIL, see testdata/dump_syms_regtest.[new|diff]"
fi
exit $status

View file

@ -0,0 +1,75 @@
// Copyright 2007 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.
// with 32-bit cl
// cl /Zi dump_syms_regtest.cc /link /PROFILE
// dump_syms dump_syms_regtest.pdb > dump_syms_regtest.sym
// with 64-bit cl
// cp dump_syms_regtest.cc dump_syms_regtest64.cc
// cl /Zi dump_syms_regtest64.cc /link /PROFILE
// dump_syms dump_syms_regtest64.pdb > dump_syms_regtest64.sym
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
namespace google_breakpad {
class C {
public:
C() : member_(1) {}
virtual ~C() {}
void set_member(int value) { member_ = value; }
int member() const { return member_; }
void f() { member_ = g(); }
virtual int g() { return 2; }
static char* h(const C& that) { return 0; }
private:
int member_;
};
static int i() {
return 3;
}
} // namespace google_breakpad
int main(int argc, char** argv) {
google_breakpad::C object;
object.set_member(google_breakpad::i());
object.f();
int value = object.g();
char* nothing = object.h(object);
return 0;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,214 @@
MODULE windows x86_64 2A5EAB481FAB4A17A9761CDC14FE531A1 pe_only_symbol_test.pdb
INFO CODE_ID 5C8AD05F12000 pe_only_symbol_test.dll
STACK CFI INIT 1440 39 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1440 .cfa: $rsp 32 +
STACK CFI INIT 1490 7f .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1490 .cfa: $rsp 128 +
STACK CFI INIT 1520 41 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1520 .cfa: $rsp 48 +
STACK CFI INIT 1570 35 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1570 .cfa: $rsp 48 +
STACK CFI INIT 15b0 3a .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 15b0 .cfa: $rsp 48 +
STACK CFI INIT 1640 8 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1640 .cfa: $rsp 16 +
STACK CFI INIT 1650 d .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1650 .cfa: $rsp 16 +
STACK CFI INIT 1660 b .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1660 .cfa: $rsp 16 +
STACK CFI INIT 1670 5a .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1670 .cfa: $rsp 64 +
STACK CFI INIT 16e0 97 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 16e0 .cfa: $rsp 112 +
STACK CFI INIT 17c0 3f .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 17c0 .cfa: $rsp 64 +
STACK CFI INIT 1810 23 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1810 .cfa: $rsp 64 +
STACK CFI INIT 1840 16 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1840 .cfa: $rsp 16 +
STACK CFI INIT 1856 20 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1856 .cfa: $rsp 16 +
STACK CFI INIT 1876 5 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1876 .cfa: $rsp 16 +
STACK CFI INIT 1890 1b .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1890 .cfa: $rsp 48 +
STACK CFI INIT 18ab 56 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 18ab .cfa: $rsp 48 +
STACK CFI INIT 1901 10 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1901 .cfa: $rsp 48 +
STACK CFI INIT 1940 7 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1940 .cfa: $rsp 64 +
STACK CFI INIT 1947 1a .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1947 .cfa: $rsp 64 +
STACK CFI INIT 1961 b .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1961 .cfa: $rsp 64 +
STACK CFI INIT 196c 4c .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 196c .cfa: $rsp 64 +
STACK CFI INIT 19b8 5 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 19b8 .cfa: $rsp 64 +
STACK CFI INIT 19bd 13 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 19bd .cfa: $rsp 64 +
STACK CFI INIT 19d0 73 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 19d0 .cfa: $rsp 64 +
STACK CFI INIT 1a90 3a .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1a90 .cfa: $rsp 48 +
STACK CFI INIT 1ae0 f8 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1ae0 .cfa: $rsp 96 +
STACK CFI INIT 1c30 21 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1c30 .cfa: $rsp 8 +
STACK CFI INIT 1c60 87 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1c60 .cfa: $rsp 64 +
STACK CFI INIT 1d10 13a .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1d10 .cfa: $rsp 80 +
STACK CFI INIT 1ea0 88 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1ea0 .cfa: $rsp 64 +
STACK CFI INIT 1f50 135 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 1f50 .cfa: $rsp 80 +
STACK CFI INIT 20e0 4d .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 20e0 .cfa: $rsp 64 +
STACK CFI INIT 2140 2a .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2140 .cfa: $rsp 48 +
STACK CFI INIT 2180 36 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2180 .cfa: $rsp 48 +
STACK CFI INIT 2290 36 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2290 .cfa: $rsp 96 +
STACK CFI INIT 22e0 44 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 22e0 .cfa: $rsp 96 +
STACK CFI INIT 2340 5f .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2340 .cfa: $rsp 544 +
STACK CFI INIT 239f d9 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 239f .cfa: $rsp 544 +
STACK CFI INIT 2478 1d .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2478 .cfa: $rsp 544 +
STACK CFI INIT 2560 cf .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2560 .cfa: $rsp 1088 +
STACK CFI INIT 2670 2d .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2670 .cfa: $rsp 80 +
STACK CFI INIT 269d 6b .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 269d .cfa: $rsp 80 +
STACK CFI INIT 2708 1a .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2708 .cfa: $rsp 80 +
STACK CFI INIT 2770 260 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2770 .cfa: $rsp 3824 +
STACK CFI INIT 2a70 1f .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2a70 .cfa: $rsp 48 +
STACK CFI INIT 2aa0 c5 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2aa0 .cfa: $rsp 1088 +
STACK CFI INIT 2ba0 64 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2ba0 .cfa: $rsp 64 +
STACK CFI INIT 2c20 25 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2c20 .cfa: $rsp 64 +
STACK CFI INIT 2c50 35 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2c50 .cfa: $rsp 48 +
STACK CFI INIT 2ca0 d1 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2ca0 .cfa: $rsp 64 +
STACK CFI INIT 2db0 13 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2db0 .cfa: $rsp 48 +
STACK CFI INIT 2dd0 9b .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2dd0 .cfa: $rsp 48 +
STACK CFI INIT 2ea0 10e .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 2ea0 .cfa: $rsp 64 +
STACK CFI INIT 3000 91 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3000 .cfa: $rsp 128 +
STACK CFI INIT 30c0 b2 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 30c0 .cfa: $rsp 128 +
STACK CFI INIT 31a0 be .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 31a0 .cfa: $rsp 80 +
STACK CFI INIT 3290 74 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3290 .cfa: $rsp 64 +
STACK CFI INIT 3330 16 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3330 .cfa: $rsp 48 +
STACK CFI INIT 3350 15 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3350 .cfa: $rsp 48 +
STACK CFI INIT 3380 45 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3380 .cfa: $rsp 64 +
STACK CFI INIT 33e0 3b .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 33e0 .cfa: $rsp 48 +
STACK CFI INIT 3430 40 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3430 .cfa: $rsp 48 +
STACK CFI INIT 34a0 15 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 34a0 .cfa: $rsp 48 +
STACK CFI INIT 34c0 c6 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 34c0 .cfa: $rsp 64 +
STACK CFI INIT 35c0 e .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 35c0 .cfa: $rsp 48 +
STACK CFI INIT 35e0 8a .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 35e0 .cfa: $rsp 48 +
STACK CFI INIT 36a0 62 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 36a0 .cfa: $rsp 80 +
STACK CFI INIT 3720 2d .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3720 .cfa: $rsp 48 +
STACK CFI INIT 3760 1d .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3760 .cfa: $rsp 48 +
STACK CFI INIT 3790 30 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3790 .cfa: $rsp 48 +
STACK CFI INIT 37d0 15 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 37d0 .cfa: $rsp 48 +
STACK CFI INIT 37f0 5b .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 37f0 .cfa: $rsp 64 +
STACK CFI INIT 3870 2e .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3870 .cfa: $rsp 48 +
STACK CFI INIT 38b0 15 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 38b0 .cfa: $rsp 48 +
STACK CFI INIT 38d0 49 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 38d0 .cfa: $rsp 48 +
STACK CFI INIT 3930 10c .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3930 .cfa: $rsp 128 +
STACK CFI INIT 3a80 8b .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3a80 .cfa: $rsp 96 +
STACK CFI INIT 3b30 2f .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3b30 .cfa: $rsp 48 +
STACK CFI INIT 3b70 3f .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3b70 .cfa: $rsp 48 +
STACK CFI INIT 3bc0 82 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3bc0 .cfa: $rsp 80 +
STACK CFI INIT 3c70 50 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3c70 .cfa: $rsp 64 +
STACK CFI INIT 3ce0 33 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3ce0 .cfa: $rsp 64 +
STACK CFI INIT 3d50 191 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3d50 .cfa: $rsp 1536 +
STACK CFI INIT 3f50 51 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3f50 .cfa: $rsp 176 +
STACK CFI INIT 3fc0 e .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3fc0 .cfa: $rsp 48 +
STACK CFI INIT 3ff0 a6 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 3ff0 .cfa: $rsp 64 +
STACK CFI INIT 40c0 16 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 40c0 .cfa: $rsp 48 +
STACK CFI INIT 40f0 72 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 40f0 .cfa: $rsp 64 +
STACK CFI INIT 4180 42 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 4180 .cfa: $rsp 48 +
STACK CFI INIT 41e0 42 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 41e0 .cfa: $rsp 48 +
STACK CFI INIT 4250 1e .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 4250 .cfa: $rsp 32 +
STACK CFI INIT 4280 18 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 4280 .cfa: $rsp 48 +
STACK CFI INIT 42a0 37 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 42a0 .cfa: $rsp 64 +
STACK CFI INIT 4300 145 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 4300 .cfa: $rsp 1120 +
STACK CFI INIT 44a0 2ae .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 44a0 .cfa: $rsp 640 +
STACK CFI INIT 4800 103 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 4800 .cfa: $rsp 1664 +
STACK CFI INIT 4950 3c5 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 4950 .cfa: $rsp 240 +
STACK CFI INIT 4e10 36d .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 4e10 .cfa: $rsp 96 +
STACK CFI INIT 5270 25 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 5270 .cfa: $rsp 32 +
STACK CFI INIT 66c0 2 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 66c0 .cfa: $rsp 8 +
STACK CFI INIT 76d0 1a .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 76d0 .cfa: $rsp 48 +
STACK CFI INIT 76f0 20 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 76f0 .cfa: $rsp 48 +
STACK CFI INIT 7720 48 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 7720 .cfa: $rsp 64 +
STACK CFI INIT 7780 20 .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 7780 .cfa: $rsp 48 +
STACK CFI INIT 77b0 3d .cfa: $rsp .ra: .cfa 8 - ^
STACK CFI 77b0 .cfa: $rsp 48 +

View file

@ -0,0 +1,23 @@
REM This batch file is meant to facilitate regenerating prebuilt binaries for
REM the Windows tools.
REM You MUST run it from a Visual Studio xxxx Command Prompt. To do this,
REM navigate to:
REM
REM Start->Programs->Microsoft Visual Studio XXXX->Tools->
REM Visual Studio Command Prompt
REM
REM Then run this batch file. It performs an SVN update, edits the
REM README.binaries file to contain
REM the revision number, and builds the tools. You must run 'svn commit' to
REM commit the pending edits to the repository.
pushd %~dp0
if %VisualStudioVersion% == 14.0 set GYP_MSVS_VERSION=2015
gyp tools_windows.gyp
msbuild tools_windows.sln /p:Configuration=Release /t:Clean,Build
copy Release\symupload.exe binaries\
copy Release\dump_syms.exe binaries\
git add binaries
git commit -m "Built Windows binaries"
echo Done!
popd

View file

@ -0,0 +1,316 @@
// Copyright 2006 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.
// Tool to upload an exe/dll and its associated symbols to an HTTP server.
// The PDB file is located automatically, using the path embedded in the
// executable. The upload is sent as a multipart/form-data POST request,
// with the following parameters:
// code_file: the basename of the module, e.g. "app.exe"
// debug_file: the basename of the debugging file, e.g. "app.pdb"
// debug_identifier: the debug file's identifier, usually consisting of
// the guid and age embedded in the pdb, e.g.
// "11111111BBBB3333DDDD555555555555F"
// product: the HTTP-friendly product name, e.g. "MyApp"
// version: the file version of the module, e.g. "1.2.3.4"
// os: the operating system that the module was built for, always
// "windows" in this implementation.
// cpu: the CPU that the module was built for, typically "x86".
// symbol_file: the contents of the breakpad-format symbol file
#ifdef HAVE_CONFIG_H
#include <config.h> // Must come first
#endif
#include <windows.h>
#include <dbghelp.h>
#include <wininet.h>
#include <cstdio>
#include <map>
#include <string>
#include <vector>
#include "common/windows/string_utils-inl.h"
#include "common/windows/http_upload.h"
#include "common/windows/pdb_source_line_writer.h"
#include "common/windows/sym_upload_v2_protocol.h"
#include "common/windows/symbol_collector_client.h"
using google_breakpad::HTTPUpload;
using google_breakpad::PDBModuleInfo;
using google_breakpad::PDBSourceLineWriter;
using google_breakpad::WindowsStringUtils;
using std::map;
using std::string;
using std::vector;
using std::wstring;
const wchar_t* kSymbolUploadTypeBreakpad = L"BREAKPAD";
// Extracts the file version information for the given filename,
// as a string, for example, "1.2.3.4". Returns true on success.
static bool GetFileVersionString(const wchar_t* filename, wstring* version) {
DWORD handle;
DWORD version_size = GetFileVersionInfoSize(filename, &handle);
if (version_size < sizeof(VS_FIXEDFILEINFO)) {
return false;
}
vector<char> version_info(version_size);
if (!GetFileVersionInfo(filename, handle, version_size, &version_info[0])) {
return false;
}
void* file_info_buffer = NULL;
unsigned int file_info_length;
if (!VerQueryValue(&version_info[0], L"\\",
&file_info_buffer, &file_info_length)) {
return false;
}
// The maximum value of each version component is 65535 (0xffff),
// so the max length is 24, including the terminating null.
wchar_t ver_string[24];
VS_FIXEDFILEINFO* file_info =
reinterpret_cast<VS_FIXEDFILEINFO*>(file_info_buffer);
swprintf(ver_string, sizeof(ver_string) / sizeof(ver_string[0]),
L"%d.%d.%d.%d",
file_info->dwFileVersionMS >> 16,
file_info->dwFileVersionMS & 0xffff,
file_info->dwFileVersionLS >> 16,
file_info->dwFileVersionLS & 0xffff);
// remove when VC++7.1 is no longer supported
ver_string[sizeof(ver_string) / sizeof(ver_string[0]) - 1] = L'\0';
*version = ver_string;
return true;
}
// Creates a new temporary file and writes the symbol data from the given
// exe/dll file to it. Returns the path to the temp file in temp_file_path
// and information about the pdb in pdb_info.
static bool DumpSymbolsToTempFile(const wchar_t* file,
wstring* temp_file_path,
PDBModuleInfo* pdb_info,
bool handle_inline) {
google_breakpad::PDBSourceLineWriter writer(handle_inline);
// Use EXE_FILE to get information out of the exe/dll in addition to the
// pdb. The name and version number of the exe/dll are of value, and
// there's no way to locate an exe/dll given a pdb.
if (!writer.Open(file, PDBSourceLineWriter::EXE_FILE)) {
return false;
}
wchar_t temp_path[_MAX_PATH];
if (GetTempPath(_MAX_PATH, temp_path) == 0) {
return false;
}
wchar_t temp_filename[_MAX_PATH];
if (GetTempFileName(temp_path, L"sym", 0, temp_filename) == 0) {
return false;
}
FILE* temp_file = NULL;
#if _MSC_VER >= 1400 // MSVC 2005/8
if (_wfopen_s(&temp_file, temp_filename, L"w") != 0)
#else // _MSC_VER >= 1400
// _wfopen_s was introduced in MSVC8. Use _wfopen for earlier environments.
// Don't use it with MSVC8 and later, because it's deprecated.
if (!(temp_file = _wfopen(temp_filename, L"w")))
#endif // _MSC_VER >= 1400
{
return false;
}
bool success = writer.WriteSymbols(temp_file);
fclose(temp_file);
if (!success) {
_wunlink(temp_filename);
return false;
}
*temp_file_path = temp_filename;
return writer.GetModuleInfo(pdb_info);
}
__declspec(noreturn) void printUsageAndExit() {
wprintf(L"Usage:\n\n"
L" symupload [--i] [--timeout NN] [--product product_name] ^\n"
L" <file.exe|file.dll> <symbol upload URL> ^\n"
L" [...<symbol upload URLs>]\n\n");
wprintf(L" - i: Extract inline information from pdb.\n");
wprintf(L" - Timeout is in milliseconds, or can be 0 to be unlimited.\n");
wprintf(L" - product_name is an HTTP-friendly product name. It must only\n"
L" contain an ascii subset: alphanumeric and punctuation.\n"
L" This string is case-sensitive.\n\n");
wprintf(L"Example:\n\n"
L" symupload.exe --timeout 0 --product Chrome ^\n"
L" chrome.dll http://no.free.symbol.server.for.you\n");
wprintf(L"\n");
wprintf(L"sym-upload-v2 usage:\n"
L" symupload -p [-f] <file.exe|file.dll> <API-URL> <API-key>\n");
wprintf(L"\n");
wprintf(L"sym_upload_v2 Options:\n");
wprintf(L" <API-URL> is the sym_upload_v2 API URL.\n");
wprintf(L" <API-key> is a secret used to authenticate with the API.\n");
wprintf(L" -p:\t Use sym_upload_v2 protocol.\n");
wprintf(L" -f:\t Force symbol upload if already exists.\n");
exit(0);
}
int wmain(int argc, wchar_t* argv[]) {
const wchar_t* module;
const wchar_t* product = nullptr;
bool handle_inline = false;
int timeout = -1;
int currentarg = 1;
bool use_sym_upload_v2 = false;
bool force = false;
const wchar_t* api_url = nullptr;
const wchar_t* api_key = nullptr;
while (argc > currentarg + 1) {
if (!wcscmp(L"--i", argv[currentarg])) {
handle_inline = true;
++currentarg;
continue;
}
if (!wcscmp(L"--timeout", argv[currentarg])) {
timeout = _wtoi(argv[currentarg + 1]);
currentarg += 2;
continue;
}
if (!wcscmp(L"--product", argv[currentarg])) {
product = argv[currentarg + 1];
currentarg += 2;
continue;
}
if (!wcscmp(L"-p", argv[currentarg])) {
use_sym_upload_v2 = true;
++currentarg;
continue;
}
if (!wcscmp(L"-f", argv[currentarg])) {
force = true;
++currentarg;
continue;
}
break;
}
if (argc >= currentarg + 2)
module = argv[currentarg++];
else
printUsageAndExit();
wstring symbol_file;
PDBModuleInfo pdb_info;
if (!DumpSymbolsToTempFile(module, &symbol_file, &pdb_info, handle_inline)) {
fwprintf(stderr, L"Could not get symbol data from %s\n", module);
return 1;
}
wstring code_file = WindowsStringUtils::GetBaseName(wstring(module));
wstring file_version;
// Don't make a missing version a hard error. Issue a warning, and let the
// server decide whether to reject files without versions.
if (!GetFileVersionString(module, &file_version)) {
fwprintf(stderr, L"Warning: Could not get file version for %s\n", module);
}
bool success = true;
if (use_sym_upload_v2) {
if (argc >= currentarg + 2) {
api_url = argv[currentarg++];
api_key = argv[currentarg++];
wstring product_name = product ? wstring(product) : L"";
success = google_breakpad::SymUploadV2ProtocolSend(
api_url, api_key, timeout == -1 ? nullptr : &timeout,
pdb_info.debug_file, pdb_info.debug_identifier, symbol_file,
kSymbolUploadTypeBreakpad, product_name, force);
} else {
printUsageAndExit();
}
} else {
map<wstring, wstring> parameters;
parameters[L"code_file"] = code_file;
parameters[L"debug_file"] = pdb_info.debug_file;
parameters[L"debug_identifier"] = pdb_info.debug_identifier;
parameters[L"os"] = L"windows"; // This version of symupload is Windows-only
parameters[L"cpu"] = pdb_info.cpu;
map<wstring, wstring> files;
files[L"symbol_file"] = symbol_file;
if (!file_version.empty()) {
parameters[L"version"] = file_version;
}
// Don't make a missing product name a hard error. Issue a warning and let
// the server decide whether to reject files without product name.
if (product) {
parameters[L"product"] = product;
}
else {
fwprintf(
stderr,
L"Warning: No product name (flag --product) was specified for %s\n",
module);
}
while (currentarg < argc) {
int response_code;
if (!HTTPUpload::SendMultipartPostRequest(argv[currentarg], parameters, files,
timeout == -1 ? NULL : &timeout,
nullptr, &response_code)) {
success = false;
fwprintf(stderr,
L"Symbol file upload to %s failed. Response code = %ld\n",
argv[currentarg], response_code);
}
currentarg++;
}
}
_wunlink(symbol_file.c_str());
if (success) {
wprintf(L"Uploaded breakpad symbols for windows-%s/%s/%s (%s %s)\n",
pdb_info.cpu.c_str(), pdb_info.debug_file.c_str(),
pdb_info.debug_identifier.c_str(), code_file.c_str(),
file_version.c_str());
}
return success ? 0 : 1;
}