summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThanos Doukoudakis <thanos.doukoudakis@isode.com>2017-10-16 11:53:42 (GMT)
committerKevin Smith <kevin.smith@isode.com>2017-10-19 09:22:57 (GMT)
commiteb07238e9c1a09a640dae06e8a433d7dba77f490 (patch)
tree51b618bbdf933bcca2b8deb915e131c48d80d5f7 /3rdParty/Breakpad/src/common
parent80ef26c165a08d5251d7ee56e0bd07b86fc82f55 (diff)
downloadswift-eb07238e9c1a09a640dae06e8a433d7dba77f490.zip
swift-eb07238e9c1a09a640dae06e8a433d7dba77f490.tar.bz2
Upgrade Breakpad to latest
This commit will upgrade breakpad to version 1.0.86 (I9957f27cd134f862b9831e4b1d90f8a014eb37b6) from https://chromium.googlesource.com/breakpad/breakpad Added a script(BreakpadSwiftCleanup.sh) that remove files from Breakpad's repository that are not used by Swift. This commit also re-applies the changes that were introduced in commit 7f0fe603be200c09c74cf9cc295a972f3c3dbdfd, that change the minidump filename format to include version and date Test-Information: https://travis-ci.org/google/breakpad/builds/283789304 https://ci.appveyor.com/project/vapier/breakpad/build/job/1bu73ysmcfpwg9e4 Tested by adding some code that forces a crash to the client on Windows 10 with VS2015. Verified the name and contents of the generated crash dump. Change-Id: Ied9e74088e43137845edc09d070c2c27494aeade
Diffstat (limited to '3rdParty/Breakpad/src/common')
-rw-r--r--3rdParty/Breakpad/src/common/basictypes.h58
-rw-r--r--3rdParty/Breakpad/src/common/byte_cursor_unittest.cc776
-rw-r--r--3rdParty/Breakpad/src/common/convert_UTF.c53
-rw-r--r--3rdParty/Breakpad/src/common/convert_UTF.h53
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/bytereader-inl.h29
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/bytereader.cc11
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/bytereader.h31
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/bytereader_unittest.cc707
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/cfi_assembler.cc204
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/cfi_assembler.h271
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.cc10
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.h18
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc527
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/dwarf2enums.h37
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/dwarf2reader.cc617
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/dwarf2reader.h449
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc2555
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc487
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/dwarf2reader_test_common.h149
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/elf_reader.cc1273
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/elf_reader.h166
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/functioninfo.cc231
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/functioninfo.h188
-rw-r--r--3rdParty/Breakpad/src/common/dwarf/types.h8
-rw-r--r--3rdParty/Breakpad/src/common/dwarf_cfi_to_module.cc39
-rw-r--r--3rdParty/Breakpad/src/common/dwarf_cfi_to_module.h12
-rw-r--r--3rdParty/Breakpad/src/common/dwarf_cfi_to_module_unittest.cc306
-rw-r--r--3rdParty/Breakpad/src/common/dwarf_cu_to_module.cc336
-rw-r--r--3rdParty/Breakpad/src/common/dwarf_cu_to_module.h105
-rw-r--r--3rdParty/Breakpad/src/common/dwarf_cu_to_module_unittest.cc1852
-rw-r--r--3rdParty/Breakpad/src/common/dwarf_line_to_module.cc27
-rw-r--r--3rdParty/Breakpad/src/common/dwarf_line_to_module.h8
-rw-r--r--3rdParty/Breakpad/src/common/dwarf_line_to_module_unittest.cc391
-rw-r--r--3rdParty/Breakpad/src/common/language.cc130
-rw-r--r--3rdParty/Breakpad/src/common/language.h17
-rw-r--r--3rdParty/Breakpad/src/common/linux/linux_libc_support.h17
-rw-r--r--3rdParty/Breakpad/src/common/mac/Breakpad.xcconfig52
-rw-r--r--3rdParty/Breakpad/src/common/mac/BreakpadDebug.xcconfig32
-rw-r--r--3rdParty/Breakpad/src/common/mac/BreakpadRelease.xcconfig34
-rw-r--r--3rdParty/Breakpad/src/common/mac/GTMDefines.h456
-rw-r--r--3rdParty/Breakpad/src/common/mac/GTMLogger.h504
-rw-r--r--3rdParty/Breakpad/src/common/mac/GTMLogger.m611
-rw-r--r--3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.h61
-rw-r--r--3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.m267
-rw-r--r--3rdParty/Breakpad/src/common/mac/MachIPC.h8
-rw-r--r--3rdParty/Breakpad/src/common/mac/arch_utilities.cc231
-rw-r--r--3rdParty/Breakpad/src/common/mac/arch_utilities.h47
-rw-r--r--3rdParty/Breakpad/src/common/mac/byteswap.h25
-rw-r--r--3rdParty/Breakpad/src/common/mac/dump_syms.cc (renamed from 3rdParty/Breakpad/src/common/mac/dump_syms.mm)476
-rw-r--r--3rdParty/Breakpad/src/common/mac/dump_syms.h76
-rw-r--r--3rdParty/Breakpad/src/common/mac/file_id.cc19
-rw-r--r--3rdParty/Breakpad/src/common/mac/file_id.h17
-rw-r--r--3rdParty/Breakpad/src/common/mac/launch_reporter.cc84
-rw-r--r--3rdParty/Breakpad/src/common/mac/launch_reporter.h43
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_id.cc65
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_id.h33
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_reader.cc47
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_reader.h21
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_reader_unittest.cc1902
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_utilities.cc89
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_utilities.h35
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_walker.cc69
-rw-r--r--3rdParty/Breakpad/src/common/mac/macho_walker.h26
-rw-r--r--3rdParty/Breakpad/src/common/mac/string_utilities.cc2
-rw-r--r--3rdParty/Breakpad/src/common/mac/super_fat_arch.h88
-rw-r--r--3rdParty/Breakpad/src/common/md5.cc8
-rw-r--r--3rdParty/Breakpad/src/common/md5.h2
-rw-r--r--3rdParty/Breakpad/src/common/memory.h193
-rw-r--r--3rdParty/Breakpad/src/common/memory_range.h145
-rw-r--r--3rdParty/Breakpad/src/common/memory_range_unittest.cc193
-rw-r--r--3rdParty/Breakpad/src/common/memory_unittest.cc124
-rw-r--r--3rdParty/Breakpad/src/common/minidump_type_helper.h56
-rw-r--r--3rdParty/Breakpad/src/common/module.cc125
-rw-r--r--3rdParty/Breakpad/src/common/module.h38
-rw-r--r--3rdParty/Breakpad/src/common/module_unittest.cc557
-rw-r--r--3rdParty/Breakpad/src/common/scoped_ptr.h404
-rw-r--r--3rdParty/Breakpad/src/common/simple_string_dictionary.cc45
-rw-r--r--3rdParty/Breakpad/src/common/simple_string_dictionary.h260
-rw-r--r--3rdParty/Breakpad/src/common/simple_string_dictionary_unittest.cc308
-rw-r--r--3rdParty/Breakpad/src/common/stabs_reader.h5
-rw-r--r--3rdParty/Breakpad/src/common/stabs_reader_unittest.cc611
-rw-r--r--3rdParty/Breakpad/src/common/stabs_to_module.cc7
-rw-r--r--3rdParty/Breakpad/src/common/stabs_to_module_unittest.cc258
-rw-r--r--3rdParty/Breakpad/src/common/stdio_wrapper.h43
-rw-r--r--3rdParty/Breakpad/src/common/string_conversion.cc42
-rw-r--r--3rdParty/Breakpad/src/common/string_conversion.h10
-rw-r--r--3rdParty/Breakpad/src/common/symbol_data.h42
-rw-r--r--3rdParty/Breakpad/src/common/test_assembler.cc359
-rw-r--r--3rdParty/Breakpad/src/common/test_assembler.h484
-rw-r--r--3rdParty/Breakpad/src/common/test_assembler_unittest.cc1662
-rw-r--r--3rdParty/Breakpad/src/common/unordered.h62
-rw-r--r--3rdParty/Breakpad/src/common/windows/dia_util.cc92
-rw-r--r--3rdParty/Breakpad/src/common/windows/dia_util.h64
-rw-r--r--3rdParty/Breakpad/src/common/windows/guid_string.h8
-rw-r--r--3rdParty/Breakpad/src/common/windows/http_upload.cc420
-rw-r--r--3rdParty/Breakpad/src/common/windows/http_upload.h129
-rw-r--r--3rdParty/Breakpad/src/common/windows/omap.cc716
-rw-r--r--3rdParty/Breakpad/src/common/windows/omap.h72
-rw-r--r--3rdParty/Breakpad/src/common/windows/omap_internal.h140
-rw-r--r--3rdParty/Breakpad/src/common/windows/omap_unittest.cc329
-rw-r--r--3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.cc1412
-rw-r--r--3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.h257
-rw-r--r--3rdParty/Breakpad/src/common/windows/string_utils-inl.h6
-rw-r--r--3rdParty/Breakpad/src/common/windows/string_utils.cc133
104 files changed, 26411 insertions, 978 deletions
diff --git a/3rdParty/Breakpad/src/common/basictypes.h b/3rdParty/Breakpad/src/common/basictypes.h
new file mode 100644
index 0000000..9426c1f
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/basictypes.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef COMMON_BASICTYPES_H_
+#define COMMON_BASICTYPES_H_
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#ifndef DISALLOW_COPY_AND_ASSIGN
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+#endif // DISALLOW_COPY_AND_ASSIGN
+
+namespace google_breakpad {
+
+// Used to explicitly mark the return value of a function as unused. If you are
+// really sure you don't want to do anything with the return value of a function
+// that has been marked with __attribute__((warn_unused_result)), wrap it with
+// this. Example:
+//
+// scoped_ptr<MyType> my_var = ...;
+// if (TakeOwnership(my_var.get()) == SUCCESS)
+// ignore_result(my_var.release());
+//
+template<typename T>
+inline void ignore_result(const T&) {
+}
+
+} // namespace google_breakpad
+
+#endif // COMMON_BASICTYPES_H_
diff --git a/3rdParty/Breakpad/src/common/byte_cursor_unittest.cc b/3rdParty/Breakpad/src/common/byte_cursor_unittest.cc
new file mode 100644
index 0000000..06bfd89
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/byte_cursor_unittest.cc
@@ -0,0 +1,776 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// byte_cursor_unittest.cc: Unit tests for google_breakpad::ByteBuffer
+// and google_breakpad::ByteCursor.
+
+#include <string>
+
+#include <string.h>
+
+#include "breakpad_googletest_includes.h"
+#include "common/byte_cursor.h"
+#include "common/using_std_string.h"
+
+using google_breakpad::ByteBuffer;
+using google_breakpad::ByteCursor;
+
+TEST(Buffer, SizeOfNothing) {
+ uint8_t data[1];
+ ByteBuffer buffer(data, 0);
+ EXPECT_EQ(0U, buffer.Size());
+}
+
+TEST(Buffer, SizeOfSomething) {
+ uint8_t data[10];
+ ByteBuffer buffer(data, sizeof(data));
+ EXPECT_EQ(10U, buffer.Size());
+}
+
+TEST(Extent, AvailableEmpty) {
+ uint8_t data[1];
+ ByteBuffer buffer(data, 0);
+ ByteCursor cursor(&buffer);
+ EXPECT_EQ(0U, cursor.Available());
+}
+
+TEST(Extent, AtEndEmpty) {
+ uint8_t data[1];
+ ByteBuffer buffer(data, 0);
+ ByteCursor cursor(&buffer);
+ EXPECT_TRUE(cursor.AtEnd());
+}
+
+TEST(Extent, AsBoolEmpty) {
+ uint8_t data[1];
+ ByteBuffer buffer(data, 0);
+ ByteCursor cursor(&buffer);
+ EXPECT_TRUE(cursor);
+}
+
+TEST(Extent, AvailableSome) {
+ uint8_t data[10];
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ EXPECT_EQ(10U, cursor.Available());
+}
+
+TEST(Extent, AtEndSome) {
+ uint8_t data[10];
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ EXPECT_FALSE(cursor.AtEnd());
+ EXPECT_TRUE(cursor.Skip(sizeof(data)).AtEnd());
+}
+
+TEST(Extent, AsBoolSome) {
+ uint8_t data[10];
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ EXPECT_TRUE(cursor);
+ EXPECT_TRUE(cursor.Skip(sizeof(data)));
+ EXPECT_FALSE(cursor.Skip(1));
+}
+
+TEST(Extent, Cursor) {
+ uint8_t data[] = { 0xf7,
+ 0x9f, 0xbe,
+ 0x67, 0xfb, 0xd3, 0x58,
+ 0x6f, 0x36, 0xde, 0xd1,
+ 0x2a, 0x2a, 0x2a };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+
+ uint8_t a;
+ uint16_t b;
+ uint32_t c;
+ uint32_t d;
+ uint8_t stars[3];
+
+ EXPECT_EQ(data + 0U, cursor.here());
+
+ EXPECT_TRUE(cursor >> a);
+ EXPECT_EQ(data + 1U, cursor.here());
+
+ EXPECT_TRUE(cursor >> b);
+ EXPECT_EQ(data + 3U, cursor.here());
+
+ EXPECT_TRUE(cursor >> c);
+ EXPECT_EQ(data + 7U, cursor.here());
+
+ EXPECT_TRUE(cursor.Skip(4));
+ EXPECT_EQ(data + 11U, cursor.here());
+
+ EXPECT_TRUE(cursor.Read(stars, 3));
+ EXPECT_EQ(data + 14U, cursor.here());
+
+ EXPECT_FALSE(cursor >> d);
+ EXPECT_EQ(data + 14U, cursor.here());
+}
+
+TEST(Extent, SetOffset) {
+ uint8_t data[] = { 0x5c, 0x79, 0x8c, 0xd5 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+
+ uint8_t a, b, c, d, e;
+ EXPECT_TRUE(cursor >> a);
+ EXPECT_EQ(0x5cU, a);
+ EXPECT_EQ(data + 1U, cursor.here());
+ EXPECT_TRUE(((cursor >> b).set_here(data + 3) >> c).set_here(data + 1)
+ >> d >> e);
+ EXPECT_EQ(0x79U, b);
+ EXPECT_EQ(0xd5U, c);
+ EXPECT_EQ(0x79U, d);
+ EXPECT_EQ(0x8cU, e);
+ EXPECT_EQ(data + 3U, cursor.here());
+}
+
+TEST(BigEndian, Signed1) {
+ uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ cursor.set_big_endian(true);
+ int a, b, c, d, e;
+ ASSERT_TRUE(cursor
+ .Read(1, true, &a)
+ .Read(1, true, &b)
+ .Read(1, true, &c)
+ .Read(1, true, &d));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7f, b);
+ EXPECT_EQ(-0x80, c);
+ EXPECT_EQ(-1, d);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(1, true, &e));
+}
+
+TEST(BigEndian, Signed2) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff,
+ 0x80, 0x00, 0x80, 0x80, 0xff, 0xff,
+ 0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer, true);
+ int a, b, c, d, e, f, g, h, i, j;
+ ASSERT_TRUE(cursor
+ .Read(2, true, &a)
+ .Read(2, true, &b)
+ .Read(2, true, &c)
+ .Read(2, true, &d)
+ .Read(2, true, &e)
+ .Read(2, true, &f)
+ .Read(2, true, &g)
+ .Read(2, true, &h)
+ .Read(2, true, &i));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x80, b);
+ EXPECT_EQ(0x7fff, c);
+ EXPECT_EQ(-0x8000, d);
+ EXPECT_EQ(-0x7f80, e);
+ EXPECT_EQ(-1, f);
+ EXPECT_EQ(0x39f1, g);
+ EXPECT_EQ(-0x7544, h);
+ EXPECT_EQ(0x5aec, i);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(2, true, &j));
+}
+
+TEST(BigEndian, Signed4) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x80, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xb6, 0xb1, 0xff, 0xef,
+ 0x19, 0x6a, 0xca, 0x46 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ cursor.set_big_endian(true);
+ int64_t a, b, c, d, e, f, g;
+ ASSERT_TRUE(cursor
+ .Read(4, true, &a)
+ .Read(4, true, &b)
+ .Read(4, true, &c)
+ .Read(4, true, &d)
+ .Read(4, true, &e)
+ .Read(4, true, &f));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7fffffff, b);
+ EXPECT_EQ(-0x80000000LL, c);
+ EXPECT_EQ(-1, d);
+ EXPECT_EQ((int32_t) 0xb6b1ffef, e);
+ EXPECT_EQ(0x196aca46, f);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(4, true, &g));
+}
+
+TEST(BigEndian, Signed8) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c,
+ 0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer, true);
+ int64_t a, b, c, d, e, f, g;
+ ASSERT_TRUE(cursor
+ .Read(8, true, &a)
+ .Read(8, true, &b)
+ .Read(8, true, &c)
+ .Read(8, true, &d)
+ .Read(8, true, &e)
+ .Read(8, true, &f));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7fffffffffffffffLL, b);
+ EXPECT_EQ(-0x7fffffffffffffffLL - 1, c);
+ EXPECT_EQ(-1, d);
+ EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e);
+ EXPECT_EQ(0x4e4249d27f8414a4LL, f);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(8, true, &g));
+}
+
+TEST(BigEndian, Unsigned1) {
+ uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ cursor.set_big_endian(true);
+ int32_t a, b, c, d, e;
+ ASSERT_TRUE(cursor
+ .Read(1, false, &a)
+ .Read(1, false, &b)
+ .Read(1, false, &c)
+ .Read(1, false, &d));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7f, b);
+ EXPECT_EQ(0x80, c);
+ EXPECT_EQ(0xff, d);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(1, false, &e));
+}
+
+TEST(BigEndian, Unsigned2) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff,
+ 0x80, 0x00, 0x80, 0x80, 0xff, 0xff,
+ 0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer, true);
+ int64_t a, b, c, d, e, f, g, h, i, j;
+ ASSERT_TRUE(cursor
+ .Read(2, false, &a)
+ .Read(2, false, &b)
+ .Read(2, false, &c)
+ .Read(2, false, &d)
+ .Read(2, false, &e)
+ .Read(2, false, &f)
+ .Read(2, false, &g)
+ .Read(2, false, &h)
+ .Read(2, false, &i));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x80, b);
+ EXPECT_EQ(0x7fff, c);
+ EXPECT_EQ(0x8000, d);
+ EXPECT_EQ(0x8080, e);
+ EXPECT_EQ(0xffff, f);
+ EXPECT_EQ(0x39f1, g);
+ EXPECT_EQ(0x8abc, h);
+ EXPECT_EQ(0x5aec, i);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(2, false, &j));
+}
+
+TEST(BigEndian, Unsigned4) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x80, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xb6, 0xb1, 0xff, 0xef,
+ 0x19, 0x6a, 0xca, 0x46 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ cursor.set_big_endian(true);
+ int64_t a, b, c, d, e, f, g;
+ ASSERT_TRUE(cursor
+ .Read(4, false, &a)
+ .Read(4, false, &b)
+ .Read(4, false, &c)
+ .Read(4, false, &d)
+ .Read(4, false, &e)
+ .Read(4, false, &f));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7fffffff, b);
+ EXPECT_EQ(0x80000000, c);
+ EXPECT_EQ(0xffffffff, d);
+ EXPECT_EQ(0xb6b1ffef, e);
+ EXPECT_EQ(0x196aca46, f);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(4, false, &g));
+}
+
+TEST(BigEndian, Unsigned8) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c,
+ 0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer, true);
+ uint64_t a, b, c, d, e, f, g;
+ ASSERT_TRUE(cursor
+ .Read(8, false, &a)
+ .Read(8, false, &b)
+ .Read(8, false, &c)
+ .Read(8, false, &d)
+ .Read(8, false, &e)
+ .Read(8, false, &f));
+ EXPECT_EQ(0U, a);
+ EXPECT_EQ(0x7fffffffffffffffULL, b);
+ EXPECT_EQ(0x8000000000000000ULL, c);
+ EXPECT_EQ(0xffffffffffffffffULL, d);
+ EXPECT_EQ(0x9320d5e9d2d5879cULL, e);
+ EXPECT_EQ(0x4e4249d27f8414a4ULL, f);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(8, false, &g));
+}
+
+TEST(LittleEndian, Signed1) {
+ uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ int32_t a, b, c, d, e;
+ ASSERT_TRUE(cursor
+ .Read(1, true, &a)
+ .Read(1, true, &b)
+ .Read(1, true, &c)
+ .Read(1, true, &d));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7f, b);
+ EXPECT_EQ(-0x80, c);
+ EXPECT_EQ(-1, d);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(1, true, &e));
+}
+
+TEST(LittleEndian, Signed2) {
+ uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f,
+ 0x00, 0x80, 0x80, 0x80, 0xff, 0xff,
+ 0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer, false);
+ int32_t a, b, c, d, e, f, g, h, i, j;
+ ASSERT_TRUE(cursor
+ .Read(2, true, &a)
+ .Read(2, true, &b)
+ .Read(2, true, &c)
+ .Read(2, true, &d)
+ .Read(2, true, &e)
+ .Read(2, true, &f)
+ .Read(2, true, &g)
+ .Read(2, true, &h)
+ .Read(2, true, &i));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x80, b);
+ EXPECT_EQ(0x7fff, c);
+ EXPECT_EQ(-0x8000, d);
+ EXPECT_EQ(-0x7f80, e);
+ EXPECT_EQ(-1, f);
+ EXPECT_EQ(0x39f1, g);
+ EXPECT_EQ(-0x7544, h);
+ EXPECT_EQ(0x5aec, i);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(2, true, &j));
+}
+
+TEST(LittleEndian, Signed4) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0x7f,
+ 0x00, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xff, 0xb1, 0xb6,
+ 0x46, 0xca, 0x6a, 0x19 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ int64_t a, b, c, d, e, f, g;
+ ASSERT_TRUE(cursor
+ .Read(4, true, &a)
+ .Read(4, true, &b)
+ .Read(4, true, &c)
+ .Read(4, true, &d)
+ .Read(4, true, &e)
+ .Read(4, true, &f));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7fffffff, b);
+ EXPECT_EQ(-0x80000000LL, c);
+ EXPECT_EQ(-1, d);
+ EXPECT_EQ((int32_t) 0xb6b1ffef, e);
+ EXPECT_EQ(0x196aca46, f);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(4, true, &g));
+}
+
+TEST(LittleEndian, Signed8) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93,
+ 0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer, false);
+ int64_t a, b, c, d, e, f, g;
+ ASSERT_TRUE(cursor
+ .Read(8, true, &a)
+ .Read(8, true, &b)
+ .Read(8, true, &c)
+ .Read(8, true, &d)
+ .Read(8, true, &e)
+ .Read(8, true, &f));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7fffffffffffffffLL, b);
+ EXPECT_EQ(-0x7fffffffffffffffLL - 1, c);
+ EXPECT_EQ(-1, d);
+ EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e);
+ EXPECT_EQ(0x4e4249d27f8414a4LL, f);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(8, true, &g));
+}
+
+TEST(LittleEndian, Unsigned1) {
+ uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ int32_t a, b, c, d, e;
+ ASSERT_TRUE(cursor
+ .Read(1, false, &a)
+ .Read(1, false, &b)
+ .Read(1, false, &c)
+ .Read(1, false, &d));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7f, b);
+ EXPECT_EQ(0x80, c);
+ EXPECT_EQ(0xff, d);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(1, false, &e));
+}
+
+TEST(LittleEndian, Unsigned2) {
+ uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f,
+ 0x00, 0x80, 0x80, 0x80, 0xff, 0xff,
+ 0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ int32_t a, b, c, d, e, f, g, h, i, j;
+ ASSERT_TRUE(cursor
+ .Read(2, false, &a)
+ .Read(2, false, &b)
+ .Read(2, false, &c)
+ .Read(2, false, &d)
+ .Read(2, false, &e)
+ .Read(2, false, &f)
+ .Read(2, false, &g)
+ .Read(2, false, &h)
+ .Read(2, false, &i));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x80, b);
+ EXPECT_EQ(0x7fff, c);
+ EXPECT_EQ(0x8000, d);
+ EXPECT_EQ(0x8080, e);
+ EXPECT_EQ(0xffff, f);
+ EXPECT_EQ(0x39f1, g);
+ EXPECT_EQ(0x8abc, h);
+ EXPECT_EQ(0x5aec, i);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(2, false, &j));
+}
+
+TEST(LittleEndian, Unsigned4) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0x7f,
+ 0x00, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xff, 0xb1, 0xb6,
+ 0x46, 0xca, 0x6a, 0x19 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ int64_t a, b, c, d, e, f, g;
+ ASSERT_TRUE(cursor
+ .Read(4, false, &a)
+ .Read(4, false, &b)
+ .Read(4, false, &c)
+ .Read(4, false, &d)
+ .Read(4, false, &e)
+ .Read(4, false, &f));
+ EXPECT_EQ(0, a);
+ EXPECT_EQ(0x7fffffff, b);
+ EXPECT_EQ(0x80000000, c);
+ EXPECT_EQ(0xffffffff, d);
+ EXPECT_EQ(0xb6b1ffef, e);
+ EXPECT_EQ(0x196aca46, f);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(4, false, &g));
+}
+
+TEST(LittleEndian, Unsigned8) {
+ uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93,
+ 0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ uint64_t a, b, c, d, e, f, g;
+ ASSERT_TRUE(cursor
+ .Read(8, false, &a)
+ .Read(8, false, &b)
+ .Read(8, false, &c)
+ .Read(8, false, &d)
+ .Read(8, false, &e)
+ .Read(8, false, &f));
+ EXPECT_EQ(0U, a);
+ EXPECT_EQ(0x7fffffffffffffffULL, b);
+ EXPECT_EQ(0x8000000000000000ULL, c);
+ EXPECT_EQ(0xffffffffffffffffULL, d);
+ EXPECT_EQ(0x9320d5e9d2d5879cULL, e);
+ EXPECT_EQ(0x4e4249d27f8414a4ULL, f);
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor.Read(8, false, &g));
+}
+
+TEST(Extractor, Signed1) {
+ uint8_t data[] = { 0xfd };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ int8_t a;
+ EXPECT_TRUE(cursor >> a);
+ EXPECT_EQ(-3, a);
+ EXPECT_FALSE(cursor >> a);
+}
+
+TEST(Extractor, Signed2) {
+ uint8_t data[] = { 0x13, 0xcd };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ int16_t a;
+ EXPECT_TRUE(cursor >> a);
+ EXPECT_EQ(-13037, a);
+ EXPECT_FALSE(cursor >> a);
+}
+
+TEST(Extractor, Signed4) {
+ uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ int32_t a;
+ // For some reason, G++ 4.4.1 complains:
+ // warning: array subscript is above array bounds
+ // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
+ // I'm not able to see how such a reference would occur.
+ EXPECT_TRUE(cursor >> a);
+ EXPECT_EQ(-380377902, a);
+ EXPECT_FALSE(cursor >> a);
+}
+
+TEST(Extractor, Unsigned1) {
+ uint8_t data[] = { 0xfd };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ uint8_t a;
+ EXPECT_TRUE(cursor >> a);
+ EXPECT_EQ(0xfd, a);
+ EXPECT_FALSE(cursor >> a);
+}
+
+TEST(Extractor, Unsigned2) {
+ uint8_t data[] = { 0x13, 0xcd };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ uint16_t a;
+ EXPECT_TRUE(cursor >> a);
+ EXPECT_EQ(0xcd13, a);
+ EXPECT_FALSE(cursor >> a);
+}
+
+TEST(Extractor, Unsigned4) {
+ uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ uint32_t a;
+ // For some reason, G++ 4.4.1 complains:
+ // warning: array subscript is above array bounds
+ // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
+ // I'm not able to see how such a reference would occur.
+ EXPECT_TRUE(cursor >> a);
+ EXPECT_EQ(0xe953e4d2, a);
+ EXPECT_FALSE(cursor >> a);
+ EXPECT_FALSE(cursor >> a);
+}
+
+TEST(Extractor, Mixed) {
+ uint8_t data[] = { 0x42,
+ 0x25, 0x0b,
+ 0x3d, 0x25, 0xed, 0x2a,
+ 0xec, 0x16, 0x9e, 0x14, 0x61, 0x5b, 0x2c, 0xcf,
+ 0xd8,
+ 0x22, 0xa5,
+ 0x3a, 0x02, 0x6a, 0xd7,
+ 0x93, 0x2a, 0x2d, 0x8d, 0xb4, 0x95, 0xe0, 0xc6 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+ cursor.set_big_endian(true);
+
+ uint8_t a;
+ uint16_t b;
+ uint32_t c;
+ uint64_t d;
+ int8_t e;
+ int16_t f;
+ int32_t g;
+ int64_t h;
+ int z;
+ EXPECT_FALSE(cursor.AtEnd());
+ EXPECT_TRUE(cursor >> a >> b >> c >> d >> e >> f >> g >> h);
+ EXPECT_EQ(0x42U, a);
+ EXPECT_EQ(0x250bU, b);
+ EXPECT_EQ(0x3d25ed2aU, c);
+ EXPECT_EQ(0xec169e14615b2ccfULL, d);
+ EXPECT_EQ(-40, e);
+ EXPECT_EQ(0x22a5, f);
+ EXPECT_EQ(0x3a026ad7, g);
+ EXPECT_EQ(-7842405714468937530LL, h);
+
+ EXPECT_TRUE(cursor.AtEnd());
+ EXPECT_FALSE(cursor >> z);
+}
+
+TEST(Strings, Zero) {
+ uint8_t data[] = { 0xa6 };
+ ByteBuffer buffer(data, 0);
+ ByteCursor cursor(&buffer);
+
+ uint8_t received[1];
+ received[0] = 0xc2;
+ EXPECT_TRUE(cursor.Read(received, 0));
+ EXPECT_EQ(0xc2U, received[0]);
+}
+
+TEST(Strings, Some) {
+ uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+
+ uint8_t received[7] = { 0xa7, 0xf7, 0x43, 0x0c, 0x27, 0xea, 0xed };
+ EXPECT_TRUE(cursor.Skip(2).Read(received, 5));
+ uint8_t expected[7] = { 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xea, 0xed };
+ EXPECT_TRUE(memcmp(received, expected, 7) == 0);
+}
+
+TEST(Strings, TooMuch) {
+ uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+
+ uint8_t received1[3];
+ uint8_t received2[3];
+ uint8_t received3[3];
+ EXPECT_FALSE(cursor
+ .Read(received1, 3)
+ .Read(received2, 3)
+ .Read(received3, 3));
+ uint8_t expected1[3] = { 0x5d, 0x31, 0x09 };
+ uint8_t expected2[3] = { 0xa6, 0x2e, 0x2c };
+
+ EXPECT_TRUE(memcmp(received1, expected1, 3) == 0);
+ EXPECT_TRUE(memcmp(received2, expected2, 3) == 0);
+}
+
+TEST(Strings, PointTo) {
+ uint8_t data[] = { 0x83, 0x80, 0xb4, 0x38, 0x00, 0x2c, 0x0a, 0x27 };
+ ByteBuffer buffer(data, sizeof(data));
+ ByteCursor cursor(&buffer);
+
+ const uint8_t *received1;
+ const uint8_t *received2;
+ const uint8_t *received3;
+ const uint8_t *received4;
+ EXPECT_FALSE(cursor
+ .PointTo(&received1, 3)
+ .PointTo(&received2, 3)
+ .PointTo(&received3)
+ .PointTo(&received4, 3));
+ EXPECT_EQ(data + 0, received1);
+ EXPECT_EQ(data + 3, received2);
+ EXPECT_EQ(data + 6, received3);
+ EXPECT_EQ(NULL, received4);
+}
+
+TEST(Strings, CString) {
+ uint8_t data[] = "abc\0\0foo";
+ ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0'
+ ByteCursor cursor(&buffer);
+
+ string a, b, c;
+ EXPECT_TRUE(cursor.CString(&a).CString(&b));
+ EXPECT_EQ("abc", a);
+ EXPECT_EQ("", b);
+ EXPECT_FALSE(cursor.CString(&c));
+ EXPECT_EQ("", c);
+ EXPECT_TRUE(cursor.AtEnd());
+}
+
+TEST(Strings, CStringLimit) {
+ uint8_t data[] = "abcdef\0\0foobar";
+ ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0'
+ ByteCursor cursor(&buffer);
+
+ string a, b, c, d, e;
+
+ EXPECT_TRUE(cursor.CString(&a, 3));
+ EXPECT_EQ("abc", a);
+
+ EXPECT_TRUE(cursor.CString(&b, 0));
+ EXPECT_EQ("", b);
+
+ EXPECT_TRUE(cursor.CString(&c, 6));
+ EXPECT_EQ("def", c);
+
+ EXPECT_TRUE(cursor.CString(&d, 4));
+ EXPECT_EQ("ooba", d);
+
+ EXPECT_FALSE(cursor.CString(&e, 4));
+ EXPECT_EQ("", e);
+
+ EXPECT_TRUE(cursor.AtEnd());
+}
+
+// uint8_t data[] = { 0xa6, 0x54, 0xdf, 0x67, 0x51, 0x43, 0xac, 0xf1 };
+// ByteBuffer buffer(data, sizeof(data));
diff --git a/3rdParty/Breakpad/src/common/convert_UTF.c b/3rdParty/Breakpad/src/common/convert_UTF.c
index 80178d3..12a3c89 100644
--- a/3rdParty/Breakpad/src/common/convert_UTF.c
+++ b/3rdParty/Breakpad/src/common/convert_UTF.c
@@ -1,23 +1,39 @@
/*
- * Copyright 2001-2004 Unicode, Inc.
+ * Copyright © 1991-2015 Unicode, Inc. All rights reserved.
+ * Distributed under the Terms of Use in
+ * http://www.unicode.org/copyright.html.
*
- * Disclaimer
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of the Unicode data files and any associated documentation
+ * (the "Data Files") or Unicode software and any associated documentation
+ * (the "Software") to deal in the Data Files or Software
+ * without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, and/or sell copies of
+ * the Data Files or Software, and to permit persons to whom the Data Files
+ * or Software are furnished to do so, provided that
+ * (a) this copyright and permission notice appear with all copies
+ * of the Data Files or Software,
+ * (b) this copyright and permission notice appear in associated
+ * documentation, and
+ * (c) there is clear notice in each modified Data File or in the Software
+ * as well as in the documentation associated with the Data File(s) or
+ * Software that the data or software has been modified.
*
- * This source code is provided as is by Unicode, Inc. No claims are
- * made as to fitness for any particular purpose. No warranties of any
- * kind are expressed or implied. The recipient agrees to determine
- * applicability of information provided. If this file has been
- * purchased on magnetic or optical media from Unicode, Inc., the
- * sole remedy for any claim will be exchange of defective media
- * within 90 days of receipt.
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+ * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+ * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
*
- * Limitations on Rights to Redistribute This Code
- *
- * Unicode, Inc. hereby grants the right to freely use the information
- * supplied in this file in the creation of products supporting the
- * Unicode Standard, and to make copies of this file in any form
- * for internal or external distribution as long as this notice
- * remains attached.
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale,
+ * use or other dealings in these Data Files or Software without prior
+ * written authorization of the copyright holder.
*/
/* ---------------------------------------------------------------------
@@ -53,8 +69,13 @@ static const UTF32 halfMask = 0x3FFUL;
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
#define UNI_SUR_LOW_START (UTF32)0xDC00
#define UNI_SUR_LOW_END (UTF32)0xDFFF
+
+#ifndef false
#define false 0
+#endif
+#ifndef true
#define true 1
+#endif
/* --------------------------------------------------------------------- */
diff --git a/3rdParty/Breakpad/src/common/convert_UTF.h b/3rdParty/Breakpad/src/common/convert_UTF.h
index b1556de..644d099 100644
--- a/3rdParty/Breakpad/src/common/convert_UTF.h
+++ b/3rdParty/Breakpad/src/common/convert_UTF.h
@@ -1,25 +1,44 @@
/*
- * Copyright 2001-2004 Unicode, Inc.
+ * Copyright © 1991-2015 Unicode, Inc. All rights reserved.
+ * Distributed under the Terms of Use in
+ * http://www.unicode.org/copyright.html.
*
- * Disclaimer
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of the Unicode data files and any associated documentation
+ * (the "Data Files") or Unicode software and any associated documentation
+ * (the "Software") to deal in the Data Files or Software
+ * without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, and/or sell copies of
+ * the Data Files or Software, and to permit persons to whom the Data Files
+ * or Software are furnished to do so, provided that
+ * (a) this copyright and permission notice appear with all copies
+ * of the Data Files or Software,
+ * (b) this copyright and permission notice appear in associated
+ * documentation, and
+ * (c) there is clear notice in each modified Data File or in the Software
+ * as well as in the documentation associated with the Data File(s) or
+ * Software that the data or software has been modified.
*
- * This source code is provided as is by Unicode, Inc. No claims are
- * made as to fitness for any particular purpose. No warranties of any
- * kind are expressed or implied. The recipient agrees to determine
- * applicability of information provided. If this file has been
- * purchased on magnetic or optical media from Unicode, Inc., the
- * sole remedy for any claim will be exchange of defective media
- * within 90 days of receipt.
+ * THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+ * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+ * DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THE DATA FILES OR SOFTWARE.
*
- * Limitations on Rights to Redistribute This Code
- *
- * Unicode, Inc. hereby grants the right to freely use the information
- * supplied in this file in the creation of products supporting the
- * Unicode Standard, and to make copies of this file in any form
- * for internal or external distribution as long as this notice
- * remains attached.
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale,
+ * use or other dealings in these Data Files or Software without prior
+ * written authorization of the copyright holder.
*/
+#ifndef COMMON_CONVERT_UTF_H_
+#define COMMON_CONVERT_UTF_H_
+
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Header file.
@@ -141,3 +160,5 @@ Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
#endif
/* --------------------------------------------------------------------- */
+
+#endif // COMMON_CONVERT_UTF_H_
diff --git a/3rdParty/Breakpad/src/common/dwarf/bytereader-inl.h b/3rdParty/Breakpad/src/common/dwarf/bytereader-inl.h
index 3c16708..42c92f9 100644
--- a/3rdParty/Breakpad/src/common/dwarf/bytereader-inl.h
+++ b/3rdParty/Breakpad/src/common/dwarf/bytereader-inl.h
@@ -32,16 +32,15 @@
#include "common/dwarf/bytereader.h"
#include <assert.h>
+#include <stdint.h>
namespace dwarf2reader {
-inline uint8 ByteReader::ReadOneByte(const char* buffer) const {
+inline uint8 ByteReader::ReadOneByte(const uint8_t *buffer) const {
return buffer[0];
}
-inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const {
- const unsigned char *buffer
- = reinterpret_cast<const unsigned char *>(signed_buffer);
+inline uint16 ByteReader::ReadTwoBytes(const uint8_t *buffer) const {
const uint16 buffer0 = buffer[0];
const uint16 buffer1 = buffer[1];
if (endian_ == ENDIANNESS_LITTLE) {
@@ -51,9 +50,7 @@ inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const {
}
}
-inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const {
- const unsigned char *buffer
- = reinterpret_cast<const unsigned char *>(signed_buffer);
+inline uint64 ByteReader::ReadFourBytes(const uint8_t *buffer) const {
const uint32 buffer0 = buffer[0];
const uint32 buffer1 = buffer[1];
const uint32 buffer2 = buffer[2];
@@ -65,9 +62,7 @@ inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const {
}
}
-inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const {
- const unsigned char *buffer
- = reinterpret_cast<const unsigned char *>(signed_buffer);
+inline uint64 ByteReader::ReadEightBytes(const uint8_t *buffer) const {
const uint64 buffer0 = buffer[0];
const uint64 buffer1 = buffer[1];
const uint64 buffer2 = buffer[2];
@@ -89,12 +84,12 @@ inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const {
// information, plus one bit saying whether the number continues or
// not.
-inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer,
+inline uint64 ByteReader::ReadUnsignedLEB128(const uint8_t *buffer,
size_t* len) const {
uint64 result = 0;
size_t num_read = 0;
unsigned int shift = 0;
- unsigned char byte;
+ uint8_t byte;
do {
byte = *buffer++;
@@ -114,12 +109,12 @@ inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer,
// Read a signed LEB128 number. These are like regular LEB128
// numbers, except the last byte may have a sign bit set.
-inline int64 ByteReader::ReadSignedLEB128(const char* buffer,
+inline int64 ByteReader::ReadSignedLEB128(const uint8_t *buffer,
size_t* len) const {
int64 result = 0;
unsigned int shift = 0;
size_t num_read = 0;
- unsigned char byte;
+ uint8_t byte;
do {
byte = *buffer++;
@@ -134,18 +129,18 @@ inline int64 ByteReader::ReadSignedLEB128(const char* buffer,
return result;
}
-inline uint64 ByteReader::ReadOffset(const char* buffer) const {
+inline uint64 ByteReader::ReadOffset(const uint8_t *buffer) const {
assert(this->offset_reader_);
return (this->*offset_reader_)(buffer);
}
-inline uint64 ByteReader::ReadAddress(const char* buffer) const {
+inline uint64 ByteReader::ReadAddress(const uint8_t *buffer) const {
assert(this->address_reader_);
return (this->*address_reader_)(buffer);
}
inline void ByteReader::SetCFIDataBase(uint64 section_base,
- const char *buffer_base) {
+ const uint8_t *buffer_base) {
section_base_ = section_base;
buffer_base_ = buffer_base;
have_section_base_ = true;
diff --git a/3rdParty/Breakpad/src/common/dwarf/bytereader.cc b/3rdParty/Breakpad/src/common/dwarf/bytereader.cc
index 6802026..14b43ad 100644
--- a/3rdParty/Breakpad/src/common/dwarf/bytereader.cc
+++ b/3rdParty/Breakpad/src/common/dwarf/bytereader.cc
@@ -27,6 +27,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
+#include <stdint.h>
#include <stdlib.h>
#include "common/dwarf/bytereader-inl.h"
@@ -62,7 +63,7 @@ void ByteReader::SetAddressSize(uint8 size) {
}
}
-uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) {
+uint64 ByteReader::ReadInitialLength(const uint8_t *start, size_t* len) {
const uint64 initial_length = ReadFourBytes(start);
start += 4;
@@ -100,7 +101,7 @@ bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const {
}
}
-uint64 ByteReader::ReadEncodedPointer(const char *buffer,
+uint64 ByteReader::ReadEncodedPointer(const uint8_t *buffer,
DwarfPointerEncoding encoding,
size_t *len) const {
// UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't
@@ -129,7 +130,7 @@ uint64 ByteReader::ReadEncodedPointer(const char *buffer,
// Round up to the next boundary.
uint64 aligned = (offset + AddressSize() - 1) & -AddressSize();
// Convert back to a pointer.
- const char *aligned_buffer = buffer_base_ + (aligned - skew);
+ const uint8_t *aligned_buffer = buffer_base_ + (aligned - skew);
// Finally, store the length and actually fetch the pointer.
*len = aligned_buffer - buffer + AddressSize();
return ReadAddress(aligned_buffer);
@@ -242,4 +243,8 @@ uint64 ByteReader::ReadEncodedPointer(const char *buffer,
return pointer;
}
+Endianness ByteReader::GetEndianness() const {
+ return endian_;
+}
+
} // namespace dwarf2reader
diff --git a/3rdParty/Breakpad/src/common/dwarf/bytereader.h b/3rdParty/Breakpad/src/common/dwarf/bytereader.h
index e389427..59d4303 100644
--- a/3rdParty/Breakpad/src/common/dwarf/bytereader.h
+++ b/3rdParty/Breakpad/src/common/dwarf/bytereader.h
@@ -31,7 +31,10 @@
#ifndef COMMON_DWARF_BYTEREADER_H__
#define COMMON_DWARF_BYTEREADER_H__
+#include <stdint.h>
+
#include <string>
+
#include "common/dwarf/types.h"
#include "common/dwarf/dwarf2enums.h"
@@ -59,22 +62,22 @@ class ByteReader {
// Read a single byte from BUFFER and return it as an unsigned 8 bit
// number.
- uint8 ReadOneByte(const char* buffer) const;
+ uint8 ReadOneByte(const uint8_t *buffer) const;
// Read two bytes from BUFFER and return them as an unsigned 16 bit
// number, using this ByteReader's endianness.
- uint16 ReadTwoBytes(const char* buffer) const;
+ uint16 ReadTwoBytes(const uint8_t *buffer) const;
// Read four bytes from BUFFER and return them as an unsigned 32 bit
// number, using this ByteReader's endianness. This function returns
// a uint64 so that it is compatible with ReadAddress and
// ReadOffset. The number it returns will never be outside the range
// of an unsigned 32 bit integer.
- uint64 ReadFourBytes(const char* buffer) const;
+ uint64 ReadFourBytes(const uint8_t *buffer) const;
// Read eight bytes from BUFFER and return them as an unsigned 64
// bit number, using this ByteReader's endianness.
- uint64 ReadEightBytes(const char* buffer) const;
+ uint64 ReadEightBytes(const uint8_t *buffer) const;
// Read an unsigned LEB128 (Little Endian Base 128) number from
// BUFFER and return it as an unsigned 64 bit integer. Set LEN to
@@ -93,7 +96,7 @@ class ByteReader {
// In other words, we break VALUE into groups of seven bits, put
// them in little-endian order, and then write them as eight-bit
// bytes with the high bit on all but the last.
- uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const;
+ uint64 ReadUnsignedLEB128(const uint8_t *buffer, size_t *len) const;
// Read a signed LEB128 number from BUFFER and return it as an
// signed 64 bit integer. Set LEN to the number of bytes read.
@@ -112,7 +115,7 @@ class ByteReader {
// In other words, we break VALUE into groups of seven bits, put
// them in little-endian order, and then write them as eight-bit
// bytes with the high bit on all but the last.
- int64 ReadSignedLEB128(const char* buffer, size_t* len) const;
+ int64 ReadSignedLEB128(const uint8_t *buffer, size_t *len) const;
// Indicate that addresses on this architecture are SIZE bytes long. SIZE
// must be either 4 or 8. (DWARF allows addresses to be any number of
@@ -135,7 +138,7 @@ class ByteReader {
// Read an address from BUFFER and return it as an unsigned 64 bit
// integer, respecting this ByteReader's endianness and address size. You
// must call SetAddressSize before calling this function.
- uint64 ReadAddress(const char* buffer) const;
+ uint64 ReadAddress(const uint8_t *buffer) const;
// DWARF actually defines two slightly different formats: 32-bit DWARF
// and 64-bit DWARF. This is *not* related to the size of registers or
@@ -172,14 +175,14 @@ class ByteReader {
// - The 32-bit value 0xffffffff, followed by a 64-bit byte count,
// indicating that the data whose length is being measured uses
// the 64-bit DWARF format.
- uint64 ReadInitialLength(const char* start, size_t* len);
+ uint64 ReadInitialLength(const uint8_t *start, size_t *len);
// Read an offset from BUFFER and return it as an unsigned 64 bit
// integer, respecting the ByteReader's endianness. In 32-bit DWARF, the
// offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes
// long. You must call ReadInitialLength or SetOffsetSize before calling
// this function; see the comments above for details.
- uint64 ReadOffset(const char* buffer) const;
+ uint64 ReadOffset(const uint8_t *buffer) const;
// Return the current offset size, in bytes.
// A return value of 4 indicates that we are reading 32-bit DWARF.
@@ -234,7 +237,7 @@ class ByteReader {
// is BUFFER_BASE. This allows us to find the address that a given
// byte in our buffer would have when loaded into the program the
// data describes. We need this to resolve DW_EH_PE_pcrel pointers.
- void SetCFIDataBase(uint64 section_base, const char *buffer_base);
+ void SetCFIDataBase(uint64 section_base, const uint8_t *buffer_base);
// Indicate that the base address of the program's ".text" section
// is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers.
@@ -273,13 +276,15 @@ class ByteReader {
// base address this reader hasn't been given, so you should check
// with ValidEncoding and UsableEncoding first if you would rather
// die in a more helpful way.
- uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding,
+ uint64 ReadEncodedPointer(const uint8_t *buffer,
+ DwarfPointerEncoding encoding,
size_t *len) const;
+ Endianness GetEndianness() const;
private:
// Function pointer type for our address and offset readers.
- typedef uint64 (ByteReader::*AddressReader)(const char*) const;
+ typedef uint64 (ByteReader::*AddressReader)(const uint8_t *) const;
// Read an offset from BUFFER and return it as an unsigned 64 bit
// integer. DWARF2/3 define offsets as either 4 or 8 bytes,
@@ -302,7 +307,7 @@ class ByteReader {
bool have_section_base_, have_text_base_, have_data_base_;
bool have_function_base_;
uint64 section_base_, text_base_, data_base_, function_base_;
- const char *buffer_base_;
+ const uint8_t *buffer_base_;
};
} // namespace dwarf2reader
diff --git a/3rdParty/Breakpad/src/common/dwarf/bytereader_unittest.cc b/3rdParty/Breakpad/src/common/dwarf/bytereader_unittest.cc
new file mode 100644
index 0000000..e66062d
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/bytereader_unittest.cc
@@ -0,0 +1,707 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader
+
+#include <stdint.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "common/dwarf/bytereader.h"
+#include "common/dwarf/bytereader-inl.h"
+#include "common/dwarf/cfi_assembler.h"
+#include "common/using_std_string.h"
+
+using dwarf2reader::ByteReader;
+using dwarf2reader::DwarfPointerEncoding;
+using dwarf2reader::ENDIANNESS_BIG;
+using dwarf2reader::ENDIANNESS_LITTLE;
+using google_breakpad::CFISection;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::kBigEndian;
+using google_breakpad::test_assembler::kLittleEndian;
+using google_breakpad::test_assembler::Section;
+using testing::Test;
+
+struct ReaderFixture {
+ string contents;
+ size_t pointer_size;
+};
+
+class Reader: public ReaderFixture, public Test { };
+class ReaderDeathTest: public ReaderFixture, public Test { };
+
+TEST_F(Reader, SimpleConstructor) {
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetAddressSize(4);
+ CFISection section(kBigEndian, 4);
+ section
+ .D8(0xc0)
+ .D16(0xcf0d)
+ .D32(0x96fdd219)
+ .D64(0xbbf55fef0825f117ULL)
+ .ULEB128(0xa0927048ba8121afULL)
+ .LEB128(-0x4f337badf4483f83LL)
+ .D32(0xfec319c9);
+ ASSERT_TRUE(section.GetContents(&contents));
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(contents.data());
+ EXPECT_EQ(0xc0U, reader.ReadOneByte(data));
+ EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1));
+ EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3));
+ EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7));
+ size_t leb128_size;
+ EXPECT_EQ(0xa0927048ba8121afULL,
+ reader.ReadUnsignedLEB128(data + 15, &leb128_size));
+ EXPECT_EQ(10U, leb128_size);
+ EXPECT_EQ(-0x4f337badf4483f83LL,
+ reader.ReadSignedLEB128(data + 25, &leb128_size));
+ EXPECT_EQ(10U, leb128_size);
+ EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35));
+}
+
+TEST_F(Reader, ValidEncodings) {
+ ByteReader reader(ENDIANNESS_LITTLE);
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_absptr |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_uleb128 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata2 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata4 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata8 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sleb128 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata2 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata4 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata8 |
+ dwarf2reader::DW_EH_PE_pcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_absptr |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_uleb128 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata2 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata4 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata8 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sleb128 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata2 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata4 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata8 |
+ dwarf2reader::DW_EH_PE_textrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_absptr |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_uleb128 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata2 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata4 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata8 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sleb128 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata2 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata4 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata8 |
+ dwarf2reader::DW_EH_PE_datarel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_absptr |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_uleb128 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata2 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata4 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_udata8 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sleb128 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata2 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata4 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+ EXPECT_TRUE(reader.ValidEncoding(
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
+ dwarf2reader::DW_EH_PE_sdata8 |
+ dwarf2reader::DW_EH_PE_funcrel)));
+
+ EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05)));
+ EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07)));
+ EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d)));
+ EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f)));
+ EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51)));
+ EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60)));
+ EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70)));
+ EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0)));
+ EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0)));
+}
+
+TEST_F(ReaderDeathTest, DW_EH_PE_omit) {
+ static const uint8_t data[] = { 42 };
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetAddressSize(4);
+ EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit,
+ &pointer_size),
+ "encoding != DW_EH_PE_omit");
+}
+
+TEST_F(Reader, DW_EH_PE_absptr4) {
+ static const uint8_t data[] = { 0x27, 0x57, 0xea, 0x40 };
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(4);
+ EXPECT_EQ(0x40ea5727U,
+ reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr,
+ &pointer_size));
+ EXPECT_EQ(4U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_absptr8) {
+ static const uint8_t data[] = {
+ 0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50
+ };
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(8);
+ EXPECT_EQ(0x010598c240ea5727ULL,
+ reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr,
+ &pointer_size));
+ EXPECT_EQ(8U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_uleb128) {
+ static const uint8_t data[] = { 0x81, 0x84, 0x4c };
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(4);
+ EXPECT_EQ(0x130201U,
+ reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128,
+ &pointer_size));
+ EXPECT_EQ(3U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_udata2) {
+ static const uint8_t data[] = { 0xf4, 0x8d };
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetAddressSize(4);
+ EXPECT_EQ(0xf48dU,
+ reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2,
+ &pointer_size));
+ EXPECT_EQ(2U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_udata4) {
+ static const uint8_t data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b };
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetAddressSize(8);
+ EXPECT_EQ(0xa5628f8b,
+ reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4,
+ &pointer_size));
+ EXPECT_EQ(4U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_udata8Addr8) {
+ static const uint8_t data[] = {
+ 0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
+ };
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(8);
+ EXPECT_EQ(0x8fed199f69047304ULL,
+ reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
+ &pointer_size));
+ EXPECT_EQ(8U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_udata8Addr4) {
+ static const uint8_t data[] = {
+ 0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
+ };
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(4);
+ EXPECT_EQ(0x69047304ULL,
+ reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
+ &pointer_size));
+ EXPECT_EQ(8U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_sleb128) {
+ static const uint8_t data[] = { 0x42, 0xff, 0xfb, 0x73 };
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetAddressSize(4);
+ EXPECT_EQ(-0x030201U & 0xffffffff,
+ reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128,
+ &pointer_size));
+ EXPECT_EQ(3U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_sdata2) {
+ static const uint8_t data[] = { 0xb9, 0xbf };
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(8);
+ EXPECT_EQ(0xffffffffffffbfb9ULL,
+ reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2,
+ &pointer_size));
+ EXPECT_EQ(2U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_sdata4) {
+ static const uint8_t data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad };
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(8);
+ EXPECT_EQ(0xffffffffadc2b8f2ULL,
+ reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4,
+ &pointer_size));
+ EXPECT_EQ(4U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_sdata8) {
+ static const uint8_t data[] = {
+ 0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87
+ };
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(8);
+ EXPECT_EQ(0x87269b0ce0795766ULL,
+ reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8,
+ &pointer_size));
+ EXPECT_EQ(8U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_pcrel) {
+ static const uint8_t data[] = {
+ 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce
+ };
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetAddressSize(4);
+ DwarfPointerEncoding encoding =
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel
+ | dwarf2reader::DW_EH_PE_absptr);
+ reader.SetCFIDataBase(0x89951377, data);
+ EXPECT_EQ(0x89951377 + 3 + 0x14c8c402,
+ reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
+ EXPECT_EQ(4U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_textrel) {
+ static const uint8_t data[] = {
+ 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e
+ };
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(4);
+ reader.SetTextBase(0xb91beaf0);
+ DwarfPointerEncoding encoding =
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel
+ | dwarf2reader::DW_EH_PE_sdata2);
+ EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff,
+ reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
+ EXPECT_EQ(2U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_datarel) {
+ static const uint8_t data[] = {
+ 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39
+ };
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetAddressSize(8);
+ reader.SetDataBase(0xbef308bd25ce74f0ULL);
+ DwarfPointerEncoding encoding =
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel
+ | dwarf2reader::DW_EH_PE_sleb128);
+ EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL,
+ reader.ReadEncodedPointer(data + 2, encoding, &pointer_size));
+ EXPECT_EQ(3U, pointer_size);
+}
+
+TEST_F(Reader, DW_EH_PE_funcrel) {
+ static const uint8_t data[] = {
+ 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9
+ };
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetAddressSize(4);
+ reader.SetFunctionBase(0x823c3520);
+ DwarfPointerEncoding encoding =
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel
+ | dwarf2reader::DW_EH_PE_udata2);
+ EXPECT_EQ(0x823c3520 + 0xd148,
+ reader.ReadEncodedPointer(data + 5, encoding, &pointer_size));
+ EXPECT_EQ(2U, pointer_size);
+}
+
+TEST(UsableBase, CFI) {
+ static const uint8_t data[] = { 0x42 };
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetCFIDataBase(0xb31cbd20, data);
+ EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
+ EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
+ EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
+}
+
+TEST(UsableBase, Text) {
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetTextBase(0xa899ccb9);
+ EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
+ EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
+ EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
+}
+
+TEST(UsableBase, Data) {
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetDataBase(0xf7b10bcd);
+ EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
+ EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
+ EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
+}
+
+TEST(UsableBase, Function) {
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetFunctionBase(0xc2c0ed81);
+ EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
+ EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
+ EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
+}
+
+TEST(UsableBase, ClearFunction) {
+ ByteReader reader(ENDIANNESS_BIG);
+ reader.SetFunctionBase(0xc2c0ed81);
+ reader.ClearFunctionBase();
+ EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
+ EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
+ EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
+}
+
+struct AlignedFixture {
+ AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); }
+ static const uint8_t data[10];
+ ByteReader reader;
+ size_t pointer_size;
+};
+
+const uint8_t AlignedFixture::data[10] = {
+ 0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b
+};
+
+class Aligned: public AlignedFixture, public Test { };
+
+TEST_F(Aligned, DW_EH_PE_aligned0) {
+ reader.SetCFIDataBase(0xb440305c, data);
+ EXPECT_EQ(0xfe6e93d8U,
+ reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
+ &pointer_size));
+ EXPECT_EQ(4U, pointer_size);
+}
+
+TEST_F(Aligned, DW_EH_PE_aligned1) {
+ reader.SetCFIDataBase(0xb440305d, data);
+ EXPECT_EQ(0xd834d51cU,
+ reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
+ &pointer_size));
+ EXPECT_EQ(7U, pointer_size);
+}
+
+TEST_F(Aligned, DW_EH_PE_aligned2) {
+ reader.SetCFIDataBase(0xb440305e, data);
+ EXPECT_EQ(0x93d834d5U,
+ reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
+ &pointer_size));
+ EXPECT_EQ(6U, pointer_size);
+}
+
+TEST_F(Aligned, DW_EH_PE_aligned3) {
+ reader.SetCFIDataBase(0xb440305f, data);
+ EXPECT_EQ(0x6e93d834U,
+ reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
+ &pointer_size));
+ EXPECT_EQ(5U, pointer_size);
+}
+
+TEST_F(Aligned, DW_EH_PE_aligned11) {
+ reader.SetCFIDataBase(0xb4403061, data);
+ EXPECT_EQ(0xd834d51cU,
+ reader.ReadEncodedPointer(data + 1,
+ dwarf2reader::DW_EH_PE_aligned,
+ &pointer_size));
+ EXPECT_EQ(6U, pointer_size);
+}
+
+TEST_F(Aligned, DW_EH_PE_aligned30) {
+ reader.SetCFIDataBase(0xb4403063, data);
+ EXPECT_EQ(0x6e93d834U,
+ reader.ReadEncodedPointer(data + 1,
+ dwarf2reader::DW_EH_PE_aligned,
+ &pointer_size));
+ EXPECT_EQ(4U, pointer_size);
+}
+
+TEST_F(Aligned, DW_EH_PE_aligned23) {
+ reader.SetCFIDataBase(0xb4403062, data);
+ EXPECT_EQ(0x1cd3ac2bU,
+ reader.ReadEncodedPointer(data + 3,
+ dwarf2reader::DW_EH_PE_aligned,
+ &pointer_size));
+ EXPECT_EQ(7U, pointer_size);
+}
+
+TEST_F(Aligned, DW_EH_PE_aligned03) {
+ reader.SetCFIDataBase(0xb4403064, data);
+ EXPECT_EQ(0x34d51cd3U,
+ reader.ReadEncodedPointer(data + 3,
+ dwarf2reader::DW_EH_PE_aligned,
+ &pointer_size));
+ EXPECT_EQ(5U, pointer_size);
+}
diff --git a/3rdParty/Breakpad/src/common/dwarf/cfi_assembler.cc b/3rdParty/Breakpad/src/common/dwarf/cfi_assembler.cc
new file mode 100644
index 0000000..2dc2208
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/cfi_assembler.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// cfi_assembler.cc: Implementation of google_breakpad::CFISection class.
+// See cfi_assembler.h for details.
+
+#include "common/dwarf/cfi_assembler.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+namespace google_breakpad {
+
+using dwarf2reader::DwarfPointerEncoding;
+
+CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
+ int data_alignment_factor,
+ unsigned return_address_register,
+ uint8_t version,
+ const string &augmentation,
+ bool dwarf64,
+ uint8_t address_size,
+ uint8_t segment_size) {
+ assert(!entry_length_);
+ entry_length_ = new PendingLength();
+ in_fde_ = false;
+
+ if (dwarf64) {
+ D32(kDwarf64InitialLengthMarker);
+ D64(entry_length_->length);
+ entry_length_->start = Here();
+ D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier);
+ } else {
+ D32(entry_length_->length);
+ entry_length_->start = Here();
+ D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier);
+ }
+ D8(version);
+ AppendCString(augmentation);
+ if (version >= 4) {
+ D8(address_size);
+ D8(segment_size);
+ }
+ ULEB128(code_alignment_factor);
+ LEB128(data_alignment_factor);
+ if (version == 1)
+ D8(return_address_register);
+ else
+ ULEB128(return_address_register);
+ return *this;
+}
+
+CFISection &CFISection::FDEHeader(Label cie_pointer,
+ uint64_t initial_location,
+ uint64_t address_range,
+ bool dwarf64) {
+ assert(!entry_length_);
+ entry_length_ = new PendingLength();
+ in_fde_ = true;
+ fde_start_address_ = initial_location;
+
+ if (dwarf64) {
+ D32(0xffffffff);
+ D64(entry_length_->length);
+ entry_length_->start = Here();
+ if (eh_frame_)
+ D64(Here() - cie_pointer);
+ else
+ D64(cie_pointer);
+ } else {
+ D32(entry_length_->length);
+ entry_length_->start = Here();
+ if (eh_frame_)
+ D32(Here() - cie_pointer);
+ else
+ D32(cie_pointer);
+ }
+ EncodedPointer(initial_location);
+ // The FDE length in an .eh_frame section uses the same encoding as the
+ // initial location, but ignores the base address (selected by the upper
+ // nybble of the encoding), as it's a length, not an address that can be
+ // made relative.
+ EncodedPointer(address_range,
+ DwarfPointerEncoding(pointer_encoding_ & 0x0f));
+ return *this;
+}
+
+CFISection &CFISection::FinishEntry() {
+ assert(entry_length_);
+ Align(address_size_, dwarf2reader::DW_CFA_nop);
+ entry_length_->length = Here() - entry_length_->start;
+ delete entry_length_;
+ entry_length_ = NULL;
+ in_fde_ = false;
+ return *this;
+}
+
+CFISection &CFISection::EncodedPointer(uint64_t address,
+ DwarfPointerEncoding encoding,
+ const EncodedPointerBases &bases) {
+ // Omitted data is extremely easy to emit.
+ if (encoding == dwarf2reader::DW_EH_PE_omit)
+ return *this;
+
+ // If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume
+ // that ADDRESS is the address at which the pointer is stored --- in
+ // other words, that bit has no effect on how we write the pointer.
+ encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect);
+
+ // Find the base address to which this pointer is relative. The upper
+ // nybble of the encoding specifies this.
+ uint64_t base;
+ switch (encoding & 0xf0) {
+ case dwarf2reader::DW_EH_PE_absptr: base = 0; break;
+ case dwarf2reader::DW_EH_PE_pcrel: base = bases.cfi + Size(); break;
+ case dwarf2reader::DW_EH_PE_textrel: base = bases.text; break;
+ case dwarf2reader::DW_EH_PE_datarel: base = bases.data; break;
+ case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break;
+ case dwarf2reader::DW_EH_PE_aligned: base = 0; break;
+ default: abort();
+ };
+
+ // Make ADDRESS relative. Yes, this is appropriate even for "absptr"
+ // values; see gcc/unwind-pe.h.
+ address -= base;
+
+ // Align the pointer, if required.
+ if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned)
+ Align(AddressSize());
+
+ // Append ADDRESS to this section in the appropriate form. For the
+ // fixed-width forms, we don't need to differentiate between signed and
+ // unsigned encodings, because ADDRESS has already been extended to 64
+ // bits before it was passed to us.
+ switch (encoding & 0x0f) {
+ case dwarf2reader::DW_EH_PE_absptr:
+ Address(address);
+ break;
+
+ case dwarf2reader::DW_EH_PE_uleb128:
+ ULEB128(address);
+ break;
+
+ case dwarf2reader::DW_EH_PE_sleb128:
+ LEB128(address);
+ break;
+
+ case dwarf2reader::DW_EH_PE_udata2:
+ case dwarf2reader::DW_EH_PE_sdata2:
+ D16(address);
+ break;
+
+ case dwarf2reader::DW_EH_PE_udata4:
+ case dwarf2reader::DW_EH_PE_sdata4:
+ D32(address);
+ break;
+
+ case dwarf2reader::DW_EH_PE_udata8:
+ case dwarf2reader::DW_EH_PE_sdata8:
+ D64(address);
+ break;
+
+ default:
+ abort();
+ }
+
+ return *this;
+};
+
+const uint32_t CFISection::kDwarf64InitialLengthMarker;
+const uint32_t CFISection::kDwarf32CIEIdentifier;
+const uint64_t CFISection::kDwarf64CIEIdentifier;
+const uint32_t CFISection::kEHFrame32CIEIdentifier;
+const uint64_t CFISection::kEHFrame64CIEIdentifier;
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/dwarf/cfi_assembler.h b/3rdParty/Breakpad/src/common/dwarf/cfi_assembler.h
new file mode 100644
index 0000000..bd7354d
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/cfi_assembler.h
@@ -0,0 +1,271 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// cfi_assembler.h: Define CFISection, a class for creating properly
+// (and improperly) formatted DWARF CFI data for unit tests.
+
+#ifndef PROCESSOR_CFI_ASSEMBLER_H_
+#define PROCESSOR_CFI_ASSEMBLER_H_
+
+#include <string>
+
+#include "common/dwarf/dwarf2enums.h"
+#include "common/test_assembler.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+
+namespace google_breakpad {
+
+using dwarf2reader::DwarfPointerEncoding;
+using google_breakpad::test_assembler::Endianness;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::Section;
+
+class CFISection: public Section {
+ public:
+
+ // CFI augmentation strings beginning with 'z', defined by the
+ // Linux/IA-64 C++ ABI, can specify interesting encodings for
+ // addresses appearing in FDE headers and call frame instructions (and
+ // for additional fields whose presence the augmentation string
+ // specifies). In particular, pointers can be specified to be relative
+ // to various base address: the start of the .text section, the
+ // location holding the address itself, and so on. These allow the
+ // frame data to be position-independent even when they live in
+ // write-protected pages. These variants are specified at the
+ // following two URLs:
+ //
+ // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
+ // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
+ //
+ // CFISection leaves the production of well-formed 'z'-augmented CIEs and
+ // FDEs to the user, but does provide EncodedPointer, to emit
+ // properly-encoded addresses for a given pointer encoding.
+ // EncodedPointer uses an instance of this structure to find the base
+ // addresses it should use; you can establish a default for all encoded
+ // pointers appended to this section with SetEncodedPointerBases.
+ struct EncodedPointerBases {
+ EncodedPointerBases() : cfi(), text(), data() { }
+
+ // The starting address of this CFI section in memory, for
+ // DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data
+ // that has is loaded into the program's address space.
+ uint64_t cfi;
+
+ // The starting address of this file's .text section, for DW_EH_PE_textrel.
+ uint64_t text;
+
+ // The starting address of this file's .got or .eh_frame_hdr section,
+ // for DW_EH_PE_datarel.
+ uint64_t data;
+ };
+
+ // Create a CFISection whose endianness is ENDIANNESS, and where
+ // machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is
+ // true, use the .eh_frame format, as described by the Linux
+ // Standards Base Core Specification, instead of the DWARF CFI
+ // format.
+ CFISection(Endianness endianness, size_t address_size,
+ bool eh_frame = false)
+ : Section(endianness), address_size_(address_size), eh_frame_(eh_frame),
+ pointer_encoding_(dwarf2reader::DW_EH_PE_absptr),
+ encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) {
+ // The 'start', 'Here', and 'Mark' members of a CFISection all refer
+ // to section offsets.
+ start() = 0;
+ }
+
+ // Return this CFISection's address size.
+ size_t AddressSize() const { return address_size_; }
+
+ // Return true if this CFISection uses the .eh_frame format, or
+ // false if it contains ordinary DWARF CFI data.
+ bool ContainsEHFrame() const { return eh_frame_; }
+
+ // Use ENCODING for pointers in calls to FDEHeader and EncodedPointer.
+ void SetPointerEncoding(DwarfPointerEncoding encoding) {
+ pointer_encoding_ = encoding;
+ }
+
+ // Use the addresses in BASES as the base addresses for encoded
+ // pointers in subsequent calls to FDEHeader or EncodedPointer.
+ // This function makes a copy of BASES.
+ void SetEncodedPointerBases(const EncodedPointerBases &bases) {
+ encoded_pointer_bases_ = bases;
+ }
+
+ // Append a Common Information Entry header to this section with the
+ // given values. If dwarf64 is true, use the 64-bit DWARF initial
+ // length format for the CIE's initial length. Return a reference to
+ // this section. You should call FinishEntry after writing the last
+ // instruction for the CIE.
+ //
+ // Before calling this function, you will typically want to use Mark
+ // or Here to make a label to pass to FDEHeader that refers to this
+ // CIE's position in the section.
+ CFISection &CIEHeader(uint64_t code_alignment_factor,
+ int data_alignment_factor,
+ unsigned return_address_register,
+ uint8_t version = 3,
+ const string &augmentation = "",
+ bool dwarf64 = false,
+ uint8_t address_size = 8,
+ uint8_t segment_size = 0);
+
+ // Append a Frame Description Entry header to this section with the
+ // given values. If dwarf64 is true, use the 64-bit DWARF initial
+ // length format for the CIE's initial length. Return a reference to
+ // this section. You should call FinishEntry after writing the last
+ // instruction for the CIE.
+ //
+ // This function doesn't support entries that are longer than
+ // 0xffffff00 bytes. (The "initial length" is always a 32-bit
+ // value.) Nor does it support .debug_frame sections longer than
+ // 0xffffff00 bytes.
+ CFISection &FDEHeader(Label cie_pointer,
+ uint64_t initial_location,
+ uint64_t address_range,
+ bool dwarf64 = false);
+
+ // Note the current position as the end of the last CIE or FDE we
+ // started, after padding with DW_CFA_nops for alignment. This
+ // defines the label representing the entry's length, cited in the
+ // entry's header. Return a reference to this section.
+ CFISection &FinishEntry();
+
+ // Append the contents of BLOCK as a DW_FORM_block value: an
+ // unsigned LEB128 length, followed by that many bytes of data.
+ CFISection &Block(const string &block) {
+ ULEB128(block.size());
+ Append(block);
+ return *this;
+ }
+
+ // Append ADDRESS to this section, in the appropriate size and
+ // endianness. Return a reference to this section.
+ CFISection &Address(uint64_t address) {
+ Section::Append(endianness(), address_size_, address);
+ return *this;
+ }
+ CFISection &Address(Label address) {
+ Section::Append(endianness(), address_size_, address);
+ return *this;
+ }
+
+ // Append ADDRESS to this section, using ENCODING and BASES. ENCODING
+ // defaults to this section's default encoding, established by
+ // SetPointerEncoding. BASES defaults to this section's bases, set by
+ // SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the
+ // encoding, assume that ADDRESS is where the true address is stored.
+ // Return a reference to this section.
+ //
+ // (C++ doesn't let me use default arguments here, because I want to
+ // refer to members of *this in the default argument expression.)
+ CFISection &EncodedPointer(uint64_t address) {
+ return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_);
+ }
+ CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) {
+ return EncodedPointer(address, encoding, encoded_pointer_bases_);
+ }
+ CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding,
+ const EncodedPointerBases &bases);
+
+ // Restate some member functions, to keep chaining working nicely.
+ CFISection &Mark(Label *label) { Section::Mark(label); return *this; }
+ CFISection &D8(uint8_t v) { Section::D8(v); return *this; }
+ CFISection &D16(uint16_t v) { Section::D16(v); return *this; }
+ CFISection &D16(Label v) { Section::D16(v); return *this; }
+ CFISection &D32(uint32_t v) { Section::D32(v); return *this; }
+ CFISection &D32(const Label &v) { Section::D32(v); return *this; }
+ CFISection &D64(uint64_t v) { Section::D64(v); return *this; }
+ CFISection &D64(const Label &v) { Section::D64(v); return *this; }
+ CFISection &LEB128(long long v) { Section::LEB128(v); return *this; }
+ CFISection &ULEB128(uint64_t v) { Section::ULEB128(v); return *this; }
+
+ private:
+ // A length value that we've appended to the section, but is not yet
+ // known. LENGTH is the appended value; START is a label referring
+ // to the start of the data whose length was cited.
+ struct PendingLength {
+ Label length;
+ Label start;
+ };
+
+ // Constants used in CFI/.eh_frame data:
+
+ // If the first four bytes of an "initial length" are this constant, then
+ // the data uses the 64-bit DWARF format, and the length itself is the
+ // subsequent eight bytes.
+ static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU;
+
+ // The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data.
+ static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0;
+ static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0;
+ static const uint32_t kEHFrame32CIEIdentifier = 0;
+ static const uint64_t kEHFrame64CIEIdentifier = 0;
+
+ // The size of a machine address for the data in this section.
+ size_t address_size_;
+
+ // If true, we are generating a Linux .eh_frame section, instead of
+ // a standard DWARF .debug_frame section.
+ bool eh_frame_;
+
+ // The encoding to use for FDE pointers.
+ DwarfPointerEncoding pointer_encoding_;
+
+ // The base addresses to use when emitting encoded pointers.
+ EncodedPointerBases encoded_pointer_bases_;
+
+ // The length value for the current entry.
+ //
+ // Oddly, this must be dynamically allocated. Labels never get new
+ // values; they only acquire constraints on the value they already
+ // have, or assert if you assign them something incompatible. So
+ // each header needs truly fresh Label objects to cite in their
+ // headers and track their positions. The alternative is explicit
+ // destructor invocation and a placement new. Ick.
+ PendingLength *entry_length_;
+
+ // True if we are currently emitting an FDE --- that is, we have
+ // called FDEHeader but have not yet called FinishEntry.
+ bool in_fde_;
+
+ // If in_fde_ is true, this is its starting address. We use this for
+ // emitting DW_EH_PE_funcrel pointers.
+ uint64_t fde_start_address_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_CFI_ASSEMBLER_H_
diff --git a/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.cc b/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.cc
index c741d69..94542b5 100644
--- a/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.cc
+++ b/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.cc
@@ -32,6 +32,7 @@
// See dwarf2diehandler.h for details.
#include <assert.h>
+#include <stdint.h>
#include <string>
@@ -57,8 +58,7 @@ bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size,
dwarf_version);
}
-bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag,
- const AttributeList& attrs) {
+bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag) {
// The stack entry for the parent of this DIE, if there is one.
HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top();
@@ -82,7 +82,7 @@ bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag,
if (parent) {
if (parent->handler_)
// Ask the parent to find a handler.
- handler = parent->handler_->FindChildHandler(offset, tag, attrs);
+ handler = parent->handler_->FindChildHandler(offset, tag);
else
// No parent handler means we're not interested in any of our
// children.
@@ -92,7 +92,7 @@ bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag,
// decides whether to visit it, but the root DIE has no parent
// handler, so we have a special method on the root DIE handler
// itself to decide.
- if (root_handler_->StartRootDIE(offset, tag, attrs))
+ if (root_handler_->StartRootDIE(offset, tag))
handler = root_handler_;
else
handler = NULL;
@@ -168,7 +168,7 @@ void DIEDispatcher::ProcessAttributeReference(uint64 offset,
void DIEDispatcher::ProcessAttributeBuffer(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
- const char* data,
+ const uint8_t *data,
uint64 len) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
diff --git a/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.h b/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.h
index 12b8d3a..a1e589a 100644
--- a/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.h
+++ b/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler.h
@@ -156,6 +156,8 @@
#ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__
#define COMMON_DWARF_DWARF2DIEHANDLER_H__
+#include <stdint.h>
+
#include <stack>
#include <string>
@@ -206,7 +208,7 @@ class DIEHandler {
uint64 data) { }
virtual void ProcessAttributeBuffer(enum DwarfAttribute attr,
enum DwarfForm form,
- const char* data,
+ const uint8_t *data,
uint64 len) { }
virtual void ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
@@ -239,12 +241,10 @@ class DIEHandler {
// that child DIE (and all its descendants).
//
// OFFSET is the offset of the child; TAG indicates what kind of DIE
- // it is; and ATTRS is the list of attributes the DIE will have, and
- // their forms (their values are not provided).
+ // it is.
//
// The default definition skips all children.
- virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
- const AttributeList &attrs) {
+ virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag) {
return NULL;
}
@@ -280,8 +280,7 @@ class RootDIEHandler: public DIEHandler {
// unit.
//
// The default definition elects to visit the root DIE.
- virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag,
- const AttributeList& attrs) { return true; }
+ virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag) { return true; }
};
class DIEDispatcher: public Dwarf2Handler {
@@ -296,8 +295,7 @@ class DIEDispatcher: public Dwarf2Handler {
bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
- bool StartDIE(uint64 offset, enum DwarfTag tag,
- const AttributeList &attrs);
+ bool StartDIE(uint64 offset, enum DwarfTag tag);
void ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
@@ -313,7 +311,7 @@ class DIEDispatcher: public Dwarf2Handler {
void ProcessAttributeBuffer(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
- const char* data,
+ const uint8_t *data,
uint64 len);
void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
diff --git a/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc b/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc
new file mode 100644
index 0000000..db70eb3
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc
@@ -0,0 +1,527 @@
+// -*- mode: c++ -*-
+
+// Copyright (c) 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
+
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "breakpad_googletest_includes.h"
+
+#include "common/dwarf/dwarf2diehandler.h"
+#include "common/using_std_string.h"
+
+using std::make_pair;
+
+using ::testing::_;
+using ::testing::ContainerEq;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::Sequence;
+using ::testing::StrEq;
+
+using dwarf2reader::DIEDispatcher;
+using dwarf2reader::DIEHandler;
+using dwarf2reader::DwarfAttribute;
+using dwarf2reader::DwarfForm;
+using dwarf2reader::DwarfTag;
+using dwarf2reader::RootDIEHandler;
+
+class MockDIEHandler: public DIEHandler {
+ public:
+ MOCK_METHOD3(ProcessAttributeUnsigned,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD3(ProcessAttributeSigned,
+ void(DwarfAttribute, DwarfForm, int64));
+ MOCK_METHOD3(ProcessAttributeReference,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD4(ProcessAttributeBuffer,
+ void(DwarfAttribute, DwarfForm, const uint8_t *, uint64));
+ MOCK_METHOD3(ProcessAttributeString,
+ void(DwarfAttribute, DwarfForm, const string &));
+ MOCK_METHOD3(ProcessAttributeSignature,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD0(EndAttributes, bool());
+ MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
+ MOCK_METHOD0(Finish, void());
+};
+
+class MockRootDIEHandler: public RootDIEHandler {
+ public:
+ MOCK_METHOD3(ProcessAttributeUnsigned,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD3(ProcessAttributeSigned,
+ void(DwarfAttribute, DwarfForm, int64));
+ MOCK_METHOD3(ProcessAttributeReference,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD4(ProcessAttributeBuffer,
+ void(DwarfAttribute, DwarfForm, const uint8_t *, uint64));
+ MOCK_METHOD3(ProcessAttributeString,
+ void(DwarfAttribute, DwarfForm, const string &));
+ MOCK_METHOD3(ProcessAttributeSignature,
+ void(DwarfAttribute, DwarfForm, uint64));
+ MOCK_METHOD0(EndAttributes, bool());
+ MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
+ MOCK_METHOD0(Finish, void());
+ MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8));
+ MOCK_METHOD2(StartRootDIE, bool(uint64, DwarfTag));
+};
+
+// If the handler elects to skip the compilation unit, the dispatcher
+// should tell the reader so.
+TEST(Dwarf2DIEHandler, SkipCompilationUnit) {
+ Sequence s;
+ MockRootDIEHandler mock_root_handler;
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x8d42aed77cfccf3eLL,
+ 0x89, 0xdc,
+ 0x2ecb4dc778a80f21LL,
+ 0x66))
+ .InSequence(s)
+ .WillOnce(Return(false));
+
+ EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
+ 0x89, 0xdc,
+ 0x2ecb4dc778a80f21LL,
+ 0x66));
+}
+
+// If the handler elects to skip the root DIE, the dispatcher should
+// tell the reader so.
+TEST(Dwarf2DIEHandler, SkipRootDIE) {
+ Sequence s;
+ MockRootDIEHandler mock_root_handler;
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02,
+ 0xb00febffa76e2b2bLL, 0x5c))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
+ .InSequence(s)
+ .WillOnce(Return(false));
+
+ EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL,
+ 0xf4, 0x02,
+ 0xb00febffa76e2b2bLL, 0x5c));
+ EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
+ (DwarfTag) 0xb4f98da6));
+ die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
+}
+
+// If the handler elects to skip the root DIE's children, the
+// dispatcher should tell the reader so --- and avoid deleting the
+// root handler.
+TEST(Dwarf2DIEHandler, SkipRootDIEChildren) {
+ MockRootDIEHandler mock_root_handler;
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0,
+ 0x09f8bf0767f91675LL, 0xdb))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
+ .WillOnce(Return(true));
+ // Please don't tell me about my children.
+ EXPECT_CALL(mock_root_handler, EndAttributes())
+ .WillOnce(Return(false));
+ EXPECT_CALL(mock_root_handler, Finish())
+ .WillOnce(Return());
+ }
+
+ EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL,
+ 0x26, 0xa0,
+ 0x09f8bf0767f91675LL, 0xdb));
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
+ (DwarfTag) 0xb4f98da6));
+ EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL,
+ (DwarfTag) 0xc3a17bba));
+ die_dispatcher.EndDIE(0x435150ceedccda18LL);
+ die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
+}
+
+// The dispatcher should pass attribute values through to the die
+// handler accurately.
+TEST(Dwarf2DIEHandler, PassAttributeValues) {
+ MockRootDIEHandler mock_root_handler;
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ const uint8_t buffer[10] = {
+ 0x24, 0x24, 0x35, 0x9a, 0xca, 0xcf, 0xa8, 0x84, 0xa7, 0x18
+ };
+ string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d";
+
+ // Set expectations.
+ {
+ InSequence s;
+
+ // We'll like the compilation unit header.
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc,
+ 0x2ecb4dc778a80f21LL, 0x66))
+ .WillOnce(Return(true));
+
+ // We'll like the root DIE.
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c))
+ .WillOnce(Return(true));
+
+ // Expect some attribute values.
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed,
+ (DwarfForm) 0x424f1468,
+ 0xa592571997facda1ULL))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0x43694dc9,
+ (DwarfForm) 0xf6f78901L,
+ 0x92602a4e3bf1f446LL))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeReference((DwarfAttribute) 0x4033e8cL,
+ (DwarfForm) 0xf66fbe0bL,
+ 0x50fddef44734fdecULL))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af,
+ (DwarfForm) 0xe99a539a,
+ buffer, sizeof(buffer)))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeString((DwarfAttribute) 0x310ed065,
+ (DwarfForm) 0x15762fec,
+ StrEq(str)))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeSignature((DwarfAttribute) 0x58790d72,
+ (DwarfForm) 0x4159f138,
+ 0x94682463613e6a5fULL))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler, EndAttributes())
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler, FindChildHandler(_, _))
+ .Times(0);
+ EXPECT_CALL(mock_root_handler, Finish())
+ .WillOnce(Return());
+ }
+
+ // Drive the dispatcher.
+
+ // Report the CU header.
+ EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
+ 0x89, 0xdc,
+ 0x2ecb4dc778a80f21LL,
+ 0x66));
+ // Report the root DIE.
+ EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL,
+ (DwarfTag) 0x9829445c));
+
+ // Report some attribute values.
+ die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x1cc0bfed,
+ (DwarfForm) 0x424f1468,
+ 0xa592571997facda1ULL);
+ die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x43694dc9,
+ (DwarfForm) 0xf6f78901,
+ 0x92602a4e3bf1f446LL);
+ die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x4033e8c,
+ (DwarfForm) 0xf66fbe0b,
+ 0x50fddef44734fdecULL);
+ die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x25d7e0af,
+ (DwarfForm) 0xe99a539a,
+ buffer, sizeof(buffer));
+ die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x310ed065,
+ (DwarfForm) 0x15762fec,
+ str);
+ die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL,
+ (DwarfAttribute) 0x58790d72,
+ (DwarfForm) 0x4159f138,
+ 0x94682463613e6a5fULL);
+
+ // Finish the root DIE (and thus the CU).
+ die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
+}
+
+TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
+ MockRootDIEHandler mock_root_handler;
+ MockDIEHandler *mock_child1_handler = new(MockDIEHandler);
+ MockDIEHandler *mock_child3_handler = new(MockDIEHandler);
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ {
+ InSequence s;
+
+ // We'll like the compilation unit header.
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
+ 0x47dd3c764275a216LL, 0xa5))
+ .WillOnce(Return(true));
+
+ // Root DIE.
+ {
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0xf779a642,
+ (DwarfForm) 0x2cb63027,
+ 0x18e744661769d08fLL))
+ .WillOnce(Return());
+ EXPECT_CALL(mock_root_handler, EndAttributes())
+ .WillOnce(Return(true));
+
+ // First child DIE.
+ EXPECT_CALL(mock_root_handler,
+ FindChildHandler(0x149f644f8116fe8cLL,
+ (DwarfTag) 0xac2cbd8c))
+ .WillOnce(Return(mock_child1_handler));
+ {
+ EXPECT_CALL(*mock_child1_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65,
+ (DwarfForm) 0xe4f64c41,
+ 0x1b04e5444a55fe67LL))
+ .WillOnce(Return());
+ EXPECT_CALL(*mock_child1_handler, EndAttributes())
+ .WillOnce(Return(false));
+ // Skip first grandchild DIE and first great-grandchild DIE.
+ EXPECT_CALL(*mock_child1_handler, Finish())
+ .WillOnce(Return());
+ }
+
+ // Second child DIE. Root handler will decline to return a handler
+ // for this child.
+ EXPECT_CALL(mock_root_handler,
+ FindChildHandler(0x97412be24875de9dLL,
+ (DwarfTag) 0x505a068b))
+ .WillOnce(Return((DIEHandler *) NULL));
+
+ // Third child DIE.
+ EXPECT_CALL(mock_root_handler,
+ FindChildHandler(0x753c964c8ab538aeLL,
+ (DwarfTag) 0x8c22970e))
+ .WillOnce(Return(mock_child3_handler));
+ {
+ EXPECT_CALL(*mock_child3_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
+ (DwarfForm) 0x610b7ae1,
+ 0x3ea5c609d7d7560fLL))
+ .WillOnce(Return());
+ EXPECT_CALL(*mock_child3_handler, EndAttributes())
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_child3_handler, Finish())
+ .WillOnce(Return());
+ }
+
+ EXPECT_CALL(mock_root_handler, Finish())
+ .WillOnce(Return());
+ }
+ }
+
+
+ // Drive the dispatcher.
+
+ // Report the CU header.
+ EXPECT_TRUE(die_dispatcher
+ .StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
+ 0x47dd3c764275a216LL, 0xa5));
+ // Report the root DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL,
+ (DwarfTag) 0xf5d60c59));
+ die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL,
+ (DwarfAttribute) 0xf779a642,
+ (DwarfForm) 0x2cb63027,
+ 0x18e744661769d08fLL);
+
+ // First child DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL,
+ (DwarfTag) 0xac2cbd8c));
+ die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL,
+ (DwarfAttribute) 0xa6fd6f65,
+ (DwarfForm) 0xe4f64c41,
+ 0x1b04e5444a55fe67LL);
+
+ // First grandchild DIE. Will be skipped.
+ {
+ EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL,
+ (DwarfTag) 0x22f05a15));
+ // First great-grandchild DIE. Will be skipped without being
+ // mentioned to any handler.
+ {
+ EXPECT_FALSE(die_dispatcher
+ .StartDIE(0xb3076285d25cac25LL,
+ (DwarfTag) 0xcff4061b));
+ die_dispatcher.EndDIE(0xb3076285d25cac25LL);
+ }
+ die_dispatcher.EndDIE(0xd68de1ee0bd29419LL);
+ }
+ die_dispatcher.EndDIE(0x149f644f8116fe8cLL);
+ }
+
+ // Second child DIE. Root handler will decline to find a handler for it.
+ {
+ EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL,
+ (DwarfTag) 0x505a068b));
+ die_dispatcher.EndDIE(0x97412be24875de9dLL);
+ }
+
+ // Third child DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL,
+ (DwarfTag) 0x8c22970e));
+ die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL,
+ (DwarfAttribute) 0x4e2b7cfb,
+ (DwarfForm) 0x610b7ae1,
+ 0x3ea5c609d7d7560fLL);
+ die_dispatcher.EndDIE(0x753c964c8ab538aeLL);
+ }
+
+ // Finish the root DIE (and thus the CU).
+ die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL);
+ }
+}
+
+// The DIEDispatcher destructor is supposed to delete all handlers on
+// the stack, except for the root.
+TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
+ MockRootDIEHandler mock_root_handler;
+ MockDIEHandler *mock_child_handler = new(MockDIEHandler);
+ MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler);
+
+ {
+ InSequence s;
+
+ // We'll like the compilation unit header.
+ EXPECT_CALL(mock_root_handler,
+ StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
+ 0x76d392ff393ddda2LL, 0xbf))
+ .WillOnce(Return(true));
+
+ // Root DIE.
+ {
+ EXPECT_CALL(mock_root_handler,
+ StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_root_handler, EndAttributes())
+ .WillOnce(Return(true));
+
+ // Child DIE.
+ EXPECT_CALL(mock_root_handler,
+ FindChildHandler(0x058f09240c5fc8c9LL,
+ (DwarfTag) 0x898bf0d0))
+ .WillOnce(Return(mock_child_handler));
+ {
+ EXPECT_CALL(*mock_child_handler, EndAttributes())
+ .WillOnce(Return(true));
+
+ // Grandchild DIE.
+ EXPECT_CALL(*mock_child_handler,
+ FindChildHandler(0x32dc00c9945dc0c8LL,
+ (DwarfTag) 0x2802d007))
+ .WillOnce(Return(mock_grandchild_handler));
+ {
+ EXPECT_CALL(*mock_grandchild_handler,
+ ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
+ (DwarfForm) 0x610b7ae1,
+ 0x3ea5c609d7d7560fLL))
+ .WillOnce(Return());
+
+ // At this point, we abandon the traversal, so none of the
+ // usual stuff should get called.
+ EXPECT_CALL(*mock_grandchild_handler, EndAttributes())
+ .Times(0);
+ EXPECT_CALL(*mock_grandchild_handler, Finish())
+ .Times(0);
+ }
+
+ EXPECT_CALL(*mock_child_handler, Finish())
+ .Times(0);
+ }
+
+ EXPECT_CALL(mock_root_handler, Finish())
+ .Times(0);
+ }
+ }
+
+ // The dispatcher.
+ DIEDispatcher die_dispatcher(&mock_root_handler);
+
+ // Report the CU header.
+ EXPECT_TRUE(die_dispatcher
+ .StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
+ 0x76d392ff393ddda2LL, 0xbf));
+ // Report the root DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL,
+ (DwarfTag) 0x98980361));
+
+ // Child DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL,
+ (DwarfTag) 0x898bf0d0));
+
+ // Grandchild DIE.
+ {
+ EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL,
+ (DwarfTag) 0x2802d007));
+ die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL,
+ (DwarfAttribute) 0x4e2b7cfb,
+ (DwarfForm) 0x610b7ae1,
+ 0x3ea5c609d7d7560fLL);
+
+ // Stop the traversal abruptly, so that there will still be
+ // handlers on the stack when the dispatcher is destructed.
+
+ // No EndDIE call...
+ }
+ // No EndDIE call...
+ }
+ // No EndDIE call...
+ }
+}
diff --git a/3rdParty/Breakpad/src/common/dwarf/dwarf2enums.h b/3rdParty/Breakpad/src/common/dwarf/dwarf2enums.h
index 5565d66..4316a89 100644
--- a/3rdParty/Breakpad/src/common/dwarf/dwarf2enums.h
+++ b/3rdParty/Breakpad/src/common/dwarf/dwarf2enums.h
@@ -149,7 +149,10 @@ enum DwarfForm {
DW_FORM_sec_offset = 0x17,
DW_FORM_exprloc = 0x18,
DW_FORM_flag_present = 0x19,
- DW_FORM_ref_sig8 = 0x20
+ DW_FORM_ref_sig8 = 0x20,
+ // Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
+ DW_FORM_GNU_addr_index = 0x1f01,
+ DW_FORM_GNU_str_index = 0x1f02
};
// Attribute names and codes
@@ -229,6 +232,8 @@ enum DwarfAttribute {
DW_AT_call_column = 0x57,
DW_AT_call_file = 0x58,
DW_AT_call_line = 0x59,
+ // DWARF 4
+ DW_AT_linkage_name = 0x6e,
// SGI/MIPS extensions.
DW_AT_MIPS_fde = 0x2001,
DW_AT_MIPS_loop_begin = 0x2002,
@@ -264,6 +269,13 @@ enum DwarfAttribute {
DW_AT_body_begin = 0x2105,
DW_AT_body_end = 0x2106,
DW_AT_GNU_vector = 0x2107,
+ // Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
+ DW_AT_GNU_dwo_name = 0x2130,
+ DW_AT_GNU_dwo_id = 0x2131,
+ DW_AT_GNU_ranges_base = 0x2132,
+ DW_AT_GNU_addr_base = 0x2133,
+ DW_AT_GNU_pubnames = 0x2134,
+ DW_AT_GNU_pubtypes = 0x2135,
// VMS extensions.
DW_AT_VMS_rtnbeg_pd_address = 0x2201,
// UPC extension.
@@ -489,9 +501,24 @@ enum DwarfOpcode {
DW_OP_call_frame_cfa =0x9c,
DW_OP_bit_piece =0x9d,
DW_OP_lo_user =0xe0,
- DW_OP_hi_user =0xff,
+ DW_OP_hi_user =0xff,
// GNU extensions
- DW_OP_GNU_push_tls_address =0xe0
+ DW_OP_GNU_push_tls_address =0xe0,
+ // Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
+ DW_OP_GNU_addr_index =0xfb,
+ DW_OP_GNU_const_index =0xfc
+};
+
+// Section identifiers for DWP files
+enum DwarfSectionId {
+ DW_SECT_INFO = 1,
+ DW_SECT_TYPES = 2,
+ DW_SECT_ABBREV = 3,
+ DW_SECT_LINE = 4,
+ DW_SECT_LOC = 5,
+ DW_SECT_STR_OFFSETS = 6,
+ DW_SECT_MACINFO = 7,
+ DW_SECT_MACRO = 8
};
// Source languages. These are values for DW_AT_language.
@@ -517,6 +544,8 @@ enum DwarfLanguage
DW_LANG_ObjC_plus_plus =0x0011,
DW_LANG_UPC =0x0012,
DW_LANG_D =0x0013,
+ DW_LANG_Rust =0x001c,
+ DW_LANG_Swift =0x001e,
// Implementation-defined language code range.
DW_LANG_lo_user = 0x8000,
DW_LANG_hi_user = 0xffff,
@@ -643,7 +672,7 @@ enum DwarfPointerEncoding
// encoding (except DW_EH_PE_aligned), and indicates that the
// encoded value represents the address at which the true address
// is stored, not the true address itself.
- DW_EH_PE_indirect = 0x80
+ DW_EH_PE_indirect = 0x80
};
} // namespace dwarf2reader
diff --git a/3rdParty/Breakpad/src/common/dwarf/dwarf2reader.cc b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader.cc
index 7c1a29d..8774122 100644
--- a/3rdParty/Breakpad/src/common/dwarf/dwarf2reader.cc
+++ b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader.cc
@@ -44,6 +44,8 @@
#include <string>
#include <utility>
+#include <sys/stat.h>
+
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/line_state_machine.h"
@@ -51,11 +53,38 @@
namespace dwarf2reader {
-CompilationUnit::CompilationUnit(const SectionMap& sections, uint64 offset,
+CompilationUnit::CompilationUnit(const string& path,
+ const SectionMap& sections, uint64 offset,
ByteReader* reader, Dwarf2Handler* handler)
- : offset_from_section_start_(offset), reader_(reader),
- sections_(sections), handler_(handler), abbrevs_(NULL),
- string_buffer_(NULL), string_buffer_length_(0) {}
+ : path_(path), offset_from_section_start_(offset), reader_(reader),
+ sections_(sections), handler_(handler), abbrevs_(),
+ string_buffer_(NULL), string_buffer_length_(0),
+ str_offsets_buffer_(NULL), str_offsets_buffer_length_(0),
+ addr_buffer_(NULL), addr_buffer_length_(0),
+ is_split_dwarf_(false), dwo_id_(0), dwo_name_(),
+ skeleton_dwo_id_(0), ranges_base_(0), addr_base_(0),
+ have_checked_for_dwp_(false), dwp_path_(),
+ dwp_byte_reader_(), dwp_reader_() {}
+
+// Initialize a compilation unit from a .dwo or .dwp file.
+// In this case, we need the .debug_addr section from the
+// executable file that contains the corresponding skeleton
+// compilation unit. We also inherit the Dwarf2Handler from
+// the executable file, and call it as if we were still
+// processing the original compilation unit.
+
+void CompilationUnit::SetSplitDwarf(const uint8_t* addr_buffer,
+ uint64 addr_buffer_length,
+ uint64 addr_base,
+ uint64 ranges_base,
+ uint64 dwo_id) {
+ is_split_dwarf_ = true;
+ addr_buffer_ = addr_buffer;
+ addr_buffer_length_ = addr_buffer_length;
+ addr_base_ = addr_base;
+ ranges_base_ = ranges_base;
+ skeleton_dwo_id_ = dwo_id;
+}
// Read a DWARF2/3 abbreviation section.
// Each abbrev consists of a abbreviation number, a tag, a byte
@@ -83,9 +112,9 @@ void CompilationUnit::ReadAbbrevs() {
// The only way to check whether we are reading over the end of the
// buffer would be to first compute the size of the leb128 data by
// reading it, then go back and read it again.
- const char* abbrev_start = iter->second.first +
+ const uint8_t *abbrev_start = iter->second.first +
header_.abbrev_offset;
- const char* abbrevptr = abbrev_start;
+ const uint8_t *abbrevptr = abbrev_start;
#ifndef NDEBUG
const uint64 abbrev_length = iter->second.second - header_.abbrev_offset;
#endif
@@ -132,8 +161,8 @@ void CompilationUnit::ReadAbbrevs() {
}
// Skips a single DIE's attributes.
-const char* CompilationUnit::SkipDIE(const char* start,
- const Abbrev& abbrev) {
+const uint8_t *CompilationUnit::SkipDIE(const uint8_t* start,
+ const Abbrev& abbrev) {
for (AttributeList::const_iterator i = abbrev.attributes.begin();
i != abbrev.attributes.end();
i++) {
@@ -143,8 +172,8 @@ const char* CompilationUnit::SkipDIE(const char* start,
}
// Skips a single attribute form's data.
-const char* CompilationUnit::SkipAttribute(const char* start,
- enum DwarfForm form) {
+const uint8_t *CompilationUnit::SkipAttribute(const uint8_t *start,
+ enum DwarfForm form) {
size_t len;
switch (form) {
@@ -171,9 +200,11 @@ const char* CompilationUnit::SkipAttribute(const char* start,
case DW_FORM_ref_sig8:
return start + 8;
case DW_FORM_string:
- return start + strlen(start) + 1;
+ return start + strlen(reinterpret_cast<const char *>(start)) + 1;
case DW_FORM_udata:
case DW_FORM_ref_udata:
+ case DW_FORM_GNU_str_index:
+ case DW_FORM_GNU_addr_index:
reader_->ReadUnsignedLEB128(start, &len);
return start + len;
@@ -183,14 +214,15 @@ const char* CompilationUnit::SkipAttribute(const char* start,
case DW_FORM_addr:
return start + reader_->AddressSize();
case DW_FORM_ref_addr:
- // DWARF2 and 3 differ on whether ref_addr is address size or
+ // DWARF2 and 3/4 differ on whether ref_addr is address size or
// offset size.
- assert(header_.version == 2 || header_.version == 3);
+ assert(header_.version >= 2);
if (header_.version == 2) {
return start + reader_->AddressSize();
- } else if (header_.version == 3) {
+ } else if (header_.version >= 3) {
return start + reader_->OffsetSize();
}
+ break;
case DW_FORM_block1:
return start + 1 + reader_->ReadOneByte(start);
@@ -217,7 +249,7 @@ const char* CompilationUnit::SkipAttribute(const char* start,
// the offset in the .debug_abbrev section for our abbrevs, and an
// address size.
void CompilationUnit::ReadHeader() {
- const char* headerptr = buffer_;
+ const uint8_t *headerptr = buffer_;
size_t initial_length_size;
assert(headerptr + 4 < buffer_ + buffer_length_);
@@ -234,7 +266,9 @@ void CompilationUnit::ReadHeader() {
header_.abbrev_offset = reader_->ReadOffset(headerptr);
headerptr += reader_->OffsetSize();
- assert(headerptr + 1 < buffer_ + buffer_length_);
+ // Compare against less than or equal because this may be the last
+ // section in the file.
+ assert(headerptr + 1 <= buffer_ + buffer_length_);
header_.address_size = reader_->ReadOneByte(headerptr);
reader_->SetAddressSize(header_.address_size);
headerptr += 1;
@@ -295,17 +329,39 @@ uint64 CompilationUnit::Start() {
string_buffer_length_ = iter->second.second;
}
+ // Set the string offsets section if we have one.
+ iter = sections_.find(".debug_str_offsets");
+ if (iter != sections_.end()) {
+ str_offsets_buffer_ = iter->second.first;
+ str_offsets_buffer_length_ = iter->second.second;
+ }
+
+ // Set the address section if we have one.
+ iter = sections_.find(".debug_addr");
+ if (iter != sections_.end()) {
+ addr_buffer_ = iter->second.first;
+ addr_buffer_length_ = iter->second.second;
+ }
+
// Now that we have our abbreviations, start processing DIE's.
ProcessDIEs();
+ // If this is a skeleton compilation unit generated with split DWARF,
+ // and the client needs the full debug info, we need to find the full
+ // compilation unit in a .dwo or .dwp file.
+ if (!is_split_dwarf_
+ && dwo_name_ != NULL
+ && handler_->NeedSplitDebugInfo())
+ ProcessSplitDwarf();
+
return ourlength;
}
// If one really wanted, you could merge SkipAttribute and
// ProcessAttribute
// This is all boring data manipulation and calling of the handler.
-const char* CompilationUnit::ProcessAttribute(
- uint64 dieoffset, const char* start, enum DwarfAttribute attr,
+const uint8_t *CompilationUnit::ProcessAttribute(
+ uint64 dieoffset, const uint8_t *start, enum DwarfAttribute attr,
enum DwarfForm form) {
size_t len;
@@ -319,48 +375,46 @@ const char* CompilationUnit::ProcessAttribute(
return ProcessAttribute(dieoffset, start, attr, form);
case DW_FORM_flag_present:
- handler_->ProcessAttributeUnsigned(dieoffset, attr, form, 1);
+ ProcessAttributeUnsigned(dieoffset, attr, form, 1);
return start;
case DW_FORM_data1:
case DW_FORM_flag:
- handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
- reader_->ReadOneByte(start));
+ ProcessAttributeUnsigned(dieoffset, attr, form,
+ reader_->ReadOneByte(start));
return start + 1;
case DW_FORM_data2:
- handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
- reader_->ReadTwoBytes(start));
+ ProcessAttributeUnsigned(dieoffset, attr, form,
+ reader_->ReadTwoBytes(start));
return start + 2;
case DW_FORM_data4:
- handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
- reader_->ReadFourBytes(start));
+ ProcessAttributeUnsigned(dieoffset, attr, form,
+ reader_->ReadFourBytes(start));
return start + 4;
case DW_FORM_data8:
- handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
- reader_->ReadEightBytes(start));
+ ProcessAttributeUnsigned(dieoffset, attr, form,
+ reader_->ReadEightBytes(start));
return start + 8;
case DW_FORM_string: {
- const char* str = start;
- handler_->ProcessAttributeString(dieoffset, attr, form,
- str);
+ const char *str = reinterpret_cast<const char *>(start);
+ ProcessAttributeString(dieoffset, attr, form, str);
return start + strlen(str) + 1;
}
case DW_FORM_udata:
- handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
- reader_->ReadUnsignedLEB128(start,
- &len));
+ ProcessAttributeUnsigned(dieoffset, attr, form,
+ reader_->ReadUnsignedLEB128(start, &len));
return start + len;
case DW_FORM_sdata:
- handler_->ProcessAttributeSigned(dieoffset, attr, form,
- reader_->ReadSignedLEB128(start, &len));
+ ProcessAttributeSigned(dieoffset, attr, form,
+ reader_->ReadSignedLEB128(start, &len));
return start + len;
case DW_FORM_addr:
- handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
- reader_->ReadAddress(start));
+ ProcessAttributeUnsigned(dieoffset, attr, form,
+ reader_->ReadAddress(start));
return start + reader_->AddressSize();
case DW_FORM_sec_offset:
- handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
- reader_->ReadOffset(start));
+ ProcessAttributeUnsigned(dieoffset, attr, form,
+ reader_->ReadOffset(start));
return start + reader_->OffsetSize();
case DW_FORM_ref1:
@@ -390,14 +444,14 @@ const char* CompilationUnit::ProcessAttribute(
+ offset_from_section_start_);
return start + len;
case DW_FORM_ref_addr:
- // DWARF2 and 3 differ on whether ref_addr is address size or
+ // DWARF2 and 3/4 differ on whether ref_addr is address size or
// offset size.
- assert(header_.version == 2 || header_.version == 3);
+ assert(header_.version >= 2);
if (header_.version == 2) {
handler_->ProcessAttributeReference(dieoffset, attr, form,
reader_->ReadAddress(start));
return start + reader_->AddressSize();
- } else if (header_.version == 3) {
+ } else if (header_.version >= 3) {
handler_->ProcessAttributeReference(dieoffset, attr, form,
reader_->ReadOffset(start));
return start + reader_->OffsetSize();
@@ -439,34 +493,66 @@ const char* CompilationUnit::ProcessAttribute(
const uint64 offset = reader_->ReadOffset(start);
assert(string_buffer_ + offset < string_buffer_ + string_buffer_length_);
- const char* str = string_buffer_ + offset;
- handler_->ProcessAttributeString(dieoffset, attr, form,
- str);
+ const char *str = reinterpret_cast<const char *>(string_buffer_ + offset);
+ ProcessAttributeString(dieoffset, attr, form, str);
return start + reader_->OffsetSize();
}
+
+ case DW_FORM_GNU_str_index: {
+ uint64 str_index = reader_->ReadUnsignedLEB128(start, &len);
+ const uint8_t* offset_ptr =
+ str_offsets_buffer_ + str_index * reader_->OffsetSize();
+ const uint64 offset = reader_->ReadOffset(offset_ptr);
+ if (offset >= string_buffer_length_) {
+ return NULL;
+ }
+
+ const char* str = reinterpret_cast<const char *>(string_buffer_) + offset;
+ ProcessAttributeString(dieoffset, attr, form, str);
+ return start + len;
+ break;
+ }
+ case DW_FORM_GNU_addr_index: {
+ uint64 addr_index = reader_->ReadUnsignedLEB128(start, &len);
+ const uint8_t* addr_ptr =
+ addr_buffer_ + addr_base_ + addr_index * reader_->AddressSize();
+ ProcessAttributeUnsigned(dieoffset, attr, form,
+ reader_->ReadAddress(addr_ptr));
+ return start + len;
+ }
}
fprintf(stderr, "Unhandled form type\n");
return NULL;
}
-const char* CompilationUnit::ProcessDIE(uint64 dieoffset,
- const char* start,
- const Abbrev& abbrev) {
+const uint8_t *CompilationUnit::ProcessDIE(uint64 dieoffset,
+ const uint8_t *start,
+ const Abbrev& abbrev) {
for (AttributeList::const_iterator i = abbrev.attributes.begin();
i != abbrev.attributes.end();
i++) {
start = ProcessAttribute(dieoffset, start, i->first, i->second);
}
+
+ // If this is a compilation unit in a split DWARF object, verify that
+ // the dwo_id matches. If it does not match, we will ignore this
+ // compilation unit.
+ if (abbrev.tag == DW_TAG_compile_unit
+ && is_split_dwarf_
+ && dwo_id_ != skeleton_dwo_id_) {
+ return NULL;
+ }
+
return start;
}
void CompilationUnit::ProcessDIEs() {
- const char* dieptr = after_header_;
+ const uint8_t *dieptr = after_header_;
size_t len;
// lengthstart is the place the length field is based on.
// It is the point in the header after the initial length field
- const char* lengthstart = buffer_;
+ const uint8_t *lengthstart = buffer_;
// In 64 bit dwarf, the initial length is 12 bytes, because of the
// 0xffffffff at the start.
@@ -500,7 +586,7 @@ void CompilationUnit::ProcessDIEs() {
const Abbrev& abbrev = abbrevs_->at(static_cast<size_t>(abbrev_num));
const enum DwarfTag tag = abbrev.tag;
- if (!handler_->StartDIE(absolute_offset, tag, abbrev.attributes)) {
+ if (!handler_->StartDIE(absolute_offset, tag)) {
dieptr = SkipDIE(dieptr, abbrev);
} else {
dieptr = ProcessDIE(absolute_offset, dieptr, abbrev);
@@ -514,10 +600,313 @@ void CompilationUnit::ProcessDIEs() {
}
}
-LineInfo::LineInfo(const char* buffer, uint64 buffer_length,
+// Check for a valid ELF file and return the Address size.
+// Returns 0 if not a valid ELF file.
+inline int GetElfWidth(const ElfReader& elf) {
+ if (elf.IsElf32File())
+ return 4;
+ if (elf.IsElf64File())
+ return 8;
+ return 0;
+}
+
+void CompilationUnit::ProcessSplitDwarf() {
+ struct stat statbuf;
+ if (!have_checked_for_dwp_) {
+ // Look for a .dwp file in the same directory as the executable.
+ have_checked_for_dwp_ = true;
+ string dwp_suffix(".dwp");
+ dwp_path_ = path_ + dwp_suffix;
+ if (stat(dwp_path_.c_str(), &statbuf) != 0) {
+ // Fall back to a split .debug file in the same directory.
+ string debug_suffix(".debug");
+ dwp_path_ = path_;
+ size_t found = path_.rfind(debug_suffix);
+ if (found + debug_suffix.length() == path_.length())
+ dwp_path_ = dwp_path_.replace(found, debug_suffix.length(), dwp_suffix);
+ }
+ if (stat(dwp_path_.c_str(), &statbuf) == 0) {
+ ElfReader* elf = new ElfReader(dwp_path_);
+ int width = GetElfWidth(*elf);
+ if (width != 0) {
+ dwp_byte_reader_.reset(new ByteReader(reader_->GetEndianness()));
+ dwp_byte_reader_->SetAddressSize(width);
+ dwp_reader_.reset(new DwpReader(*dwp_byte_reader_, elf));
+ dwp_reader_->Initialize();
+ } else {
+ delete elf;
+ }
+ }
+ }
+ bool found_in_dwp = false;
+ if (dwp_reader_) {
+ // If we have a .dwp file, read the debug sections for the requested CU.
+ SectionMap sections;
+ dwp_reader_->ReadDebugSectionsForCU(dwo_id_, &sections);
+ if (!sections.empty()) {
+ found_in_dwp = true;
+ CompilationUnit dwp_comp_unit(dwp_path_, sections, 0,
+ dwp_byte_reader_.get(), handler_);
+ dwp_comp_unit.SetSplitDwarf(addr_buffer_, addr_buffer_length_, addr_base_,
+ ranges_base_, dwo_id_);
+ dwp_comp_unit.Start();
+ }
+ }
+ if (!found_in_dwp) {
+ // If no .dwp file, try to open the .dwo file.
+ if (stat(dwo_name_, &statbuf) == 0) {
+ ElfReader elf(dwo_name_);
+ int width = GetElfWidth(elf);
+ if (width != 0) {
+ ByteReader reader(ENDIANNESS_LITTLE);
+ reader.SetAddressSize(width);
+ SectionMap sections;
+ ReadDebugSectionsFromDwo(&elf, &sections);
+ CompilationUnit dwo_comp_unit(dwo_name_, sections, 0, &reader,
+ handler_);
+ dwo_comp_unit.SetSplitDwarf(addr_buffer_, addr_buffer_length_,
+ addr_base_, ranges_base_, dwo_id_);
+ dwo_comp_unit.Start();
+ }
+ }
+ }
+}
+
+void CompilationUnit::ReadDebugSectionsFromDwo(ElfReader* elf_reader,
+ SectionMap* sections) {
+ static const char* const section_names[] = {
+ ".debug_abbrev",
+ ".debug_info",
+ ".debug_str_offsets",
+ ".debug_str"
+ };
+ for (unsigned int i = 0u;
+ i < sizeof(section_names)/sizeof(*(section_names)); ++i) {
+ string base_name = section_names[i];
+ string dwo_name = base_name + ".dwo";
+ size_t section_size;
+ const char* section_data = elf_reader->GetSectionByName(dwo_name,
+ &section_size);
+ if (section_data != NULL)
+ sections->insert(std::make_pair(
+ base_name, std::make_pair(
+ reinterpret_cast<const uint8_t *>(section_data),
+ section_size)));
+ }
+}
+
+DwpReader::DwpReader(const ByteReader& byte_reader, ElfReader* elf_reader)
+ : elf_reader_(elf_reader), byte_reader_(byte_reader),
+ cu_index_(NULL), cu_index_size_(0), string_buffer_(NULL),
+ string_buffer_size_(0), version_(0), ncolumns_(0), nunits_(0),
+ nslots_(0), phash_(NULL), pindex_(NULL), shndx_pool_(NULL),
+ offset_table_(NULL), size_table_(NULL), abbrev_data_(NULL),
+ abbrev_size_(0), info_data_(NULL), info_size_(0),
+ str_offsets_data_(NULL), str_offsets_size_(0) {}
+
+DwpReader::~DwpReader() {
+ if (elf_reader_) delete elf_reader_;
+}
+
+void DwpReader::Initialize() {
+ cu_index_ = elf_reader_->GetSectionByName(".debug_cu_index",
+ &cu_index_size_);
+ if (cu_index_ == NULL) {
+ return;
+ }
+ // The .debug_str.dwo section is shared by all CUs in the file.
+ string_buffer_ = elf_reader_->GetSectionByName(".debug_str.dwo",
+ &string_buffer_size_);
+
+ version_ = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(cu_index_));
+
+ if (version_ == 1) {
+ nslots_ = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(cu_index_)
+ + 3 * sizeof(uint32));
+ phash_ = cu_index_ + 4 * sizeof(uint32);
+ pindex_ = phash_ + nslots_ * sizeof(uint64);
+ shndx_pool_ = pindex_ + nslots_ * sizeof(uint32);
+ if (shndx_pool_ >= cu_index_ + cu_index_size_) {
+ version_ = 0;
+ }
+ } else if (version_ == 2) {
+ ncolumns_ = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(cu_index_) + sizeof(uint32));
+ nunits_ = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(cu_index_) + 2 * sizeof(uint32));
+ nslots_ = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(cu_index_) + 3 * sizeof(uint32));
+ phash_ = cu_index_ + 4 * sizeof(uint32);
+ pindex_ = phash_ + nslots_ * sizeof(uint64);
+ offset_table_ = pindex_ + nslots_ * sizeof(uint32);
+ size_table_ = offset_table_ + ncolumns_ * (nunits_ + 1) * sizeof(uint32);
+ abbrev_data_ = elf_reader_->GetSectionByName(".debug_abbrev.dwo",
+ &abbrev_size_);
+ info_data_ = elf_reader_->GetSectionByName(".debug_info.dwo", &info_size_);
+ str_offsets_data_ = elf_reader_->GetSectionByName(".debug_str_offsets.dwo",
+ &str_offsets_size_);
+ if (size_table_ >= cu_index_ + cu_index_size_) {
+ version_ = 0;
+ }
+ }
+}
+
+void DwpReader::ReadDebugSectionsForCU(uint64 dwo_id,
+ SectionMap* sections) {
+ if (version_ == 1) {
+ int slot = LookupCU(dwo_id);
+ if (slot == -1) {
+ return;
+ }
+
+ // The index table points to the section index pool, where we
+ // can read a list of section indexes for the debug sections
+ // for the CU whose dwo_id we are looking for.
+ int index = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(pindex_)
+ + slot * sizeof(uint32));
+ const char* shndx_list = shndx_pool_ + index * sizeof(uint32);
+ for (;;) {
+ if (shndx_list >= cu_index_ + cu_index_size_) {
+ version_ = 0;
+ return;
+ }
+ unsigned int shndx = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(shndx_list));
+ shndx_list += sizeof(uint32);
+ if (shndx == 0)
+ break;
+ const char* section_name = elf_reader_->GetSectionName(shndx);
+ size_t section_size;
+ const char* section_data;
+ // We're only interested in these four debug sections.
+ // The section names in the .dwo file end with ".dwo", but we
+ // add them to the sections table with their normal names.
+ if (!strncmp(section_name, ".debug_abbrev", strlen(".debug_abbrev"))) {
+ section_data = elf_reader_->GetSectionByIndex(shndx, &section_size);
+ sections->insert(std::make_pair(
+ ".debug_abbrev",
+ std::make_pair(reinterpret_cast<const uint8_t *> (section_data),
+ section_size)));
+ } else if (!strncmp(section_name, ".debug_info", strlen(".debug_info"))) {
+ section_data = elf_reader_->GetSectionByIndex(shndx, &section_size);
+ sections->insert(std::make_pair(
+ ".debug_info",
+ std::make_pair(reinterpret_cast<const uint8_t *> (section_data),
+ section_size)));
+ } else if (!strncmp(section_name, ".debug_str_offsets",
+ strlen(".debug_str_offsets"))) {
+ section_data = elf_reader_->GetSectionByIndex(shndx, &section_size);
+ sections->insert(std::make_pair(
+ ".debug_str_offsets",
+ std::make_pair(reinterpret_cast<const uint8_t *> (section_data),
+ section_size)));
+ }
+ }
+ sections->insert(std::make_pair(
+ ".debug_str",
+ std::make_pair(reinterpret_cast<const uint8_t *> (string_buffer_),
+ string_buffer_size_)));
+ } else if (version_ == 2) {
+ uint32 index = LookupCUv2(dwo_id);
+ if (index == 0) {
+ return;
+ }
+
+ // The index points to a row in each of the section offsets table
+ // and the section size table, where we can read the offsets and sizes
+ // of the contributions to each debug section from the CU whose dwo_id
+ // we are looking for. Row 0 of the section offsets table has the
+ // section ids for each column of the table. The size table begins
+ // with row 1.
+ const char* id_row = offset_table_;
+ const char* offset_row = offset_table_
+ + index * ncolumns_ * sizeof(uint32);
+ const char* size_row =
+ size_table_ + (index - 1) * ncolumns_ * sizeof(uint32);
+ if (size_row + ncolumns_ * sizeof(uint32) > cu_index_ + cu_index_size_) {
+ version_ = 0;
+ return;
+ }
+ for (unsigned int col = 0u; col < ncolumns_; ++col) {
+ uint32 section_id =
+ byte_reader_.ReadFourBytes(reinterpret_cast<const uint8_t *>(id_row)
+ + col * sizeof(uint32));
+ uint32 offset = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(offset_row)
+ + col * sizeof(uint32));
+ uint32 size = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(size_row) + col * sizeof(uint32));
+ if (section_id == DW_SECT_ABBREV) {
+ sections->insert(std::make_pair(
+ ".debug_abbrev",
+ std::make_pair(reinterpret_cast<const uint8_t *> (abbrev_data_)
+ + offset, size)));
+ } else if (section_id == DW_SECT_INFO) {
+ sections->insert(std::make_pair(
+ ".debug_info",
+ std::make_pair(reinterpret_cast<const uint8_t *> (info_data_)
+ + offset, size)));
+ } else if (section_id == DW_SECT_STR_OFFSETS) {
+ sections->insert(std::make_pair(
+ ".debug_str_offsets",
+ std::make_pair(reinterpret_cast<const uint8_t *> (str_offsets_data_)
+ + offset, size)));
+ }
+ }
+ sections->insert(std::make_pair(
+ ".debug_str",
+ std::make_pair(reinterpret_cast<const uint8_t *> (string_buffer_),
+ string_buffer_size_)));
+ }
+}
+
+int DwpReader::LookupCU(uint64 dwo_id) {
+ uint32 slot = static_cast<uint32>(dwo_id) & (nslots_ - 1);
+ uint64 probe = byte_reader_.ReadEightBytes(
+ reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64));
+ if (probe != 0 && probe != dwo_id) {
+ uint32 secondary_hash =
+ (static_cast<uint32>(dwo_id >> 32) & (nslots_ - 1)) | 1;
+ do {
+ slot = (slot + secondary_hash) & (nslots_ - 1);
+ probe = byte_reader_.ReadEightBytes(
+ reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64));
+ } while (probe != 0 && probe != dwo_id);
+ }
+ if (probe == 0)
+ return -1;
+ return slot;
+}
+
+uint32 DwpReader::LookupCUv2(uint64 dwo_id) {
+ uint32 slot = static_cast<uint32>(dwo_id) & (nslots_ - 1);
+ uint64 probe = byte_reader_.ReadEightBytes(
+ reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64));
+ uint32 index = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(pindex_) + slot * sizeof(uint32));
+ if (index != 0 && probe != dwo_id) {
+ uint32 secondary_hash =
+ (static_cast<uint32>(dwo_id >> 32) & (nslots_ - 1)) | 1;
+ do {
+ slot = (slot + secondary_hash) & (nslots_ - 1);
+ probe = byte_reader_.ReadEightBytes(
+ reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64));
+ index = byte_reader_.ReadFourBytes(
+ reinterpret_cast<const uint8_t *>(pindex_) + slot * sizeof(uint32));
+ } while (index != 0 && probe != dwo_id);
+ }
+ return index;
+}
+
+LineInfo::LineInfo(const uint8_t *buffer, uint64 buffer_length,
ByteReader* reader, LineInfoHandler* handler):
- handler_(handler), reader_(reader), buffer_(buffer),
- buffer_length_(buffer_length) {
+ handler_(handler), reader_(reader), buffer_(buffer) {
+#ifndef NDEBUG
+ buffer_length_ = buffer_length;
+#endif
header_.std_opcode_lengths = NULL;
}
@@ -530,7 +919,7 @@ uint64 LineInfo::Start() {
// The header for a debug_line section is mildly complicated, because
// the line info is very tightly encoded.
void LineInfo::ReadHeader() {
- const char* lineptr = buffer_;
+ const uint8_t *lineptr = buffer_;
size_t initial_length_size;
const uint64 initial_length
@@ -553,6 +942,13 @@ void LineInfo::ReadHeader() {
header_.min_insn_length = reader_->ReadOneByte(lineptr);
lineptr += 1;
+ if (header_.version >= 4) {
+ __attribute__((unused)) uint8 max_ops_per_insn =
+ reader_->ReadOneByte(lineptr);
+ ++lineptr;
+ assert(max_ops_per_insn == 1);
+ }
+
header_.default_is_stmt = reader_->ReadOneByte(lineptr);
lineptr += 1;
@@ -577,7 +973,7 @@ void LineInfo::ReadHeader() {
if (*lineptr) {
uint32 dirindex = 1;
while (*lineptr) {
- const char* dirname = lineptr;
+ const char *dirname = reinterpret_cast<const char *>(lineptr);
handler_->DefineDir(dirname, dirindex);
lineptr += strlen(dirname) + 1;
dirindex++;
@@ -590,7 +986,7 @@ void LineInfo::ReadHeader() {
uint32 fileindex = 1;
size_t len;
while (*lineptr) {
- const char* filename = lineptr;
+ const char *filename = reinterpret_cast<const char *>(lineptr);
lineptr += strlen(filename) + 1;
uint64 dirindex = reader_->ReadUnsignedLEB128(lineptr, &len);
@@ -615,7 +1011,7 @@ void LineInfo::ReadHeader() {
bool LineInfo::ProcessOneOpcode(ByteReader* reader,
LineInfoHandler* handler,
const struct LineInfoHeader &header,
- const char* start,
+ const uint8_t *start,
struct LineStateMachine* lsm,
size_t* len,
uintptr pc,
@@ -756,7 +1152,7 @@ bool LineInfo::ProcessOneOpcode(ByteReader* reader,
}
break;
case DW_LNE_define_file: {
- const char* filename = start;
+ const char *filename = reinterpret_cast<const char *>(start);
templen = strlen(filename) + 1;
start += templen;
@@ -803,7 +1199,7 @@ void LineInfo::ReadLines() {
// lengthstart is the place the length field is based on.
// It is the point in the header after the initial length field
- const char* lengthstart = buffer_;
+ const uint8_t *lengthstart = buffer_;
// In 64 bit dwarf, the initial length is 12 bytes, because of the
// 0xffffffff at the start.
@@ -812,7 +1208,7 @@ void LineInfo::ReadLines() {
else
lengthstart += 4;
- const char* lineptr = after_header_;
+ const uint8_t *lineptr = after_header_;
lsm.Reset(header_.default_is_stmt);
// The LineInfoHandler interface expects each line's length along
@@ -1311,7 +1707,7 @@ class CallFrameInfo::State {
const Entry *entry_;
// The next instruction to process.
- const char *cursor_;
+ const uint8_t *cursor_;
// The current set of rules.
RuleMap rules_;
@@ -1409,7 +1805,8 @@ bool CallFrameInfo::State::ParseOperands(const char *format,
if (len > bytes_left || expression_length > bytes_left - len)
return ReportIncomplete();
cursor_ += len;
- operands->expression = string(cursor_, expression_length);
+ operands->expression = string(reinterpret_cast<const char *>(cursor_),
+ expression_length);
cursor_ += expression_length;
break;
}
@@ -1512,16 +1909,19 @@ bool CallFrameInfo::State::DoInstruction() {
// Change the base register used to compute the CFA.
case DW_CFA_def_cfa_register: {
+ if (!ParseOperands("r", &ops)) return false;
Rule *cfa_rule = rules_.CFARule();
if (!cfa_rule) {
- reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+ if (!DoDefCFA(ops.register_number, ops.offset)) {
+ reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
+ return false;
+ }
+ } else {
+ cfa_rule->SetBaseRegister(ops.register_number);
+ if (!cfa_rule->Handle(handler_, address_,
+ Handler::kCFARegister))
return false;
}
- if (!ParseOperands("r", &ops)) return false;
- cfa_rule->SetBaseRegister(ops.register_number);
- if (!cfa_rule->Handle(handler_, address_,
- Handler::kCFARegister))
- return false;
break;
}
@@ -1759,8 +2159,8 @@ bool CallFrameInfo::State::DoRestore(unsigned reg) {
return DoRule(reg, rule);
}
-bool CallFrameInfo::ReadEntryPrologue(const char *cursor, Entry *entry) {
- const char *buffer_end = buffer_ + buffer_length_;
+bool CallFrameInfo::ReadEntryPrologue(const uint8_t *cursor, Entry *entry) {
+ const uint8_t *buffer_end = buffer_ + buffer_length_;
// Initialize enough of ENTRY for use in error reporting.
entry->offset = cursor - buffer_;
@@ -1838,7 +2238,7 @@ bool CallFrameInfo::ReadEntryPrologue(const char *cursor, Entry *entry) {
}
bool CallFrameInfo::ReadCIEFields(CIE *cie) {
- const char *cursor = cie->fields;
+ const uint8_t *cursor = cie->fields;
size_t len;
assert(cie->kind == kCIE);
@@ -1860,22 +2260,23 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
cursor++;
// If we don't recognize the version, we can't parse any more fields of the
- // CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a
- // version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
+ // CIE. For DWARF CFI, we handle versions 1 through 4 (there was never a
+ // version 2 of CFI data). For .eh_frame, we handle versions 1 and 4 as well;
// the difference between those versions seems to be the same as for
// .debug_frame.
- if (cie->version < 1 || cie->version > 3) {
+ if (cie->version < 1 || cie->version > 4) {
reporter_->UnrecognizedVersion(cie->offset, cie->version);
return false;
}
- const char *augmentation_start = cursor;
- const void *augmentation_end =
- memchr(augmentation_start, '\0', cie->end - augmentation_start);
+ const uint8_t *augmentation_start = cursor;
+ const uint8_t *augmentation_end =
+ reinterpret_cast<const uint8_t *>(memchr(augmentation_start, '\0',
+ cie->end - augmentation_start));
if (! augmentation_end) return ReportIncomplete(cie);
- cursor = static_cast<const char *>(augmentation_end);
- cie->augmentation = string(augmentation_start,
- cursor - augmentation_start);
+ cursor = augmentation_end;
+ cie->augmentation = string(reinterpret_cast<const char *>(augmentation_start),
+ cursor - augmentation_start);
// Skip the terminating '\0'.
cursor++;
@@ -1893,16 +2294,36 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
}
}
+ if (cie->version >= 4) {
+ uint8_t address_size = *cursor++;
+ if (address_size != 8) {
+ // TODO(scottmg): Only supporting x64 for now.
+ reporter_->UnexpectedAddressSize(cie->offset, address_size);
+ return false;
+ }
+
+ uint8_t segment_size = *cursor++;
+ if (segment_size != 0) {
+ // TODO(scottmg): Only supporting x64 for now.
+ // I would have perhaps expected 4 here, but LLVM emits a 0, near
+ // http://llvm.org/docs/doxygen/html/MCDwarf_8cpp_source.html#l00606. As
+ // we are not using the value, only succeed for now if it's the expected
+ // 0.
+ reporter_->UnexpectedSegmentSize(cie->offset, segment_size);
+ return false;
+ }
+ }
+
// Parse the code alignment factor.
cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len);
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
cursor += len;
-
+
// Parse the data alignment factor.
cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len);
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
cursor += len;
-
+
// Parse the return address register. This is a ubyte in version 1, and
// a ULEB128 in version 3.
if (cie->version == 1) {
@@ -1921,9 +2342,9 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
if (size_t(cie->end - cursor) < len + data_size)
return ReportIncomplete(cie);
cursor += len;
- const char *data = cursor;
+ const uint8_t *data = cursor;
cursor += data_size;
- const char *data_end = cursor;
+ const uint8_t *data_end = cursor;
cie->has_z_lsda = false;
cie->has_z_personality = false;
@@ -2013,9 +2434,9 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
return true;
}
-
+
bool CallFrameInfo::ReadFDEFields(FDE *fde) {
- const char *cursor = fde->fields;
+ const uint8_t *cursor = fde->fields;
size_t size;
fde->address = reader_->ReadEncodedPointer(cursor, fde->cie->pointer_encoding,
@@ -2081,10 +2502,10 @@ bool CallFrameInfo::ReadFDEFields(FDE *fde) {
}
bool CallFrameInfo::Start() {
- const char *buffer_end = buffer_ + buffer_length_;
- const char *cursor;
+ const uint8_t *buffer_end = buffer_ + buffer_length_;
+ const uint8_t *cursor;
bool all_ok = true;
- const char *entry_end;
+ const uint8_t *entry_end;
bool ok;
// Traverse all the entries in buffer_, skipping CIEs and offering
@@ -2254,6 +2675,22 @@ void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) {
filename_.c_str(), offset, section_.c_str(), cie_offset);
}
+void CallFrameInfo::Reporter::UnexpectedAddressSize(uint64 offset,
+ uint8_t address_size) {
+ fprintf(stderr,
+ "%s: CFI frame description entry at offset 0x%llx in '%s':"
+ " CIE specifies unexpected address size: %d\n",
+ filename_.c_str(), offset, section_.c_str(), address_size);
+}
+
+void CallFrameInfo::Reporter::UnexpectedSegmentSize(uint64 offset,
+ uint8_t segment_size) {
+ fprintf(stderr,
+ "%s: CFI frame description entry at offset 0x%llx in '%s':"
+ " CIE specifies unexpected segment size: %d\n",
+ filename_.c_str(), offset, section_.c_str(), segment_size);
+}
+
void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
fprintf(stderr,
"%s: CFI frame description entry at offset 0x%llx in '%s':"
diff --git a/3rdParty/Breakpad/src/common/dwarf/dwarf2reader.h b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader.h
index ecf4eb2..5d2d7f6 100644
--- a/3rdParty/Breakpad/src/common/dwarf/dwarf2reader.h
+++ b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader.h
@@ -40,25 +40,30 @@
#ifndef COMMON_DWARF_DWARF2READER_H__
#define COMMON_DWARF_DWARF2READER_H__
+#include <stdint.h>
+
#include <list>
#include <map>
#include <string>
#include <utility>
#include <vector>
+#include <memory>
#include "common/dwarf/bytereader.h"
#include "common/dwarf/dwarf2enums.h"
#include "common/dwarf/types.h"
#include "common/using_std_string.h"
+#include "common/dwarf/elf_reader.h"
namespace dwarf2reader {
struct LineStateMachine;
class Dwarf2Handler;
class LineInfoHandler;
+class DwpReader;
// This maps from a string naming a section to a pair containing a
// the data for the section, and the size of the section.
-typedef std::map<string, std::pair<const char*, uint64> > SectionMap;
+typedef std::map<string, std::pair<const uint8_t *, uint64> > SectionMap;
typedef std::list<std::pair<enum DwarfAttribute, enum DwarfForm> >
AttributeList;
typedef AttributeList::iterator AttributeIterator;
@@ -85,7 +90,7 @@ class LineInfo {
// to the beginning and length of the line information to read.
// Reader is a ByteReader class that has the endianness set
// properly.
- LineInfo(const char* buffer_, uint64 buffer_length,
+ LineInfo(const uint8_t *buffer_, uint64 buffer_length,
ByteReader* reader, LineInfoHandler* handler);
virtual ~LineInfo() {
@@ -111,7 +116,7 @@ class LineInfo {
static bool ProcessOneOpcode(ByteReader* reader,
LineInfoHandler* handler,
const struct LineInfoHeader &header,
- const char* start,
+ const uint8_t *start,
struct LineStateMachine* lsm,
size_t* len,
uintptr pc,
@@ -139,9 +144,11 @@ class LineInfo {
// buffer is the buffer for our line info, starting at exactly where
// the line info to read is. after_header is the place right after
// the end of the line information header.
- const char* buffer_;
+ const uint8_t *buffer_;
+#ifndef NDEBUG
uint64 buffer_length_;
- const char* after_header_;
+#endif
+ const uint8_t *after_header_;
};
// This class is the main interface between the line info reader and
@@ -180,6 +187,106 @@ class LineInfoHandler {
uint32 file_num, uint32 line_num, uint32 column_num) { }
};
+// This class is the main interface between the reader and the
+// client. The virtual functions inside this get called for
+// interesting events that happen during DWARF2 reading.
+// The default implementation skips everything.
+class Dwarf2Handler {
+ public:
+ Dwarf2Handler() { }
+
+ virtual ~Dwarf2Handler() { }
+
+ // Start to process a compilation unit at OFFSET from the beginning of the
+ // .debug_info section. Return false if you would like to skip this
+ // compilation unit.
+ virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
+ uint8 offset_size, uint64 cu_length,
+ uint8 dwarf_version) { return false; }
+
+ // When processing a skeleton compilation unit, resulting from a split
+ // DWARF compilation, once the skeleton debug info has been read,
+ // the reader will call this function to ask the client if it needs
+ // the full debug info from the .dwo or .dwp file. Return true if
+ // you need it, or false to skip processing the split debug info.
+ virtual bool NeedSplitDebugInfo() { return true; }
+
+ // Start to process a split compilation unit at OFFSET from the beginning of
+ // the debug_info section in the .dwp/.dwo file. Return false if you would
+ // like to skip this compilation unit.
+ virtual bool StartSplitCompilationUnit(uint64 offset,
+ uint64 cu_length) { return false; }
+
+ // Start to process a DIE at OFFSET from the beginning of the .debug_info
+ // section. Return false if you would like to skip this DIE.
+ virtual bool StartDIE(uint64 offset, enum DwarfTag tag) { return false; }
+
+ // Called when we have an attribute with unsigned data to give to our
+ // handler. The attribute is for the DIE at OFFSET from the beginning of the
+ // .debug_info section. Its name is ATTR, its form is FORM, and its value is
+ // DATA.
+ virtual void ProcessAttributeUnsigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data) { }
+
+ // Called when we have an attribute with signed data to give to our handler.
+ // The attribute is for the DIE at OFFSET from the beginning of the
+ // .debug_info section. Its name is ATTR, its form is FORM, and its value is
+ // DATA.
+ virtual void ProcessAttributeSigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ int64 data) { }
+
+ // Called when we have an attribute whose value is a reference to
+ // another DIE. The attribute belongs to the DIE at OFFSET from the
+ // beginning of the .debug_info section. Its name is ATTR, its form
+ // is FORM, and the offset of the DIE being referred to from the
+ // beginning of the .debug_info section is DATA.
+ virtual void ProcessAttributeReference(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data) { }
+
+ // Called when we have an attribute with a buffer of data to give to our
+ // handler. The attribute is for the DIE at OFFSET from the beginning of the
+ // .debug_info section. Its name is ATTR, its form is FORM, DATA points to
+ // the buffer's contents, and its length in bytes is LENGTH. The buffer is
+ // owned by the caller, not the callee, and may not persist for very long.
+ // If you want the data to be available later, it needs to be copied.
+ virtual void ProcessAttributeBuffer(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const uint8_t *data,
+ uint64 len) { }
+
+ // Called when we have an attribute with string data to give to our handler.
+ // The attribute is for the DIE at OFFSET from the beginning of the
+ // .debug_info section. Its name is ATTR, its form is FORM, and its value is
+ // DATA.
+ virtual void ProcessAttributeString(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const string& data) { }
+
+ // Called when we have an attribute whose value is the 64-bit signature
+ // of a type unit in the .debug_types section. OFFSET is the offset of
+ // the DIE whose attribute we're reporting. ATTR and FORM are the
+ // attribute's name and form. SIGNATURE is the type unit's signature.
+ virtual void ProcessAttributeSignature(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 signature) { }
+
+ // Called when finished processing the DIE at OFFSET.
+ // Because DWARF2/3 specifies a tree of DIEs, you may get starts
+ // before ends of the previous DIE, as we process children before
+ // ending the parent.
+ virtual void EndDIE(uint64 offset) { }
+
+};
+
// The base of DWARF2/3 debug info is a DIE (Debugging Information
// Entry.
// DWARF groups DIE's into a tree and calls the root of this tree a
@@ -221,12 +328,21 @@ class CompilationUnit {
// Initialize a compilation unit. This requires a map of sections,
// the offset of this compilation unit in the .debug_info section, a
// ByteReader, and a Dwarf2Handler class to call callbacks in.
- CompilationUnit(const SectionMap& sections, uint64 offset,
+ CompilationUnit(const string& path, const SectionMap& sections, uint64 offset,
ByteReader* reader, Dwarf2Handler* handler);
virtual ~CompilationUnit() {
if (abbrevs_) delete abbrevs_;
}
+ // Initialize a compilation unit from a .dwo or .dwp file.
+ // In this case, we need the .debug_addr section from the
+ // executable file that contains the corresponding skeleton
+ // compilation unit. We also inherit the Dwarf2Handler from
+ // the executable file, and call it as if we were still
+ // processing the original compilation unit.
+ void SetSplitDwarf(const uint8_t* addr_buffer, uint64 addr_buffer_length,
+ uint64 addr_base, uint64 ranges_base, uint64 dwo_id);
+
// Begin reading a Dwarf2 compilation unit, and calling the
// callbacks in the Dwarf2Handler
@@ -266,29 +382,104 @@ class CompilationUnit {
// Processes a single DIE for this compilation unit and return a new
// pointer just past the end of it
- const char* ProcessDIE(uint64 dieoffset,
- const char* start,
- const Abbrev& abbrev);
+ const uint8_t *ProcessDIE(uint64 dieoffset,
+ const uint8_t *start,
+ const Abbrev& abbrev);
// Processes a single attribute and return a new pointer just past the
// end of it
- const char* ProcessAttribute(uint64 dieoffset,
- const char* start,
- enum DwarfAttribute attr,
- enum DwarfForm form);
+ const uint8_t *ProcessAttribute(uint64 dieoffset,
+ const uint8_t *start,
+ enum DwarfAttribute attr,
+ enum DwarfForm form);
+
+ // Called when we have an attribute with unsigned data to give to
+ // our handler. The attribute is for the DIE at OFFSET from the
+ // beginning of compilation unit, has a name of ATTR, a form of
+ // FORM, and the actual data of the attribute is in DATA.
+ // If we see a DW_AT_GNU_dwo_id attribute, save the value so that
+ // we can find the debug info in a .dwo or .dwp file.
+ void ProcessAttributeUnsigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data) {
+ if (attr == DW_AT_GNU_dwo_id) {
+ dwo_id_ = data;
+ }
+ else if (attr == DW_AT_GNU_addr_base) {
+ addr_base_ = data;
+ }
+ else if (attr == DW_AT_GNU_ranges_base) {
+ ranges_base_ = data;
+ }
+ // TODO(yunlian): When we add DW_AT_ranges_base from DWARF-5,
+ // that base will apply to DW_AT_ranges attributes in the
+ // skeleton CU as well as in the .dwo/.dwp files.
+ else if (attr == DW_AT_ranges && is_split_dwarf_) {
+ data += ranges_base_;
+ }
+ handler_->ProcessAttributeUnsigned(offset, attr, form, data);
+ }
+
+ // Called when we have an attribute with signed data to give to
+ // our handler. The attribute is for the DIE at OFFSET from the
+ // beginning of compilation unit, has a name of ATTR, a form of
+ // FORM, and the actual data of the attribute is in DATA.
+ void ProcessAttributeSigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ int64 data) {
+ handler_->ProcessAttributeSigned(offset, attr, form, data);
+ }
+
+ // Called when we have an attribute with a buffer of data to give to
+ // our handler. The attribute is for the DIE at OFFSET from the
+ // beginning of compilation unit, has a name of ATTR, a form of
+ // FORM, and the actual data of the attribute is in DATA, and the
+ // length of the buffer is LENGTH.
+ void ProcessAttributeBuffer(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const uint8_t* data,
+ uint64 len) {
+ handler_->ProcessAttributeBuffer(offset, attr, form, data, len);
+ }
+
+ // Called when we have an attribute with string data to give to
+ // our handler. The attribute is for the DIE at OFFSET from the
+ // beginning of compilation unit, has a name of ATTR, a form of
+ // FORM, and the actual data of the attribute is in DATA.
+ // If we see a DW_AT_GNU_dwo_name attribute, save the value so
+ // that we can find the debug info in a .dwo or .dwp file.
+ void ProcessAttributeString(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const char* data) {
+ if (attr == DW_AT_GNU_dwo_name)
+ dwo_name_ = data;
+ handler_->ProcessAttributeString(offset, attr, form, data);
+ }
// Processes all DIEs for this compilation unit
void ProcessDIEs();
// Skips the die with attributes specified in ABBREV starting at
// START, and return the new place to position the stream to.
- const char* SkipDIE(const char* start,
- const Abbrev& abbrev);
+ const uint8_t *SkipDIE(const uint8_t *start, const Abbrev& abbrev);
// Skips the attribute starting at START, with FORM, and return the
// new place to position the stream to.
- const char* SkipAttribute(const char* start,
- enum DwarfForm form);
+ const uint8_t *SkipAttribute(const uint8_t *start, enum DwarfForm form);
+
+ // Process the actual debug information in a split DWARF file.
+ void ProcessSplitDwarf();
+
+ // Read the debug sections from a .dwo file.
+ void ReadDebugSectionsFromDwo(ElfReader* elf_reader,
+ SectionMap* sections);
+
+ // Path of the file containing the debug information.
+ const string path_;
// Offset from section start is the offset of this compilation unit
// from the beginning of the .debug_info section.
@@ -297,9 +488,9 @@ class CompilationUnit {
// buffer is the buffer for our CU, starting at .debug_info + offset
// passed in from constructor.
// after_header points to right after the compilation unit header.
- const char* buffer_;
+ const uint8_t *buffer_;
uint64 buffer_length_;
- const char* after_header_;
+ const uint8_t *after_header_;
// The associated ByteReader that handles endianness issues for us
ByteReader* reader_;
@@ -318,97 +509,143 @@ class CompilationUnit {
// String section buffer and length, if we have a string section.
// This is here to avoid doing a section lookup for strings in
// ProcessAttribute, which is in the hot path for DWARF2 reading.
- const char* string_buffer_;
+ const uint8_t *string_buffer_;
uint64 string_buffer_length_;
-};
-// This class is the main interface between the reader and the
-// client. The virtual functions inside this get called for
-// interesting events that happen during DWARF2 reading.
-// The default implementation skips everything.
+ // String offsets section buffer and length, if we have a string offsets
+ // section (.debug_str_offsets or .debug_str_offsets.dwo).
+ const uint8_t* str_offsets_buffer_;
+ uint64 str_offsets_buffer_length_;
-class Dwarf2Handler {
+ // Address section buffer and length, if we have an address section
+ // (.debug_addr).
+ const uint8_t* addr_buffer_;
+ uint64 addr_buffer_length_;
+
+ // Flag indicating whether this compilation unit is part of a .dwo
+ // or .dwp file. If true, we are reading this unit because a
+ // skeleton compilation unit in an executable file had a
+ // DW_AT_GNU_dwo_name or DW_AT_GNU_dwo_id attribute.
+ // In a .dwo file, we expect the string offsets section to
+ // have a ".dwo" suffix, and we will use the ".debug_addr" section
+ // associated with the skeleton compilation unit.
+ bool is_split_dwarf_;
+
+ // The value of the DW_AT_GNU_dwo_id attribute, if any.
+ uint64 dwo_id_;
+
+ // The value of the DW_AT_GNU_dwo_name attribute, if any.
+ const char* dwo_name_;
+
+ // If this is a split DWARF CU, the value of the DW_AT_GNU_dwo_id attribute
+ // from the skeleton CU.
+ uint64 skeleton_dwo_id_;
+
+ // The value of the DW_AT_GNU_ranges_base attribute, if any.
+ uint64 ranges_base_;
+
+ // The value of the DW_AT_GNU_addr_base attribute, if any.
+ uint64 addr_base_;
+
+ // True if we have already looked for a .dwp file.
+ bool have_checked_for_dwp_;
+
+ // Path to the .dwp file.
+ string dwp_path_;
+
+ // ByteReader for the DWP file.
+ std::unique_ptr<ByteReader> dwp_byte_reader_;
+
+ // DWP reader.
+ std::unique_ptr<DwpReader> dwp_reader_;
+};
+
+// A Reader for a .dwp file. Supports the fetching of DWARF debug
+// info for a given dwo_id.
+//
+// There are two versions of .dwp files. In both versions, the
+// .dwp file is an ELF file containing only debug sections.
+// In Version 1, the file contains many copies of each debug
+// section, one for each .dwo file that is packaged in the .dwp
+// file, and the .debug_cu_index section maps from the dwo_id
+// to a set of section indexes. In Version 2, the file contains
+// one of each debug section, and the .debug_cu_index section
+// maps from the dwo_id to a set of offsets and lengths that
+// identify each .dwo file's contribution to the larger sections.
+
+class DwpReader {
public:
- Dwarf2Handler() { }
+ DwpReader(const ByteReader& byte_reader, ElfReader* elf_reader);
- virtual ~Dwarf2Handler() { }
+ ~DwpReader();
- // Start to process a compilation unit at OFFSET from the beginning of the
- // .debug_info section. Return false if you would like to skip this
- // compilation unit.
- virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
- uint8 offset_size, uint64 cu_length,
- uint8 dwarf_version) { return false; }
+ // Read the CU index and initialize data members.
+ void Initialize();
- // Start to process a DIE at OFFSET from the beginning of the .debug_info
- // section. Return false if you would like to skip this DIE.
- virtual bool StartDIE(uint64 offset, enum DwarfTag tag,
- const AttributeList& attrs) { return false; }
+ // Read the debug sections for the given dwo_id.
+ void ReadDebugSectionsForCU(uint64 dwo_id, SectionMap* sections);
- // Called when we have an attribute with unsigned data to give to our
- // handler. The attribute is for the DIE at OFFSET from the beginning of the
- // .debug_info section. Its name is ATTR, its form is FORM, and its value is
- // DATA.
- virtual void ProcessAttributeUnsigned(uint64 offset,
- enum DwarfAttribute attr,
- enum DwarfForm form,
- uint64 data) { }
+ private:
+ // Search a v1 hash table for "dwo_id". Returns the slot index
+ // where the dwo_id was found, or -1 if it was not found.
+ int LookupCU(uint64 dwo_id);
- // Called when we have an attribute with signed data to give to our handler.
- // The attribute is for the DIE at OFFSET from the beginning of the
- // .debug_info section. Its name is ATTR, its form is FORM, and its value is
- // DATA.
- virtual void ProcessAttributeSigned(uint64 offset,
- enum DwarfAttribute attr,
- enum DwarfForm form,
- int64 data) { }
+ // Search a v2 hash table for "dwo_id". Returns the row index
+ // in the offsets and sizes tables, or 0 if it was not found.
+ uint32 LookupCUv2(uint64 dwo_id);
- // Called when we have an attribute whose value is a reference to
- // another DIE. The attribute belongs to the DIE at OFFSET from the
- // beginning of the .debug_info section. Its name is ATTR, its form
- // is FORM, and the offset of the DIE being referred to from the
- // beginning of the .debug_info section is DATA.
- virtual void ProcessAttributeReference(uint64 offset,
- enum DwarfAttribute attr,
- enum DwarfForm form,
- uint64 data) { }
+ // The ELF reader for the .dwp file.
+ ElfReader* elf_reader_;
- // Called when we have an attribute with a buffer of data to give to our
- // handler. The attribute is for the DIE at OFFSET from the beginning of the
- // .debug_info section. Its name is ATTR, its form is FORM, DATA points to
- // the buffer's contents, and its length in bytes is LENGTH. The buffer is
- // owned by the caller, not the callee, and may not persist for very long.
- // If you want the data to be available later, it needs to be copied.
- virtual void ProcessAttributeBuffer(uint64 offset,
- enum DwarfAttribute attr,
- enum DwarfForm form,
- const char* data,
- uint64 len) { }
+ // The ByteReader for the .dwp file.
+ const ByteReader& byte_reader_;
- // Called when we have an attribute with string data to give to our handler.
- // The attribute is for the DIE at OFFSET from the beginning of the
- // .debug_info section. Its name is ATTR, its form is FORM, and its value is
- // DATA.
- virtual void ProcessAttributeString(uint64 offset,
- enum DwarfAttribute attr,
- enum DwarfForm form,
- const string& data) { }
+ // Pointer to the .debug_cu_index section.
+ const char* cu_index_;
- // Called when we have an attribute whose value is the 64-bit signature
- // of a type unit in the .debug_types section. OFFSET is the offset of
- // the DIE whose attribute we're reporting. ATTR and FORM are the
- // attribute's name and form. SIGNATURE is the type unit's signature.
- virtual void ProcessAttributeSignature(uint64 offset,
- enum DwarfAttribute attr,
- enum DwarfForm form,
- uint64 signature) { }
+ // Size of the .debug_cu_index section.
+ size_t cu_index_size_;
- // Called when finished processing the DIE at OFFSET.
- // Because DWARF2/3 specifies a tree of DIEs, you may get starts
- // before ends of the previous DIE, as we process children before
- // ending the parent.
- virtual void EndDIE(uint64 offset) { }
+ // Pointer to the .debug_str.dwo section.
+ const char* string_buffer_;
+ // Size of the .debug_str.dwo section.
+ size_t string_buffer_size_;
+
+ // Version of the .dwp file. We support versions 1 and 2 currently.
+ int version_;
+
+ // Number of columns in the section tables (version 2).
+ unsigned int ncolumns_;
+
+ // Number of units in the section tables (version 2).
+ unsigned int nunits_;
+
+ // Number of slots in the hash table.
+ unsigned int nslots_;
+
+ // Pointer to the beginning of the hash table.
+ const char* phash_;
+
+ // Pointer to the beginning of the index table.
+ const char* pindex_;
+
+ // Pointer to the beginning of the section index pool (version 1).
+ const char* shndx_pool_;
+
+ // Pointer to the beginning of the section offset table (version 2).
+ const char* offset_table_;
+
+ // Pointer to the beginning of the section size table (version 2).
+ const char* size_table_;
+
+ // Contents of the sections of interest (version 2).
+ const char* abbrev_data_;
+ size_t abbrev_size_;
+ const char* info_data_;
+ size_t info_size_;
+ const char* str_offsets_data_;
+ size_t str_offsets_size_;
};
// This class is a reader for DWARF's Call Frame Information. CFI
@@ -638,7 +875,7 @@ class CallFrameInfo {
// The mechanics of C++ exception handling, personality routines,
// and language-specific data areas are described here, rather nicely:
// http://www.codesourcery.com/public/cxx-abi/abi-eh.html
- CallFrameInfo(const char *buffer, size_t buffer_length,
+ CallFrameInfo(const uint8_t *buffer, size_t buffer_length,
ByteReader *reader, Handler *handler, Reporter *reporter,
bool eh_frame = false)
: buffer_(buffer), buffer_length_(buffer_length),
@@ -666,7 +903,7 @@ class CallFrameInfo {
size_t offset;
// The start of this entry in the buffer.
- const char *start;
+ const uint8_t *start;
// Which kind of entry this is.
//
@@ -677,16 +914,16 @@ class CallFrameInfo {
// The end of this entry's common prologue (initial length and id), and
// the start of this entry's kind-specific fields.
- const char *fields;
+ const uint8_t *fields;
// The start of this entry's instructions.
- const char *instructions;
+ const uint8_t *instructions;
// The address past the entry's last byte in the buffer. (Note that
// since offset points to the entry's initial length field, and the
// length field is the number of bytes after that field, this is not
// simply buffer_ + offset + length.)
- const char *end;
+ const uint8_t *end;
// For both DWARF CFI and .eh_frame sections, this is the CIE id in a
// CIE, and the offset of the associated CIE in an FDE.
@@ -763,7 +1000,7 @@ class CallFrameInfo {
// true. On failure, report the problem, and return false. Even if we
// return false, set ENTRY->end to the first byte after the entry if we
// were able to figure that out, or NULL if we weren't.
- bool ReadEntryPrologue(const char *cursor, Entry *entry);
+ bool ReadEntryPrologue(const uint8_t *cursor, Entry *entry);
// Parse the fields of a CIE after the entry prologue, including any 'z'
// augmentation data. Assume that the 'Entry' fields of CIE are
@@ -791,7 +1028,7 @@ class CallFrameInfo {
}
// The contents of the DWARF .debug_info section we're parsing.
- const char *buffer_;
+ const uint8_t *buffer_;
size_t buffer_length_;
// For reading multi-byte values with the appropriate endianness.
@@ -990,6 +1227,14 @@ class CallFrameInfo::Reporter {
// there is not a CIE.
virtual void BadCIEId(uint64 offset, uint64 cie_offset);
+ // The FDE at OFFSET refers to a CIE with an address size we don't know how
+ // to handle.
+ virtual void UnexpectedAddressSize(uint64 offset, uint8_t address_size);
+
+ // The FDE at OFFSET refers to a CIE with an segment descriptor size we
+ // don't know how to handle.
+ virtual void UnexpectedSegmentSize(uint64 offset, uint8_t segment_size);
+
// The FDE at OFFSET refers to a CIE with version number VERSION,
// which we don't recognize. We cannot parse DWARF CFI if it uses
// a version number we don't recognize.
diff --git a/3rdParty/Breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc
new file mode 100644
index 0000000..38cc7ea
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc
@@ -0,0 +1,2555 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dwarf2reader_cfi_unittest.cc: Unit tests for dwarf2reader::CallFrameInfo
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+// The '.eh_frame' format, used by the Linux C++ ABI for exception
+// handling, is poorly specified. To help test our support for .eh_frame,
+// if you #define WRITE_ELF while compiling this file, and add the
+// 'include' directory from the binutils, gcc, or gdb source tree to the
+// #include path, then each test that calls the
+// PERHAPS_WRITE_DEBUG_FRAME_FILE or PERHAPS_WRITE_EH_FRAME_FILE will write
+// an ELF file containing a .debug_frame or .eh_frame section; you can then
+// use tools like readelf to examine the test data, and check the tools'
+// interpretation against the test's intentions. Each ELF file is named
+// "cfitest-TEST", where TEST identifies the particular test.
+#ifdef WRITE_ELF
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+extern "C" {
+// To compile with WRITE_ELF, you should add the 'include' directory
+// of the binutils, gcc, or gdb source tree to your #include path;
+// that directory contains this header.
+#include "elf/common.h"
+}
+#endif
+
+#include "breakpad_googletest_includes.h"
+#include "common/dwarf/bytereader-inl.h"
+#include "common/dwarf/cfi_assembler.h"
+#include "common/dwarf/dwarf2reader.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+
+using google_breakpad::CFISection;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::kBigEndian;
+using google_breakpad::test_assembler::kLittleEndian;
+using google_breakpad::test_assembler::Section;
+
+using dwarf2reader::DwarfPointerEncoding;
+using dwarf2reader::ENDIANNESS_BIG;
+using dwarf2reader::ENDIANNESS_LITTLE;
+using dwarf2reader::ByteReader;
+using dwarf2reader::CallFrameInfo;
+
+using std::vector;
+using testing::InSequence;
+using testing::Return;
+using testing::Sequence;
+using testing::Test;
+using testing::_;
+
+#ifdef WRITE_ELF
+void WriteELFFrameSection(const char *filename, const char *section_name,
+ const CFISection &section);
+#define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) \
+ WriteELFFrameSection("cfitest-" name, ".debug_frame", section);
+#define PERHAPS_WRITE_EH_FRAME_FILE(name, section) \
+ WriteELFFrameSection("cfitest-" name, ".eh_frame", section);
+#else
+#define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section)
+#define PERHAPS_WRITE_EH_FRAME_FILE(name, section)
+#endif
+
+class MockCallFrameInfoHandler: public CallFrameInfo::Handler {
+ public:
+ MOCK_METHOD6(Entry, bool(size_t offset, uint64 address, uint64 length,
+ uint8 version, const string &augmentation,
+ unsigned return_address));
+ MOCK_METHOD2(UndefinedRule, bool(uint64 address, int reg));
+ MOCK_METHOD2(SameValueRule, bool(uint64 address, int reg));
+ MOCK_METHOD4(OffsetRule, bool(uint64 address, int reg, int base_register,
+ long offset));
+ MOCK_METHOD4(ValOffsetRule, bool(uint64 address, int reg, int base_register,
+ long offset));
+ MOCK_METHOD3(RegisterRule, bool(uint64 address, int reg, int base_register));
+ MOCK_METHOD3(ExpressionRule, bool(uint64 address, int reg,
+ const string &expression));
+ MOCK_METHOD3(ValExpressionRule, bool(uint64 address, int reg,
+ const string &expression));
+ MOCK_METHOD0(End, bool());
+ MOCK_METHOD2(PersonalityRoutine, bool(uint64 address, bool indirect));
+ MOCK_METHOD2(LanguageSpecificDataArea, bool(uint64 address, bool indirect));
+ MOCK_METHOD0(SignalHandler, bool());
+};
+
+class MockCallFrameErrorReporter: public CallFrameInfo::Reporter {
+ public:
+ MockCallFrameErrorReporter() : Reporter("mock filename", "mock section") { }
+ MOCK_METHOD2(Incomplete, void(uint64, CallFrameInfo::EntryKind));
+ MOCK_METHOD1(EarlyEHTerminator, void(uint64));
+ MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64));
+ MOCK_METHOD2(BadCIEId, void(uint64, uint64));
+ MOCK_METHOD2(UnexpectedAddressSize, void(uint64, uint8_t));
+ MOCK_METHOD2(UnexpectedSegmentSize, void(uint64, uint8_t));
+ MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version));
+ MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &));
+ MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8));
+ MOCK_METHOD2(UnusablePointerEncoding, void(uint64, uint8));
+ MOCK_METHOD2(RestoreInCIE, void(uint64, uint64));
+ MOCK_METHOD3(BadInstruction, void(uint64, CallFrameInfo::EntryKind, uint64));
+ MOCK_METHOD3(NoCFARule, void(uint64, CallFrameInfo::EntryKind, uint64));
+ MOCK_METHOD3(EmptyStateStack, void(uint64, CallFrameInfo::EntryKind, uint64));
+};
+
+struct CFIFixture {
+
+ enum { kCFARegister = CallFrameInfo::Handler::kCFARegister };
+
+ CFIFixture() {
+ // Default expectations for the data handler.
+ //
+ // - Leave Entry and End without expectations, as it's probably a
+ // good idea to set those explicitly in each test.
+ //
+ // - Expect the *Rule functions to not be called,
+ // so that each test can simply list the calls they expect.
+ //
+ // I gather I could use StrictMock for this, but the manual seems
+ // to suggest using that only as a last resort, and this isn't so
+ // bad.
+ EXPECT_CALL(handler, UndefinedRule(_, _)).Times(0);
+ EXPECT_CALL(handler, SameValueRule(_, _)).Times(0);
+ EXPECT_CALL(handler, OffsetRule(_, _, _, _)).Times(0);
+ EXPECT_CALL(handler, ValOffsetRule(_, _, _, _)).Times(0);
+ EXPECT_CALL(handler, RegisterRule(_, _, _)).Times(0);
+ EXPECT_CALL(handler, ExpressionRule(_, _, _)).Times(0);
+ EXPECT_CALL(handler, ValExpressionRule(_, _, _)).Times(0);
+ EXPECT_CALL(handler, PersonalityRoutine(_, _)).Times(0);
+ EXPECT_CALL(handler, LanguageSpecificDataArea(_, _)).Times(0);
+ EXPECT_CALL(handler, SignalHandler()).Times(0);
+
+ // Default expectations for the error/warning reporer.
+ EXPECT_CALL(reporter, Incomplete(_, _)).Times(0);
+ EXPECT_CALL(reporter, EarlyEHTerminator(_)).Times(0);
+ EXPECT_CALL(reporter, CIEPointerOutOfRange(_, _)).Times(0);
+ EXPECT_CALL(reporter, BadCIEId(_, _)).Times(0);
+ EXPECT_CALL(reporter, UnrecognizedVersion(_, _)).Times(0);
+ EXPECT_CALL(reporter, UnrecognizedAugmentation(_, _)).Times(0);
+ EXPECT_CALL(reporter, InvalidPointerEncoding(_, _)).Times(0);
+ EXPECT_CALL(reporter, UnusablePointerEncoding(_, _)).Times(0);
+ EXPECT_CALL(reporter, RestoreInCIE(_, _)).Times(0);
+ EXPECT_CALL(reporter, BadInstruction(_, _, _)).Times(0);
+ EXPECT_CALL(reporter, NoCFARule(_, _, _)).Times(0);
+ EXPECT_CALL(reporter, EmptyStateStack(_, _, _)).Times(0);
+ }
+
+ MockCallFrameInfoHandler handler;
+ MockCallFrameErrorReporter reporter;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+TEST_F(CFI, EmptyRegion) {
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+ static const uint8_t data[] = { 42 };
+
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ CallFrameInfo parser(data, 0, &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+TEST_F(CFI, IncompleteLength32) {
+ CFISection section(kBigEndian, 8);
+ section
+ // Not even long enough for an initial length.
+ .D16(0xa0f)
+ // Padding to keep valgrind happy. We subtract these off when we
+ // construct the parser.
+ .D16(0);
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown))
+ .WillOnce(Return());
+
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size() - 2,
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+TEST_F(CFI, IncompleteLength64) {
+ CFISection section(kLittleEndian, 4);
+ section
+ // An incomplete 64-bit DWARF initial length.
+ .D32(0xffffffff).D32(0x71fbaec2)
+ // Padding to keep valgrind happy. We subtract these off when we
+ // construct the parser.
+ .D32(0);
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown))
+ .WillOnce(Return());
+
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+
+ ByteReader byte_reader(ENDIANNESS_LITTLE);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size() - 4,
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+TEST_F(CFI, IncompleteId32) {
+ CFISection section(kBigEndian, 8);
+ section
+ .D32(3) // Initial length, not long enough for id
+ .D8(0xd7).D8(0xe5).D8(0xf1) // incomplete id
+ .CIEHeader(8727, 3983, 8889, 3, "")
+ .FinishEntry();
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown))
+ .WillOnce(Return());
+
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+TEST_F(CFI, BadId32) {
+ CFISection section(kBigEndian, 8);
+ section
+ .D32(0x100) // Initial length
+ .D32(0xe802fade) // bogus ID
+ .Append(0x100 - 4, 0x42); // make the length true
+ section
+ .CIEHeader(1672, 9872, 8529, 3, "")
+ .FinishEntry();
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ EXPECT_CALL(reporter, CIEPointerOutOfRange(_, 0xe802fade))
+ .WillOnce(Return());
+
+ string contents;
+ ASSERT_TRUE(section.GetContents(&contents));
+
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+// A lone CIE shouldn't cause any handler calls.
+TEST_F(CFI, SingleCIE) {
+ CFISection section(kLittleEndian, 4);
+ section.CIEHeader(0xffe799a8, 0x3398dcdd, 0x6e9683de, 3, "");
+ section.Append(10, dwarf2reader::DW_CFA_nop);
+ section.FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("SingleCIE", section);
+
+ EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, End()).Times(0);
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_LITTLE);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// One FDE, one CIE.
+TEST_F(CFI, OneFDE) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "")
+ .FinishEntry()
+ .FDEHeader(cie, 0x7714740d, 0x3d5a10cd)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("OneFDE", section);
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0x7714740d, 0x3d5a10cd, 3, "", 0x6b6efb87))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// Two FDEs share a CIE.
+TEST_F(CFI, TwoFDEsOneCIE) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ // First FDE. readelf complains about this one because it makes
+ // a forward reference to its CIE.
+ .FDEHeader(cie, 0xa42744df, 0xa3b42121)
+ .FinishEntry()
+ // CIE.
+ .Mark(&cie)
+ .CIEHeader(0x04f7dc7b, 0x3d00c05f, 0xbd43cb59, 3, "")
+ .FinishEntry()
+ // Second FDE.
+ .FDEHeader(cie, 0x6057d391, 0x700f608d)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsOneCIE", section);
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0xa42744df, 0xa3b42121, 3, "", 0xbd43cb59))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0x6057d391, 0x700f608d, 3, "", 0xbd43cb59))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// Two FDEs, two CIEs.
+TEST_F(CFI, TwoFDEsTwoCIEs) {
+ CFISection section(kLittleEndian, 8);
+ Label cie1, cie2;
+ section
+ // First CIE.
+ .Mark(&cie1)
+ .CIEHeader(0x694d5d45, 0x4233221b, 0xbf45e65a, 3, "")
+ .FinishEntry()
+ // First FDE which cites second CIE. readelf complains about
+ // this one because it makes a forward reference to its CIE.
+ .FDEHeader(cie2, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL)
+ .FinishEntry()
+ // Second FDE, which cites first CIE.
+ .FDEHeader(cie1, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL)
+ .FinishEntry()
+ // Second CIE.
+ .Mark(&cie2)
+ .CIEHeader(0xfba3fad7, 0x6287e1fd, 0x61d2c581, 2, "")
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsTwoCIEs", section);
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL, 2,
+ "", 0x61d2c581))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL, 3,
+ "", 0xbf45e65a))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_LITTLE);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// An FDE whose CIE specifies a version we don't recognize.
+TEST_F(CFI, BadVersion) {
+ CFISection section(kBigEndian, 4);
+ Label cie1, cie2;
+ section
+ .Mark(&cie1)
+ .CIEHeader(0xca878cf0, 0x7698ec04, 0x7b616f54, 0x52, "")
+ .FinishEntry()
+ // We should skip this entry, as its CIE specifies a version we
+ // don't recognize.
+ .FDEHeader(cie1, 0x08852292, 0x2204004a)
+ .FinishEntry()
+ // Despite the above, we should visit this entry.
+ .Mark(&cie2)
+ .CIEHeader(0x7c3ae7c9, 0xb9b9a512, 0x96cb3264, 3, "")
+ .FinishEntry()
+ .FDEHeader(cie2, 0x2094735a, 0x6e875501)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("BadVersion", section);
+
+ EXPECT_CALL(reporter, UnrecognizedVersion(_, 0x52))
+ .WillOnce(Return());
+
+ {
+ InSequence s;
+ // We should see no mention of the first FDE, but we should get
+ // a call to Entry for the second.
+ EXPECT_CALL(handler, Entry(_, 0x2094735a, 0x6e875501, 3, "",
+ 0x96cb3264))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+// An FDE whose CIE specifies an augmentation we don't recognize.
+TEST_F(CFI, BadAugmentation) {
+ CFISection section(kBigEndian, 4);
+ Label cie1, cie2;
+ section
+ .Mark(&cie1)
+ .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "spaniels!")
+ .FinishEntry()
+ // We should skip this entry, as its CIE specifies an
+ // augmentation we don't recognize.
+ .FDEHeader(cie1, 0x7714740d, 0x3d5a10cd)
+ .FinishEntry()
+ // Despite the above, we should visit this entry.
+ .Mark(&cie2)
+ .CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 3, "")
+ .FinishEntry()
+ .FDEHeader(cie2, 0x7bf0fda0, 0xcbcd28d8)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("BadAugmentation", section);
+
+ EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "spaniels!"))
+ .WillOnce(Return());
+
+ {
+ InSequence s;
+ // We should see no mention of the first FDE, but we should get
+ // a call to Entry for the second.
+ EXPECT_CALL(handler, Entry(_, 0x7bf0fda0, 0xcbcd28d8, 3, "",
+ 0xf2f519b2))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+// The return address column field is a byte in CFI version 1
+// (DWARF2), but a ULEB128 value in version 3 (DWARF3).
+TEST_F(CFI, CIEVersion1ReturnColumn) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ // CIE, using the version 1 format: return column is a ubyte.
+ .Mark(&cie)
+ // Use a value for the return column that is parsed differently
+ // as a ubyte and as a ULEB128.
+ .CIEHeader(0xbcdea24f, 0x5be28286, 0x9f, 1, "")
+ .FinishEntry()
+ // FDE, citing that CIE.
+ .FDEHeader(cie, 0xb8d347b5, 0x825e55dc)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion1ReturnColumn", section);
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler, Entry(_, 0xb8d347b5, 0x825e55dc, 1, "", 0x9f))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+// The return address column field is a byte in CFI version 1
+// (DWARF2), but a ULEB128 value in version 3 (DWARF3).
+TEST_F(CFI, CIEVersion3ReturnColumn) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ // CIE, using the version 3 format: return column is a ULEB128.
+ .Mark(&cie)
+ // Use a value for the return column that is parsed differently
+ // as a ubyte and as a ULEB128.
+ .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 3, "")
+ .FinishEntry()
+ // FDE, citing that CIE.
+ .FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section);
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 3, "", 0x89))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+TEST_F(CFI, CIEVersion4AdditionalFields) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ .Mark(&cie)
+ // CIE version 4 with expected address and segment size.
+ .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 4, "", true, 8, 0)
+ .FinishEntry()
+ // FDE, citing that CIE.
+ .FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section);
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 4, "", 0x89))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(4);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_TRUE(parser.Start());
+}
+
+TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedAddressSize) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+
+ section
+ .Mark(&cie)
+ // Unexpected address size.
+ .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 4, "", true, 3, 0)
+ .FinishEntry()
+ // FDE, citing that CIE.
+ .FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedAddress", section);
+
+ EXPECT_CALL(reporter, UnexpectedAddressSize(_, 3))
+ .WillOnce(Return());
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedSegmentSize) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+
+ section
+ .Mark(&cie)
+ .CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 4, "", true, 8, 7)
+ .FinishEntry()
+ .FDEHeader(cie, 0x7bf0fda0, 0xcbcd28d8)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedSegment", section);
+
+ EXPECT_CALL(reporter, UnexpectedSegmentSize(_, 7))
+ .WillOnce(Return());
+
+ string contents;
+ EXPECT_TRUE(section.GetContents(&contents));
+ ByteReader byte_reader(ENDIANNESS_BIG);
+ byte_reader.SetAddressSize(8);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ EXPECT_FALSE(parser.Start());
+}
+
+struct CFIInsnFixture: public CFIFixture {
+ CFIInsnFixture() : CFIFixture() {
+ data_factor = 0xb6f;
+ return_register = 0x9be1ed9f;
+ version = 3;
+ cfa_base_register = 0x383a3aa;
+ cfa_offset = 0xf748;
+ }
+
+ // Prepare SECTION to receive FDE instructions.
+ //
+ // - Append a stock CIE header that establishes the fixture's
+ // code_factor, data_factor, return_register, version, and
+ // augmentation values.
+ // - Have the CIE set up a CFA rule using cfa_base_register and
+ // cfa_offset.
+ // - Append a stock FDE header, referring to the above CIE, for the
+ // fde_size bytes at fde_start. Choose fde_start and fde_size
+ // appropriately for the section's address size.
+ // - Set appropriate expectations on handler in sequence s for the
+ // frame description entry and the CIE's CFA rule.
+ //
+ // On return, SECTION is ready to have FDE instructions appended to
+ // it, and its FinishEntry member called.
+ void StockCIEAndFDE(CFISection *section) {
+ // Choose appropriate constants for our address size.
+ if (section->AddressSize() == 4) {
+ fde_start = 0xc628ecfbU;
+ fde_size = 0x5dee04a2;
+ code_factor = 0x60b;
+ } else {
+ assert(section->AddressSize() == 8);
+ fde_start = 0x0005c57ce7806bd3ULL;
+ fde_size = 0x2699521b5e333100ULL;
+ code_factor = 0x01008e32855274a8ULL;
+ }
+
+ // Create the CIE.
+ (*section)
+ .Mark(&cie_label)
+ .CIEHeader(code_factor, data_factor, return_register, version,
+ "")
+ .D8(dwarf2reader::DW_CFA_def_cfa)
+ .ULEB128(cfa_base_register)
+ .ULEB128(cfa_offset)
+ .FinishEntry();
+
+ // Create the FDE.
+ section->FDEHeader(cie_label, fde_start, fde_size);
+
+ // Expect an Entry call for the FDE and a ValOffsetRule call for the
+ // CIE's CFA rule.
+ EXPECT_CALL(handler, Entry(_, fde_start, fde_size, version, "",
+ return_register))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start, kCFARegister,
+ cfa_base_register, cfa_offset))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ }
+
+ // Run the contents of SECTION through a CallFrameInfo parser,
+ // expecting parser.Start to return SUCCEEDS
+ void ParseSection(CFISection *section, bool succeeds = true) {
+ string contents;
+ EXPECT_TRUE(section->GetContents(&contents));
+ dwarf2reader::Endianness endianness;
+ if (section->endianness() == kBigEndian)
+ endianness = ENDIANNESS_BIG;
+ else {
+ assert(section->endianness() == kLittleEndian);
+ endianness = ENDIANNESS_LITTLE;
+ }
+ ByteReader byte_reader(endianness);
+ byte_reader.SetAddressSize(section->AddressSize());
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter);
+ if (succeeds)
+ EXPECT_TRUE(parser.Start());
+ else
+ EXPECT_FALSE(parser.Start());
+ }
+
+ Label cie_label;
+ Sequence s;
+ uint64 code_factor;
+ int data_factor;
+ unsigned return_register;
+ unsigned version;
+ unsigned cfa_base_register;
+ int cfa_offset;
+ uint64 fde_start, fde_size;
+};
+
+class CFIInsn: public CFIInsnFixture, public Test { };
+
+TEST_F(CFIInsn, DW_CFA_set_loc) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_set_loc).D32(0xb1ee3e7a)
+ // Use DW_CFA_def_cfa to force a handler call that we can use to
+ // check the effect of the DW_CFA_set_loc.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4defb431).ULEB128(0x6d17b0ee)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_set_loc", section);
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(0xb1ee3e7a, kCFARegister, 0x4defb431, 0x6d17b0ee))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_advance_loc) {
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x2a)
+ // Use DW_CFA_def_cfa to force a handler call that we can use to
+ // check the effect of the DW_CFA_advance_loc.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x5bbb3715).ULEB128(0x0186c7bf)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc", section);
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start + 0x2a * code_factor,
+ kCFARegister, 0x5bbb3715, 0x0186c7bf))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_advance_loc1) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_advance_loc1).D8(0xd8)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x69d5696a).ULEB128(0x1eb7fc93)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc1", section);
+
+ EXPECT_CALL(handler,
+ ValOffsetRule((fde_start + 0xd8 * code_factor),
+ kCFARegister, 0x69d5696a, 0x1eb7fc93))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_advance_loc2) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_advance_loc2).D16(0x3adb)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3a368bed).ULEB128(0x3194ee37)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc2", section);
+
+ EXPECT_CALL(handler,
+ ValOffsetRule((fde_start + 0x3adb * code_factor),
+ kCFARegister, 0x3a368bed, 0x3194ee37))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_advance_loc4) {
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_advance_loc4).D32(0x15813c88)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x135270c5).ULEB128(0x24bad7cb)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc4", section);
+
+ EXPECT_CALL(handler,
+ ValOffsetRule((fde_start + 0x15813c88ULL * code_factor),
+ kCFARegister, 0x135270c5, 0x24bad7cb))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_MIPS_advance_loc8) {
+ code_factor = 0x2d;
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_MIPS_advance_loc8).D64(0x3c4f3945b92c14ULL)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0xe17ed602).ULEB128(0x3d162e7f)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc8", section);
+
+ EXPECT_CALL(handler,
+ ValOffsetRule((fde_start + 0x3c4f3945b92c14ULL * code_factor),
+ kCFARegister, 0xe17ed602, 0x3d162e7f))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4e363a85).ULEB128(0x815f9aa7)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_def_cfa", section);
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x4e363a85, 0x815f9aa7))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_sf) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x8ccb32b7).LEB128(0x9ea)
+ .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x9b40f5da).LEB128(-0x40a2)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x8ccb32b7,
+ 0x9ea * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x9b40f5da,
+ -0x40a2 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_register) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0x3e7e9363)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x3e7e9363, cfa_offset))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+// DW_CFA_def_cfa_register should have no effect when applied to a
+// non-base/offset rule.
+TEST_F(CFIInsn, DW_CFA_def_cfa_registerBadRule) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("needle in a haystack")
+ .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0xf1b49e49)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValExpressionRule(fde_start, kCFARegister,
+ "needle in a haystack"))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_offset) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, cfa_base_register,
+ 0x1e8e3b9b))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_offset_sf) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(0x970)
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(-0x2cd)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, cfa_base_register,
+ 0x970 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, cfa_base_register,
+ -0x2cd * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+// DW_CFA_def_cfa_offset should have no effect when applied to a
+// non-base/offset rule.
+TEST_F(CFIInsn, DW_CFA_def_cfa_offsetBadRule) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("six ways to Sunday")
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValExpressionRule(fde_start, kCFARegister, "six ways to Sunday"))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_def_cfa_expression) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("eating crow")
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValExpressionRule(fde_start, kCFARegister,
+ "eating crow"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_undefined) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x300ce45d)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, UndefinedRule(fde_start, 0x300ce45d))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_same_value) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3865a760)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, SameValueRule(fde_start, 0x3865a760))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_offset) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x9f6)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x2c, kCFARegister, 0x9f6 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_offset_extended) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset_extended).ULEB128(0x402b).ULEB128(0xb48)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x402b, kCFARegister, 0xb48 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_offset_extended_sf) {
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset_extended_sf)
+ .ULEB128(0x997c23ee).LEB128(0x2d00)
+ .D8(dwarf2reader::DW_CFA_offset_extended_sf)
+ .ULEB128(0x9519eb82).LEB128(-0xa77)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x997c23ee,
+ kCFARegister, 0x2d00 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x9519eb82,
+ kCFARegister, -0xa77 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_val_offset) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x623562fe).ULEB128(0x673)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, 0x623562fe,
+ kCFARegister, 0x673 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_val_offset_sf) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x6f4f).LEB128(0xaab)
+ .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x2483).LEB128(-0x8a2)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, 0x6f4f,
+ kCFARegister, 0xaab * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, 0x2483,
+ kCFARegister, -0x8a2 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_register) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x278d18f9).ULEB128(0x1a684414)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0x278d18f9, 0x1a684414))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_expression) {
+ CFISection section(kBigEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xa1619fb2)
+ .Block("plus ça change, plus c'est la même chose")
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ExpressionRule(fde_start, 0xa1619fb2,
+ "plus ça change, plus c'est la même chose"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_val_expression) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xc5e4a9e3)
+ .Block("he who has the gold makes the rules")
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValExpressionRule(fde_start, 0xc5e4a9e3,
+ "he who has the gold makes the rules"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_restore) {
+ CFISection section(kLittleEndian, 8);
+ code_factor = 0x01bd188a9b1fa083ULL;
+ data_factor = -0x1ac8;
+ return_register = 0x8c35b049;
+ version = 2;
+ fde_start = 0x2d70fe998298bbb1ULL;
+ fde_size = 0x46ccc2e63cf0b108ULL;
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(code_factor, data_factor, return_register, version,
+ "")
+ // Provide a CFA rule, because register rules require them.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x6ca1d50e).ULEB128(0x372e38e8)
+ // Provide an offset(N) rule for register 0x3c.
+ .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0xb348)
+ .FinishEntry()
+ // In the FDE...
+ .FDEHeader(cie, fde_start, fde_size)
+ // At a second address, provide a new offset(N) rule for register 0x3c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x13)
+ .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0x9a50)
+ // At a third address, restore the original rule for register 0x3c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x01)
+ .D8(dwarf2reader::DW_CFA_restore | 0x3c)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, fde_start, fde_size, version, "", return_register))
+ .WillOnce(Return(true));
+ // CIE's CFA rule.
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x6ca1d50e, 0x372e38e8))
+ .WillOnce(Return(true));
+ // CIE's rule for register 0x3c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x3c, kCFARegister, 0xb348 * data_factor))
+ .WillOnce(Return(true));
+ // FDE's rule for register 0x3c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + 0x13 * code_factor, 0x3c,
+ kCFARegister, 0x9a50 * data_factor))
+ .WillOnce(Return(true));
+ // Restore CIE's rule for register 0x3c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + (0x13 + 0x01) * code_factor, 0x3c,
+ kCFARegister, 0xb348 * data_factor))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_restoreNoRule) {
+ CFISection section(kBigEndian, 4);
+ code_factor = 0x005f78143c1c3b82ULL;
+ data_factor = 0x25d0;
+ return_register = 0xe8;
+ version = 1;
+ fde_start = 0x4062e30f;
+ fde_size = 0x5302a389;
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(code_factor, data_factor, return_register, version, "")
+ // Provide a CFA rule, because register rules require them.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x470aa334).ULEB128(0x099ef127)
+ .FinishEntry()
+ // In the FDE...
+ .FDEHeader(cie, fde_start, fde_size)
+ // At a second address, provide an offset(N) rule for register 0x2c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x7)
+ .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x1f47)
+ // At a third address, restore the (missing) CIE rule for register 0x2c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0xb)
+ .D8(dwarf2reader::DW_CFA_restore | 0x2c)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, fde_start, fde_size, version, "", return_register))
+ .WillOnce(Return(true));
+ // CIE's CFA rule.
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x470aa334, 0x099ef127))
+ .WillOnce(Return(true));
+ // FDE's rule for register 0x2c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + 0x7 * code_factor, 0x2c,
+ kCFARegister, 0x1f47 * data_factor))
+ .WillOnce(Return(true));
+ // Restore CIE's (missing) rule for register 0x2c.
+ EXPECT_CALL(handler,
+ SameValueRule(fde_start + (0x7 + 0xb) * code_factor, 0x2c))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_restore_extended) {
+ CFISection section(kBigEndian, 4);
+ code_factor = 0x126e;
+ data_factor = -0xd8b;
+ return_register = 0x77711787;
+ version = 3;
+ fde_start = 0x01f55a45;
+ fde_size = 0x452adb80;
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(code_factor, data_factor, return_register, version,
+ "", true /* dwarf64 */ )
+ // Provide a CFA rule, because register rules require them.
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x56fa0edd).ULEB128(0x097f78a5)
+ // Provide an offset(N) rule for register 0x0f9b8a1c.
+ .D8(dwarf2reader::DW_CFA_offset_extended)
+ .ULEB128(0x0f9b8a1c).ULEB128(0xc979)
+ .FinishEntry()
+ // In the FDE...
+ .FDEHeader(cie, fde_start, fde_size)
+ // At a second address, provide a new offset(N) rule for reg 0x0f9b8a1c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x3)
+ .D8(dwarf2reader::DW_CFA_offset_extended)
+ .ULEB128(0x0f9b8a1c).ULEB128(0x3b7b)
+ // At a third address, restore the original rule for register 0x0f9b8a1c.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 0x04)
+ .D8(dwarf2reader::DW_CFA_restore_extended).ULEB128(0x0f9b8a1c)
+ .FinishEntry();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ Entry(_, fde_start, fde_size, version, "", return_register))
+ .WillOnce(Return(true));
+ // CIE's CFA rule.
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x56fa0edd, 0x097f78a5))
+ .WillOnce(Return(true));
+ // CIE's rule for register 0x0f9b8a1c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x0f9b8a1c, kCFARegister,
+ 0xc979 * data_factor))
+ .WillOnce(Return(true));
+ // FDE's rule for register 0x0f9b8a1c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + 0x3 * code_factor, 0x0f9b8a1c,
+ kCFARegister, 0x3b7b * data_factor))
+ .WillOnce(Return(true));
+ // Restore CIE's rule for register 0x0f9b8a1c.
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start + (0x3 + 0x4) * code_factor, 0x0f9b8a1c,
+ kCFARegister, 0xc979 * data_factor))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+ }
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_remember_and_restore_state) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+
+ // We create a state, save it, modify it, and then restore. We
+ // refer to the state that is overridden the restore as the
+ // "outgoing" state, and the restored state the "incoming" state.
+ //
+ // Register outgoing incoming expect
+ // 1 offset(N) no rule new "same value" rule
+ // 2 register(R) offset(N) report changed rule
+ // 3 offset(N) offset(M) report changed offset
+ // 4 offset(N) offset(N) no report
+ // 5 offset(N) no rule new "same value" rule
+ section
+ // Create the "incoming" state, which we will save and later restore.
+ .D8(dwarf2reader::DW_CFA_offset | 2).ULEB128(0x9806)
+ .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0x995d)
+ .D8(dwarf2reader::DW_CFA_offset | 4).ULEB128(0x7055)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ // Advance to a new instruction; an implementation could legitimately
+ // ignore all but the final rule for a given register at a given address.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ // Create the "outgoing" state, which we will discard.
+ .D8(dwarf2reader::DW_CFA_offset | 1).ULEB128(0xea1a)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(2).ULEB128(0x1d2a3767)
+ .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0xdd29)
+ .D8(dwarf2reader::DW_CFA_offset | 5).ULEB128(0xf1ce)
+ // At a third address, restore the incoming state.
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ uint64 addr = fde_start;
+
+ // Expect the incoming rules to be reported.
+ EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 4, kCFARegister, 0x7055 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+
+ addr += code_factor;
+
+ // After the save, we establish the outgoing rule set.
+ EXPECT_CALL(handler, OffsetRule(addr, 1, kCFARegister, 0xea1a * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(addr, 2, 0x1d2a3767))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0xdd29 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 5, kCFARegister, 0xf1ce * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+
+ addr += code_factor;
+
+ // Finally, after the restore, expect to see the differences from
+ // the outgoing to the incoming rules reported.
+ EXPECT_CALL(handler, SameValueRule(addr, 1))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, SameValueRule(addr, 5))
+ .InSequence(s).WillOnce(Return(true));
+
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+// Check that restoring a rule set reports changes to the CFA rule.
+TEST_F(CFIInsn, DW_CFA_remember_and_restore_stateCFA) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+
+ section
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x90481102)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, kCFARegister,
+ cfa_base_register, 0x90481102))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor * 2, kCFARegister,
+ cfa_base_register, cfa_offset))
+ .InSequence(s).WillOnce(Return(true));
+
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_nop) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_nop)
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3fb8d4f1).ULEB128(0x078dc67b)
+ .D8(dwarf2reader::DW_CFA_nop)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ ValOffsetRule(fde_start, kCFARegister, 0x3fb8d4f1, 0x078dc67b))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_GNU_window_save) {
+ CFISection section(kBigEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_GNU_window_save)
+ .FinishEntry();
+
+ // Don't include all the rules in any particular sequence.
+
+ // The caller's %o0-%o7 have become the callee's %i0-%i7. This is
+ // the GCC register numbering.
+ for (int i = 8; i < 16; i++)
+ EXPECT_CALL(handler, RegisterRule(fde_start, i, i + 16))
+ .WillOnce(Return(true));
+ // The caller's %l0-%l7 and %i0-%i7 have been saved at the top of
+ // its frame.
+ for (int i = 16; i < 32; i++)
+ EXPECT_CALL(handler, OffsetRule(fde_start, i, kCFARegister, (i-16) * 4))
+ .WillOnce(Return(true));
+
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_GNU_args_size) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_GNU_args_size).ULEB128(0xeddfa520)
+ // Verify that we see this, meaning we parsed the above properly.
+ .D8(dwarf2reader::DW_CFA_offset | 0x23).ULEB128(0x269)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x23, kCFARegister, 0x269 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIInsn, DW_CFA_GNU_negative_offset_extended) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_GNU_negative_offset_extended)
+ .ULEB128(0x430cc87a).ULEB128(0x613)
+ .FinishEntry();
+
+ EXPECT_CALL(handler,
+ OffsetRule(fde_start, 0x430cc87a,
+ kCFARegister, -0x613 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+// Three FDEs: skip the second
+TEST_F(CFIInsn, SkipFDE) {
+ CFISection section(kBigEndian, 4);
+ Label cie;
+ section
+ // CIE, used by all FDEs.
+ .Mark(&cie)
+ .CIEHeader(0x010269f2, 0x9177, 0xedca5849, 2, "")
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x42ed390b).ULEB128(0x98f43aad)
+ .FinishEntry()
+ // First FDE.
+ .FDEHeader(cie, 0xa870ebdd, 0x60f6aa4)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x3a860351).ULEB128(0x6c9a6bcf)
+ .FinishEntry()
+ // Second FDE.
+ .FDEHeader(cie, 0xc534f7c0, 0xf6552e9, true /* dwarf64 */)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x1b62c234).ULEB128(0x26586b18)
+ .FinishEntry()
+ // Third FDE.
+ .FDEHeader(cie, 0xf681cfc8, 0x7e4594e)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x26c53934).ULEB128(0x18eeb8a4)
+ .FinishEntry();
+
+ {
+ InSequence s;
+
+ // Process the first FDE.
+ EXPECT_CALL(handler, Entry(_, 0xa870ebdd, 0x60f6aa4, 2, "", 0xedca5849))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(0xa870ebdd, kCFARegister,
+ 0x42ed390b, 0x98f43aad))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(0xa870ebdd, 0x3a860351, 0x6c9a6bcf))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .WillOnce(Return(true));
+
+ // Skip the second FDE.
+ EXPECT_CALL(handler, Entry(_, 0xc534f7c0, 0xf6552e9, 2, "", 0xedca5849))
+ .WillOnce(Return(false));
+
+ // Process the third FDE.
+ EXPECT_CALL(handler, Entry(_, 0xf681cfc8, 0x7e4594e, 2, "", 0xedca5849))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(0xf681cfc8, kCFARegister,
+ 0x42ed390b, 0x98f43aad))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(0xf681cfc8, 0x26c53934, 0x18eeb8a4))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .WillOnce(Return(true));
+ }
+
+ ParseSection(&section);
+}
+
+// Quit processing in the middle of an entry's instructions.
+TEST_F(CFIInsn, QuitMidentry) {
+ CFISection section(kLittleEndian, 8);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe0cf850d).ULEB128(0x15aab431)
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x46750aa5).Block("meat")
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0xe0cf850d, 0x15aab431))
+ .InSequence(s).WillOnce(Return(false));
+ EXPECT_CALL(handler, End())
+ .InSequence(s).WillOnce(Return(true));
+
+ ParseSection(&section, false);
+}
+
+class CFIRestore: public CFIInsnFixture, public Test { };
+
+TEST_F(CFIRestore, RestoreUndefinedRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x0bac878e)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, UndefinedRule(fde_start, 0x0bac878e))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreUndefinedRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x7dedff5f)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x7dedff5f)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, UndefinedRule(fde_start, 0x7dedff5f))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, SameValueRule(fde_start + code_factor, 0x7dedff5f))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + 2 * code_factor, 0x7dedff5f))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreSameValueRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0xadbc9b3a)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, SameValueRule(fde_start, 0xadbc9b3a))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreSameValueRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3d90dcb5)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x3d90dcb5)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, SameValueRule(fde_start, 0x3d90dcb5))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x3d90dcb5))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, SameValueRule(fde_start + 2 * code_factor, 0x3d90dcb5))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreOffsetRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset | 0x14).ULEB128(0xb6f)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, OffsetRule(fde_start, 0x14,
+ kCFARegister, 0xb6f * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreOffsetRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xeb7)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x21)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, OffsetRule(fde_start, 0x21,
+ kCFARegister, 0xeb7 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x21))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21,
+ kCFARegister, 0xeb7 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreOffsetRuleChangedOffset) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0x134)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xf4f)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, OffsetRule(fde_start, 0x21,
+ kCFARegister, 0x134 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(fde_start + code_factor, 0x21,
+ kCFARegister, 0xf4f * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21,
+ kCFARegister, 0x134 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValOffsetRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x829caee6).ULEB128(0xe4c)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x829caee6,
+ kCFARegister, 0xe4c * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValOffsetRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0xf17c36d6).ULEB128(0xeb7)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xf17c36d6)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValOffsetRule(fde_start, 0xf17c36d6,
+ kCFARegister, 0xeb7 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xf17c36d6))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0xf17c36d6,
+ kCFARegister, 0xeb7 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValOffsetRuleChangedValOffset) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0x562)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0xe88)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x2cf0ab1b,
+ kCFARegister, 0x562 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, 0x2cf0ab1b,
+ kCFARegister, 0xe88 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0x2cf0ab1b,
+ kCFARegister, 0x562 * data_factor))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreRegisterRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0x77514acc).ULEB128(0x464de4ce)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0x77514acc, 0x464de4ce))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreRegisterRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe39acce5).ULEB128(0x095f1559)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xe39acce5)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0xe39acce5, 0x095f1559))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xe39acce5))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xe39acce5,
+ 0x095f1559))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreRegisterRuleChangedRegister) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0x16607d6a)
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0xbabb4742)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, RegisterRule(fde_start, 0xd40e21b1, 0x16607d6a))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(fde_start + code_factor, 0xd40e21b1,
+ 0xbabb4742))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xd40e21b1,
+ 0x16607d6a))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreExpressionRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x666ae152).Block("dwarf")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ExpressionRule(fde_start, 0x666ae152, "dwarf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreExpressionRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xb5ca5c46).Block("elf")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ExpressionRule(fde_start, 0xb5ca5c46, "elf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46,
+ "elf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreExpressionRuleChangedExpression) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("smurf")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("orc")
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ExpressionRule(fde_start, 0x500f5739, "smurf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ExpressionRule(fde_start + code_factor, 0x500f5739,
+ "orc"))
+ .InSequence(s).WillOnce(Return(true));
+ // Expectations are not wishes.
+ EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0x500f5739,
+ "smurf"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValExpressionRuleUnchanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x666ae152)
+ .Block("hideous")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x666ae152, "hideous"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValExpressionRuleChanged) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xb5ca5c46)
+ .Block("revolting")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChanged", section);
+
+ EXPECT_CALL(handler, ValExpressionRule(fde_start, 0xb5ca5c46, "revolting"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46,
+ "revolting"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+TEST_F(CFIRestore, RestoreValExpressionRuleChangedValExpression) {
+ CFISection section(kLittleEndian, 4);
+ StockCIEAndFDE(&section);
+ section
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739)
+ .Block("repulsive")
+ .D8(dwarf2reader::DW_CFA_remember_state)
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739)
+ .Block("nauseous")
+ .D8(dwarf2reader::DW_CFA_advance_loc | 1)
+ .D8(dwarf2reader::DW_CFA_restore_state)
+ .FinishEntry();
+
+ PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChangedValExpression",
+ section);
+
+ EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x500f5739, "repulsive"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValExpressionRule(fde_start + code_factor, 0x500f5739,
+ "nauseous"))
+ .InSequence(s).WillOnce(Return(true));
+ // Expectations are not wishes.
+ EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0x500f5739,
+ "repulsive"))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End()).WillOnce(Return(true));
+
+ ParseSection(&section);
+}
+
+struct EHFrameFixture: public CFIInsnFixture {
+ EHFrameFixture()
+ : CFIInsnFixture(), section(kBigEndian, 4, true) {
+ encoded_pointer_bases.cfi = 0x7f496cb2;
+ encoded_pointer_bases.text = 0x540f67b6;
+ encoded_pointer_bases.data = 0xe3eab768;
+ section.SetEncodedPointerBases(encoded_pointer_bases);
+ }
+ CFISection section;
+ CFISection::EncodedPointerBases encoded_pointer_bases;
+
+ // Parse CFIInsnFixture::ParseSection, but parse the section as
+ // .eh_frame data, supplying stock base addresses.
+ void ParseEHFrameSection(CFISection *section, bool succeeds = true) {
+ EXPECT_TRUE(section->ContainsEHFrame());
+ string contents;
+ EXPECT_TRUE(section->GetContents(&contents));
+ dwarf2reader::Endianness endianness;
+ if (section->endianness() == kBigEndian)
+ endianness = ENDIANNESS_BIG;
+ else {
+ assert(section->endianness() == kLittleEndian);
+ endianness = ENDIANNESS_LITTLE;
+ }
+ ByteReader byte_reader(endianness);
+ byte_reader.SetAddressSize(section->AddressSize());
+ byte_reader.SetCFIDataBase(encoded_pointer_bases.cfi,
+ reinterpret_cast<const uint8_t *>(contents.data()));
+ byte_reader.SetTextBase(encoded_pointer_bases.text);
+ byte_reader.SetDataBase(encoded_pointer_bases.data);
+ CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
+ contents.size(),
+ &byte_reader, &handler, &reporter, true);
+ if (succeeds)
+ EXPECT_TRUE(parser.Start());
+ else
+ EXPECT_FALSE(parser.Start());
+ }
+
+};
+
+class EHFrame: public EHFrameFixture, public Test { };
+
+// A simple CIE, an FDE, and a terminator.
+TEST_F(EHFrame, Terminator) {
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(9968, 2466, 67, 1, "")
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3772).ULEB128(1372)
+ .FinishEntry()
+ .FDEHeader(cie, 0x848037a1, 0x7b30475e)
+ .D8(dwarf2reader::DW_CFA_set_loc).D32(0x17713850)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(5721)
+ .FinishEntry()
+ .D32(0) // Terminate the sequence.
+ // This FDE should be ignored.
+ .FDEHeader(cie, 0xf19629fe, 0x439fb09b)
+ .FinishEntry();
+
+ PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.Terminator", section);
+
+ EXPECT_CALL(handler, Entry(_, 0x848037a1, 0x7b30475e, 1, "", 67))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(0x848037a1, kCFARegister, 3772, 1372))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(0x17713850, 5721))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(reporter, EarlyEHTerminator(_))
+ .InSequence(s).WillOnce(Return());
+
+ ParseEHFrameSection(&section);
+}
+
+// The parser should recognize the Linux Standards Base 'z' augmentations.
+TEST_F(EHFrame, SimpleFDE) {
+ DwarfPointerEncoding lsda_encoding =
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect
+ | dwarf2reader::DW_EH_PE_datarel
+ | dwarf2reader::DW_EH_PE_sdata2);
+ DwarfPointerEncoding fde_encoding =
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel
+ | dwarf2reader::DW_EH_PE_udata2);
+
+ section.SetPointerEncoding(fde_encoding);
+ section.SetEncodedPointerBases(encoded_pointer_bases);
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(4873, 7012, 100, 1, "zSLPR")
+ .ULEB128(7) // Augmentation data length
+ .D8(lsda_encoding) // LSDA pointer format
+ .D8(dwarf2reader::DW_EH_PE_pcrel) // personality pointer format
+ .EncodedPointer(0x97baa00, dwarf2reader::DW_EH_PE_pcrel) // and value
+ .D8(fde_encoding) // FDE pointer format
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(6706).ULEB128(31)
+ .FinishEntry()
+ .FDEHeader(cie, 0x540f6b56, 0xf686)
+ .ULEB128(2) // Augmentation data length
+ .EncodedPointer(0xe3eab475, lsda_encoding) // LSDA pointer, signed
+ .D8(dwarf2reader::DW_CFA_set_loc)
+ .EncodedPointer(0x540fa4ce, fde_encoding)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x675e)
+ .FinishEntry()
+ .D32(0); // terminator
+
+ PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.SimpleFDE", section);
+
+ EXPECT_CALL(handler, Entry(_, 0x540f6b56, 0xf686, 1, "zSLPR", 100))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, PersonalityRoutine(0x97baa00, false))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, LanguageSpecificDataArea(0xe3eab475, true))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, SignalHandler())
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(0x540f6b56, kCFARegister, 6706, 31))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(0x540fa4ce, 0x675e))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .InSequence(s).WillOnce(Return(true));
+
+ ParseEHFrameSection(&section);
+}
+
+// Check that we can handle an empty 'z' augmentation.
+TEST_F(EHFrame, EmptyZ) {
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(5955, 5805, 228, 1, "z")
+ .ULEB128(0) // Augmentation data length
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3629).ULEB128(247)
+ .FinishEntry()
+ .FDEHeader(cie, 0xda007738, 0xfb55c641)
+ .ULEB128(0) // Augmentation data length
+ .D8(dwarf2reader::DW_CFA_advance_loc1).D8(11)
+ .D8(dwarf2reader::DW_CFA_undefined).ULEB128(3769)
+ .FinishEntry();
+
+ PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.EmptyZ", section);
+
+ EXPECT_CALL(handler, Entry(_, 0xda007738, 0xfb55c641, 1, "z", 228))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, ValOffsetRule(0xda007738, kCFARegister, 3629, 247))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, UndefinedRule(0xda007738 + 11 * 5955, 3769))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .InSequence(s).WillOnce(Return(true));
+
+ ParseEHFrameSection(&section);
+}
+
+// Check that we recognize bad 'z' augmentation characters.
+TEST_F(EHFrame, BadZ) {
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(6937, 1045, 142, 1, "zQ")
+ .ULEB128(0) // Augmentation data length
+ .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(9006).ULEB128(7725)
+ .FinishEntry()
+ .FDEHeader(cie, 0x1293efa8, 0x236f53f2)
+ .ULEB128(0) // Augmentation data length
+ .D8(dwarf2reader::DW_CFA_advance_loc | 12)
+ .D8(dwarf2reader::DW_CFA_register).ULEB128(5667).ULEB128(3462)
+ .FinishEntry();
+
+ PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.BadZ", section);
+
+ EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "zQ"))
+ .WillOnce(Return());
+
+ ParseEHFrameSection(&section, false);
+}
+
+TEST_F(EHFrame, zL) {
+ Label cie;
+ DwarfPointerEncoding lsda_encoding =
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel
+ | dwarf2reader::DW_EH_PE_udata2);
+ section
+ .Mark(&cie)
+ .CIEHeader(9285, 9959, 54, 1, "zL")
+ .ULEB128(1) // Augmentation data length
+ .D8(lsda_encoding) // encoding for LSDA pointer in FDE
+
+ .FinishEntry()
+ .FDEHeader(cie, 0xd40091aa, 0x9aa6e746)
+ .ULEB128(2) // Augmentation data length
+ .EncodedPointer(0xd40099cd, lsda_encoding) // LSDA pointer
+ .FinishEntry()
+ .D32(0); // terminator
+
+ PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zL", section);
+
+ EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zL", 54))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, LanguageSpecificDataArea(0xd40099cd, false))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .InSequence(s).WillOnce(Return(true));
+
+ ParseEHFrameSection(&section);
+}
+
+TEST_F(EHFrame, zP) {
+ Label cie;
+ DwarfPointerEncoding personality_encoding =
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel
+ | dwarf2reader::DW_EH_PE_udata2);
+ section
+ .Mark(&cie)
+ .CIEHeader(1097, 6313, 17, 1, "zP")
+ .ULEB128(3) // Augmentation data length
+ .D8(personality_encoding) // encoding for personality routine
+ .EncodedPointer(0xe3eaccac, personality_encoding) // value
+ .FinishEntry()
+ .FDEHeader(cie, 0x0c8350c9, 0xbef11087)
+ .ULEB128(0) // Augmentation data length
+ .FinishEntry()
+ .D32(0); // terminator
+
+ PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zP", section);
+
+ EXPECT_CALL(handler, Entry(_, 0x0c8350c9, 0xbef11087, 1, "zP", 17))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, PersonalityRoutine(0xe3eaccac, false))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .InSequence(s).WillOnce(Return(true));
+
+ ParseEHFrameSection(&section);
+}
+
+TEST_F(EHFrame, zR) {
+ Label cie;
+ DwarfPointerEncoding pointer_encoding =
+ DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel
+ | dwarf2reader::DW_EH_PE_sdata2);
+ section.SetPointerEncoding(pointer_encoding);
+ section
+ .Mark(&cie)
+ .CIEHeader(8011, 5496, 75, 1, "zR")
+ .ULEB128(1) // Augmentation data length
+ .D8(pointer_encoding) // encoding for FDE addresses
+ .FinishEntry()
+ .FDEHeader(cie, 0x540f9431, 0xbd0)
+ .ULEB128(0) // Augmentation data length
+ .FinishEntry()
+ .D32(0); // terminator
+
+ PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zR", section);
+
+ EXPECT_CALL(handler, Entry(_, 0x540f9431, 0xbd0, 1, "zR", 75))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .InSequence(s).WillOnce(Return(true));
+
+ ParseEHFrameSection(&section);
+}
+
+TEST_F(EHFrame, zS) {
+ Label cie;
+ section
+ .Mark(&cie)
+ .CIEHeader(9217, 7694, 57, 1, "zS")
+ .ULEB128(0) // Augmentation data length
+ .FinishEntry()
+ .FDEHeader(cie, 0xd40091aa, 0x9aa6e746)
+ .ULEB128(0) // Augmentation data length
+ .FinishEntry()
+ .D32(0); // terminator
+
+ PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zS", section);
+
+ EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zS", 57))
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, SignalHandler())
+ .InSequence(s).WillOnce(Return(true));
+ EXPECT_CALL(handler, End())
+ .InSequence(s).WillOnce(Return(true));
+
+ ParseEHFrameSection(&section);
+}
+
+// These tests require manual inspection of the test output.
+struct CFIReporterFixture {
+ CFIReporterFixture() : reporter("test file name", "test section name") { }
+ CallFrameInfo::Reporter reporter;
+};
+
+class CFIReporter: public CFIReporterFixture, public Test { };
+
+TEST_F(CFIReporter, Incomplete) {
+ reporter.Incomplete(0x0102030405060708ULL, CallFrameInfo::kUnknown);
+}
+
+TEST_F(CFIReporter, EarlyEHTerminator) {
+ reporter.EarlyEHTerminator(0x0102030405060708ULL);
+}
+
+TEST_F(CFIReporter, CIEPointerOutOfRange) {
+ reporter.CIEPointerOutOfRange(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, BadCIEId) {
+ reporter.BadCIEId(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, UnrecognizedVersion) {
+ reporter.UnrecognizedVersion(0x0123456789abcdefULL, 43);
+}
+
+TEST_F(CFIReporter, UnrecognizedAugmentation) {
+ reporter.UnrecognizedAugmentation(0x0123456789abcdefULL, "poodles");
+}
+
+TEST_F(CFIReporter, InvalidPointerEncoding) {
+ reporter.InvalidPointerEncoding(0x0123456789abcdefULL, 0x42);
+}
+
+TEST_F(CFIReporter, UnusablePointerEncoding) {
+ reporter.UnusablePointerEncoding(0x0123456789abcdefULL, 0x42);
+}
+
+TEST_F(CFIReporter, RestoreInCIE) {
+ reporter.RestoreInCIE(0x0123456789abcdefULL, 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, BadInstruction) {
+ reporter.BadInstruction(0x0123456789abcdefULL, CallFrameInfo::kFDE,
+ 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, NoCFARule) {
+ reporter.NoCFARule(0x0123456789abcdefULL, CallFrameInfo::kCIE,
+ 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, EmptyStateStack) {
+ reporter.EmptyStateStack(0x0123456789abcdefULL, CallFrameInfo::kTerminator,
+ 0xfedcba9876543210ULL);
+}
+
+TEST_F(CFIReporter, ClearingCFARule) {
+ reporter.ClearingCFARule(0x0123456789abcdefULL, CallFrameInfo::kFDE,
+ 0xfedcba9876543210ULL);
+}
+
+#ifdef WRITE_ELF
+// See comments at the top of the file mentioning WRITE_ELF for details.
+
+using google_breakpad::test_assembler::Section;
+
+struct ELFSectionHeader {
+ ELFSectionHeader(unsigned int set_type)
+ : type(set_type), flags(0), address(0), link(0), info(0),
+ alignment(1), entry_size(0) { }
+ Label name;
+ unsigned int type;
+ uint64_t flags;
+ uint64_t address;
+ Label file_offset;
+ Label file_size;
+ unsigned int link;
+ unsigned int info;
+ uint64_t alignment;
+ uint64_t entry_size;
+};
+
+void AppendSectionHeader(CFISection *table, const ELFSectionHeader &header) {
+ (*table)
+ .D32(header.name) // name, index in string tbl
+ .D32(header.type) // type
+ .Address(header.flags) // flags
+ .Address(header.address) // address in memory
+ .Address(header.file_offset) // offset in ELF file
+ .Address(header.file_size) // length in bytes
+ .D32(header.link) // link to related section
+ .D32(header.info) // miscellaneous
+ .Address(header.alignment) // alignment
+ .Address(header.entry_size); // entry size
+}
+
+void WriteELFFrameSection(const char *filename, const char *cfi_name,
+ const CFISection &cfi) {
+ int elf_class = cfi.AddressSize() == 4 ? ELFCLASS32 : ELFCLASS64;
+ int elf_data = (cfi.endianness() == kBigEndian
+ ? ELFDATA2MSB : ELFDATA2LSB);
+ CFISection elf(cfi.endianness(), cfi.AddressSize());
+ Label elf_header_size, section_table_offset;
+ elf
+ .Append("\x7f" "ELF")
+ .D8(elf_class) // 32-bit or 64-bit ELF
+ .D8(elf_data) // endianness
+ .D8(1) // ELF version
+ .D8(ELFOSABI_LINUX) // Operating System/ABI indication
+ .D8(0) // ABI version
+ .Append(7, 0xda) // padding
+ .D16(ET_EXEC) // file type: executable file
+ .D16(EM_386) // architecture: Intel IA-32
+ .D32(EV_CURRENT); // ELF version
+ elf
+ .Address(0x0123456789abcdefULL) // program entry point
+ .Address(0) // program header offset
+ .Address(section_table_offset) // section header offset
+ .D32(0) // processor-specific flags
+ .D16(elf_header_size) // ELF header size in bytes */
+ .D16(elf_class == ELFCLASS32 ? 32 : 56) // program header entry size
+ .D16(0) // program header table entry count
+ .D16(elf_class == ELFCLASS32 ? 40 : 64) // section header entry size
+ .D16(3) // section count
+ .D16(1) // section name string table
+ .Mark(&elf_header_size);
+
+ // The null section. Every ELF file has one, as the first entry in
+ // the section header table.
+ ELFSectionHeader null_header(SHT_NULL);
+ null_header.file_offset = 0;
+ null_header.file_size = 0;
+
+ // The CFI section. The whole reason for writing out this ELF file
+ // is to put this in it so that we can run other dumping programs on
+ // it to check its contents.
+ ELFSectionHeader cfi_header(SHT_PROGBITS);
+ cfi_header.file_size = cfi.Size();
+
+ // The section holding the names of the sections. This is the
+ // section whose index appears in the e_shstrndx member of the ELF
+ // header.
+ ELFSectionHeader section_names_header(SHT_STRTAB);
+ CFISection section_names(cfi.endianness(), cfi.AddressSize());
+ section_names
+ .Mark(&null_header.name)
+ .AppendCString("")
+ .Mark(&section_names_header.name)
+ .AppendCString(".shstrtab")
+ .Mark(&cfi_header.name)
+ .AppendCString(cfi_name)
+ .Mark(&section_names_header.file_size);
+
+ // Create the section table. The ELF header's e_shoff member refers
+ // to this, and the e_shnum member gives the number of entries it
+ // contains.
+ CFISection section_table(cfi.endianness(), cfi.AddressSize());
+ AppendSectionHeader(&section_table, null_header);
+ AppendSectionHeader(&section_table, section_names_header);
+ AppendSectionHeader(&section_table, cfi_header);
+
+ // Append the section table and the section contents to the ELF file.
+ elf
+ .Mark(&section_table_offset)
+ .Append(section_table)
+ .Mark(&section_names_header.file_offset)
+ .Append(section_names)
+ .Mark(&cfi_header.file_offset)
+ .Append(cfi);
+
+ string contents;
+ if (!elf.GetContents(&contents)) {
+ fprintf(stderr, "failed to get ELF file contents\n");
+ exit(1);
+ }
+
+ FILE *out = fopen(filename, "w");
+ if (!out) {
+ fprintf(stderr, "error opening ELF file '%s': %s\n",
+ filename, strerror(errno));
+ exit(1);
+ }
+
+ if (fwrite(contents.data(), 1, contents.size(), out) != contents.size()) {
+ fprintf(stderr, "error writing ELF data to '%s': %s\n",
+ filename, strerror(errno));
+ exit(1);
+ }
+
+ if (fclose(out) == EOF) {
+ fprintf(stderr, "error closing ELF file '%s': %s\n",
+ filename, strerror(errno));
+ exit(1);
+ }
+}
+#endif
diff --git a/3rdParty/Breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc
new file mode 100644
index 0000000..71418eb
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc
@@ -0,0 +1,487 @@
+// Copyright (c) 2012, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/dwarf/bytereader-inl.h"
+#include "common/dwarf/dwarf2reader_test_common.h"
+#include "common/dwarf/dwarf2reader.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+
+using google_breakpad::test_assembler::Endianness;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::Section;
+using google_breakpad::test_assembler::kBigEndian;
+using google_breakpad::test_assembler::kLittleEndian;
+
+using dwarf2reader::ByteReader;
+using dwarf2reader::CompilationUnit;
+using dwarf2reader::Dwarf2Handler;
+using dwarf2reader::DwarfAttribute;
+using dwarf2reader::DwarfForm;
+using dwarf2reader::DwarfHasChild;
+using dwarf2reader::DwarfTag;
+using dwarf2reader::ENDIANNESS_BIG;
+using dwarf2reader::ENDIANNESS_LITTLE;
+using dwarf2reader::SectionMap;
+
+using std::vector;
+using testing::InSequence;
+using testing::Pointee;
+using testing::Return;
+using testing::Sequence;
+using testing::Test;
+using testing::TestWithParam;
+using testing::_;
+
+class MockDwarf2Handler: public Dwarf2Handler {
+ public:
+ MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size,
+ uint8 offset_size, uint64 cu_length,
+ uint8 dwarf_version));
+ MOCK_METHOD2(StartDIE, bool(uint64 offset, enum DwarfTag tag));
+ MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset,
+ DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data));
+ MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ int64 data));
+ MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data));
+ MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const uint8_t *data,
+ uint64 len));
+ MOCK_METHOD4(ProcessAttributeString, void(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const string& data));
+ MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset,
+ DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 signature));
+ MOCK_METHOD1(EndDIE, void(uint64 offset));
+};
+
+struct DIEFixture {
+
+ DIEFixture() {
+ // Fix the initial offset of the .debug_info and .debug_abbrev sections.
+ info.start() = 0;
+ abbrevs.start() = 0;
+
+ // Default expectations for the data handler.
+ EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, StartDIE(_, _)).Times(0);
+ EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0);
+ EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0);
+ EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0);
+ EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0);
+ EXPECT_CALL(handler, EndDIE(_)).Times(0);
+ }
+
+ // Return a reference to a section map whose .debug_info section refers
+ // to |info|, and whose .debug_abbrev section refers to |abbrevs|. This
+ // function returns a reference to the same SectionMap each time; new
+ // calls wipe out maps established by earlier calls.
+ const SectionMap &MakeSectionMap() {
+ // Copy the sections' contents into strings that will live as long as
+ // the map itself.
+ assert(info.GetContents(&info_contents));
+ assert(abbrevs.GetContents(&abbrevs_contents));
+ section_map.clear();
+ section_map[".debug_info"].first
+ = reinterpret_cast<const uint8_t *>(info_contents.data());
+ section_map[".debug_info"].second = info_contents.size();
+ section_map[".debug_abbrev"].first
+ = reinterpret_cast<const uint8_t *>(abbrevs_contents.data());
+ section_map[".debug_abbrev"].second = abbrevs_contents.size();
+ return section_map;
+ }
+
+ TestCompilationUnit info;
+ TestAbbrevTable abbrevs;
+ MockDwarf2Handler handler;
+ string abbrevs_contents, info_contents;
+ SectionMap section_map;
+};
+
+struct DwarfHeaderParams {
+ DwarfHeaderParams(Endianness endianness, size_t format_size,
+ int version, size_t address_size)
+ : endianness(endianness), format_size(format_size),
+ version(version), address_size(address_size) { }
+ Endianness endianness;
+ size_t format_size; // 4-byte or 8-byte DWARF offsets
+ int version;
+ size_t address_size;
+};
+
+class DwarfHeader: public DIEFixture,
+ public TestWithParam<DwarfHeaderParams> { };
+
+TEST_P(DwarfHeader, Header) {
+ Label abbrev_table = abbrevs.Here();
+ abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit,
+ dwarf2reader::DW_children_yes)
+ .Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string)
+ .EndAbbrev()
+ .EndTable();
+
+ info.set_format_size(GetParam().format_size);
+ info.set_endianness(GetParam().endianness);
+
+ info.Header(GetParam().version, abbrev_table, GetParam().address_size)
+ .ULEB128(1) // DW_TAG_compile_unit, with children
+ .AppendCString("sam") // DW_AT_name, DW_FORM_string
+ .D8(0); // end of children
+ info.Finish();
+
+ {
+ InSequence s;
+ EXPECT_CALL(handler,
+ StartCompilationUnit(0, GetParam().address_size,
+ GetParam().format_size, _,
+ GetParam().version))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit))
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_string,
+ "sam"))
+ .WillOnce(Return());
+ EXPECT_CALL(handler, EndDIE(_))
+ .WillOnce(Return());
+ }
+
+ ByteReader byte_reader(GetParam().endianness == kLittleEndian ?
+ ENDIANNESS_LITTLE : ENDIANNESS_BIG);
+ CompilationUnit parser("", MakeSectionMap(), 0, &byte_reader, &handler);
+ EXPECT_EQ(parser.Start(), info_contents.size());
+}
+
+INSTANTIATE_TEST_CASE_P(
+ HeaderVariants, DwarfHeader,
+ ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
+ DwarfHeaderParams(kLittleEndian, 4, 2, 8),
+ DwarfHeaderParams(kLittleEndian, 4, 3, 4),
+ DwarfHeaderParams(kLittleEndian, 4, 3, 8),
+ DwarfHeaderParams(kLittleEndian, 4, 4, 4),
+ DwarfHeaderParams(kLittleEndian, 4, 4, 8),
+ DwarfHeaderParams(kLittleEndian, 8, 2, 4),
+ DwarfHeaderParams(kLittleEndian, 8, 2, 8),
+ DwarfHeaderParams(kLittleEndian, 8, 3, 4),
+ DwarfHeaderParams(kLittleEndian, 8, 3, 8),
+ DwarfHeaderParams(kLittleEndian, 8, 4, 4),
+ DwarfHeaderParams(kLittleEndian, 8, 4, 8),
+ DwarfHeaderParams(kBigEndian, 4, 2, 4),
+ DwarfHeaderParams(kBigEndian, 4, 2, 8),
+ DwarfHeaderParams(kBigEndian, 4, 3, 4),
+ DwarfHeaderParams(kBigEndian, 4, 3, 8),
+ DwarfHeaderParams(kBigEndian, 4, 4, 4),
+ DwarfHeaderParams(kBigEndian, 4, 4, 8),
+ DwarfHeaderParams(kBigEndian, 8, 2, 4),
+ DwarfHeaderParams(kBigEndian, 8, 2, 8),
+ DwarfHeaderParams(kBigEndian, 8, 3, 4),
+ DwarfHeaderParams(kBigEndian, 8, 3, 8),
+ DwarfHeaderParams(kBigEndian, 8, 4, 4),
+ DwarfHeaderParams(kBigEndian, 8, 4, 8)));
+
+struct DwarfFormsFixture: public DIEFixture {
+ // Start a compilation unit, as directed by |params|, containing one
+ // childless DIE of the given tag, with one attribute of the given name
+ // and form. The 'info' fixture member is left just after the abbrev
+ // code, waiting for the attribute value to be appended.
+ void StartSingleAttributeDIE(const DwarfHeaderParams &params,
+ DwarfTag tag, DwarfAttribute name,
+ DwarfForm form) {
+ // Create the abbreviation table.
+ Label abbrev_table = abbrevs.Here();
+ abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no)
+ .Attribute(name, form)
+ .EndAbbrev()
+ .EndTable();
+
+ // Create the compilation unit, up to the attribute value.
+ info.set_format_size(params.format_size);
+ info.set_endianness(params.endianness);
+ info.Header(params.version, abbrev_table, params.address_size)
+ .ULEB128(1); // abbrev code
+ }
+
+ // Set up handler to expect a compilation unit matching |params|,
+ // containing one childless DIE of the given tag, in the sequence s. Stop
+ // just before the expectations.
+ void ExpectBeginCompilationUnit(const DwarfHeaderParams &params,
+ DwarfTag tag, uint64 offset=0) {
+ EXPECT_CALL(handler,
+ StartCompilationUnit(offset, params.address_size,
+ params.format_size, _,
+ params.version))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(handler, StartDIE(_, tag))
+ .InSequence(s)
+ .WillOnce(Return(true));
+ }
+
+ void ExpectEndCompilationUnit() {
+ EXPECT_CALL(handler, EndDIE(_))
+ .InSequence(s)
+ .WillOnce(Return());
+ }
+
+ void ParseCompilationUnit(const DwarfHeaderParams &params, uint64 offset=0) {
+ ByteReader byte_reader(params.endianness == kLittleEndian ?
+ ENDIANNESS_LITTLE : ENDIANNESS_BIG);
+ CompilationUnit parser("", MakeSectionMap(), offset, &byte_reader, &handler);
+ EXPECT_EQ(offset + parser.Start(), info_contents.size());
+ }
+
+ // The sequence to which the fixture's methods append expectations.
+ Sequence s;
+};
+
+struct DwarfForms: public DwarfFormsFixture,
+ public TestWithParam<DwarfHeaderParams> { };
+
+TEST_P(DwarfForms, addr) {
+ StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit,
+ dwarf2reader::DW_AT_low_pc,
+ dwarf2reader::DW_FORM_addr);
+ uint64_t value;
+ if (GetParam().address_size == 4) {
+ value = 0xc8e9ffcc;
+ info.D32(value);
+ } else {
+ value = 0xe942517fc2768564ULL;
+ info.D64(value);
+ }
+ info.Finish();
+
+ ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit);
+ EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc,
+ dwarf2reader::DW_FORM_addr,
+ value))
+ .InSequence(s)
+ .WillOnce(Return());
+ ExpectEndCompilationUnit();
+
+ ParseCompilationUnit(GetParam());
+}
+
+TEST_P(DwarfForms, block2_empty) {
+ StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
+ (DwarfAttribute) 0xe52c4463,
+ dwarf2reader::DW_FORM_block2);
+ info.D16(0);
+ info.Finish();
+
+ ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
+ EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
+ dwarf2reader::DW_FORM_block2,
+ _, 0))
+ .InSequence(s)
+ .WillOnce(Return());
+ ExpectEndCompilationUnit();
+
+ ParseCompilationUnit(GetParam());
+}
+
+TEST_P(DwarfForms, block2) {
+ StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
+ (DwarfAttribute) 0xe52c4463,
+ dwarf2reader::DW_FORM_block2);
+ unsigned char data[258];
+ memset(data, '*', sizeof(data));
+ info.D16(sizeof(data))
+ .Append(data, sizeof(data));
+ info.Finish();
+
+ ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
+ EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
+ dwarf2reader::DW_FORM_block2,
+ Pointee('*'), 258))
+ .InSequence(s)
+ .WillOnce(Return());
+ ExpectEndCompilationUnit();
+
+ ParseCompilationUnit(GetParam());
+}
+
+TEST_P(DwarfForms, flag_present) {
+ StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2,
+ (DwarfAttribute) 0x359d1972,
+ dwarf2reader::DW_FORM_flag_present);
+ // DW_FORM_flag_present occupies no space in the DIE.
+ info.Finish();
+
+ ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2);
+ EXPECT_CALL(handler,
+ ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972,
+ dwarf2reader::DW_FORM_flag_present,
+ 1))
+ .InSequence(s)
+ .WillOnce(Return());
+ ExpectEndCompilationUnit();
+
+ ParseCompilationUnit(GetParam());
+}
+
+TEST_P(DwarfForms, sec_offset) {
+ StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689,
+ (DwarfAttribute) 0xa060bfd1,
+ dwarf2reader::DW_FORM_sec_offset);
+ uint64_t value;
+ if (GetParam().format_size == 4) {
+ value = 0xacc9c388;
+ info.D32(value);
+ } else {
+ value = 0xcffe5696ffe3ed0aULL;
+ info.D64(value);
+ }
+ info.Finish();
+
+ ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689);
+ EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1,
+ dwarf2reader::DW_FORM_sec_offset,
+ value))
+ .InSequence(s)
+ .WillOnce(Return());
+ ExpectEndCompilationUnit();
+
+ ParseCompilationUnit(GetParam());
+}
+
+TEST_P(DwarfForms, exprloc) {
+ StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb,
+ (DwarfAttribute) 0xba3ae5cb,
+ dwarf2reader::DW_FORM_exprloc);
+ info.ULEB128(29)
+ .Append(29, 173);
+ info.Finish();
+
+ ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb);
+ EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb,
+ dwarf2reader::DW_FORM_exprloc,
+ Pointee(173), 29))
+ .InSequence(s)
+ .WillOnce(Return());
+ ExpectEndCompilationUnit();
+
+ ParseCompilationUnit(GetParam());
+}
+
+TEST_P(DwarfForms, ref_sig8) {
+ StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
+ (DwarfAttribute) 0xd708d908,
+ dwarf2reader::DW_FORM_ref_sig8);
+ info.D64(0xf72fa0cb6ddcf9d6ULL);
+ info.Finish();
+
+ ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b);
+ EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
+ dwarf2reader::DW_FORM_ref_sig8,
+ 0xf72fa0cb6ddcf9d6ULL))
+ .InSequence(s)
+ .WillOnce(Return());
+ ExpectEndCompilationUnit();
+
+ ParseCompilationUnit(GetParam());
+}
+
+// A value passed to ProcessAttributeSignature is just an absolute number,
+// not an offset within the compilation unit as most of the other
+// DW_FORM_ref forms are. Check that the reader doesn't try to apply any
+// offset to the signature, by reading it from a compilation unit that does
+// not start at the beginning of the section.
+TEST_P(DwarfForms, ref_sig8_not_first) {
+ info.Append(98, '*');
+ StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
+ (DwarfAttribute) 0xd708d908,
+ dwarf2reader::DW_FORM_ref_sig8);
+ info.D64(0xf72fa0cb6ddcf9d6ULL);
+ info.Finish();
+
+ ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98);
+ EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
+ dwarf2reader::DW_FORM_ref_sig8,
+ 0xf72fa0cb6ddcf9d6ULL))
+ .InSequence(s)
+ .WillOnce(Return());
+ ExpectEndCompilationUnit();
+
+ ParseCompilationUnit(GetParam(), 98);
+}
+
+// Tests for the other attribute forms could go here.
+
+INSTANTIATE_TEST_CASE_P(
+ HeaderVariants, DwarfForms,
+ ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
+ DwarfHeaderParams(kLittleEndian, 4, 2, 8),
+ DwarfHeaderParams(kLittleEndian, 4, 3, 4),
+ DwarfHeaderParams(kLittleEndian, 4, 3, 8),
+ DwarfHeaderParams(kLittleEndian, 4, 4, 4),
+ DwarfHeaderParams(kLittleEndian, 4, 4, 8),
+ DwarfHeaderParams(kLittleEndian, 8, 2, 4),
+ DwarfHeaderParams(kLittleEndian, 8, 2, 8),
+ DwarfHeaderParams(kLittleEndian, 8, 3, 4),
+ DwarfHeaderParams(kLittleEndian, 8, 3, 8),
+ DwarfHeaderParams(kLittleEndian, 8, 4, 4),
+ DwarfHeaderParams(kLittleEndian, 8, 4, 8),
+ DwarfHeaderParams(kBigEndian, 4, 2, 4),
+ DwarfHeaderParams(kBigEndian, 4, 2, 8),
+ DwarfHeaderParams(kBigEndian, 4, 3, 4),
+ DwarfHeaderParams(kBigEndian, 4, 3, 8),
+ DwarfHeaderParams(kBigEndian, 4, 4, 4),
+ DwarfHeaderParams(kBigEndian, 4, 4, 8),
+ DwarfHeaderParams(kBigEndian, 8, 2, 4),
+ DwarfHeaderParams(kBigEndian, 8, 2, 8),
+ DwarfHeaderParams(kBigEndian, 8, 3, 4),
+ DwarfHeaderParams(kBigEndian, 8, 3, 8),
+ DwarfHeaderParams(kBigEndian, 8, 4, 4),
+ DwarfHeaderParams(kBigEndian, 8, 4, 8)));
diff --git a/3rdParty/Breakpad/src/common/dwarf/dwarf2reader_test_common.h b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader_test_common.h
new file mode 100644
index 0000000..e91de90
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/dwarf2reader_test_common.h
@@ -0,0 +1,149 @@
+// -*- mode: c++ -*-
+
+// Copyright (c) 2012, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dwarf2reader_test_common.h: Define TestCompilationUnit and
+// TestAbbrevTable, classes for creating properly (and improperly)
+// formatted DWARF compilation unit data for unit tests.
+
+#ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
+#define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
+
+#include "common/test_assembler.h"
+#include "common/dwarf/dwarf2enums.h"
+
+// A subclass of test_assembler::Section, specialized for constructing
+// DWARF compilation units.
+class TestCompilationUnit: public google_breakpad::test_assembler::Section {
+ public:
+ typedef dwarf2reader::DwarfTag DwarfTag;
+ typedef dwarf2reader::DwarfAttribute DwarfAttribute;
+ typedef dwarf2reader::DwarfForm DwarfForm;
+ typedef google_breakpad::test_assembler::Label Label;
+
+ // Set the section's DWARF format size (the 32-bit DWARF format or the
+ // 64-bit DWARF format, for lengths and section offsets --- not the
+ // address size) to format_size.
+ void set_format_size(size_t format_size) {
+ assert(format_size == 4 || format_size == 8);
+ format_size_ = format_size;
+ }
+
+ // Append a DWARF section offset value, of the appropriate size for this
+ // compilation unit.
+ template<typename T>
+ void SectionOffset(T offset) {
+ if (format_size_ == 4)
+ D32(offset);
+ else
+ D64(offset);
+ }
+
+ // Append a DWARF compilation unit header to the section, with the given
+ // DWARF version, abbrev table offset, and address size.
+ TestCompilationUnit &Header(int version, const Label &abbrev_offset,
+ size_t address_size) {
+ if (format_size_ == 4) {
+ D32(length_);
+ } else {
+ D32(0xffffffff);
+ D64(length_);
+ }
+ post_length_offset_ = Size();
+ D16(version);
+ SectionOffset(abbrev_offset);
+ D8(address_size);
+ return *this;
+ }
+
+ // Mark the end of this header's DIEs.
+ TestCompilationUnit &Finish() {
+ length_ = Size() - post_length_offset_;
+ return *this;
+ }
+
+ private:
+ // The DWARF format size for this compilation unit.
+ size_t format_size_;
+
+ // The offset of the point in the compilation unit header immediately
+ // after the initial length field.
+ uint64_t post_length_offset_;
+
+ // The length of the compilation unit, not including the initial length field.
+ Label length_;
+};
+
+// A subclass of test_assembler::Section specialized for constructing DWARF
+// abbreviation tables.
+class TestAbbrevTable: public google_breakpad::test_assembler::Section {
+ public:
+ typedef dwarf2reader::DwarfTag DwarfTag;
+ typedef dwarf2reader::DwarfAttribute DwarfAttribute;
+ typedef dwarf2reader::DwarfForm DwarfForm;
+ typedef dwarf2reader::DwarfHasChild DwarfHasChild;
+ typedef google_breakpad::test_assembler::Label Label;
+
+ // Start a new abbreviation table entry for abbreviation code |code|,
+ // encoding a DIE whose tag is |tag|, and which has children if and only
+ // if |has_children| is true.
+ TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) {
+ assert(code != 0);
+ ULEB128(code);
+ ULEB128(static_cast<unsigned>(tag));
+ D8(static_cast<unsigned>(has_children));
+ return *this;
+ };
+
+ // Add an attribute to the current abbreviation code whose name is |name|
+ // and whose form is |form|.
+ TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) {
+ ULEB128(static_cast<unsigned>(name));
+ ULEB128(static_cast<unsigned>(form));
+ return *this;
+ }
+
+ // Finish the current abbreviation code.
+ TestAbbrevTable &EndAbbrev() {
+ ULEB128(0);
+ ULEB128(0);
+ return *this;
+ }
+
+ // Finish the current abbreviation table.
+ TestAbbrevTable &EndTable() {
+ ULEB128(0);
+ return *this;
+ }
+};
+
+#endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
diff --git a/3rdParty/Breakpad/src/common/dwarf/elf_reader.cc b/3rdParty/Breakpad/src/common/dwarf/elf_reader.cc
new file mode 100644
index 0000000..4135a51
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/elf_reader.cc
@@ -0,0 +1,1273 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+// Author: chatham@google.com (Andrew Chatham)
+// Author: satorux@google.com (Satoru Takabayashi)
+//
+// Code for reading in ELF files.
+//
+// For information on the ELF format, see
+// http://www.x86.org/ftp/manuals/tools/elf.pdf
+//
+// I also liked:
+// http://www.caldera.com/developers/gabi/1998-04-29/contents.html
+//
+// A note about types: When dealing with the file format, we use types
+// like Elf32_Word, but in the public interfaces we treat all
+// addresses as uint64. As a result, we should be able to symbolize
+// 64-bit binaries from a 32-bit process (which we don't do,
+// anyway). size_t should therefore be avoided, except where required
+// by things like mmap().
+//
+// Although most of this code can deal with arbitrary ELF files of
+// either word size, the public ElfReader interface only examines
+// files loaded into the current address space, which must all match
+// __WORDSIZE. This code cannot handle ELF files with a non-native
+// byte ordering.
+//
+// TODO(chatham): It would be nice if we could accomplish this task
+// without using malloc(), so we could use it as the process is dying.
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // needed for pread()
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
+// TODO(saugustine): Add support for compressed debug.
+// Also need to add configure tests for zlib.
+//#include "zlib.h"
+
+#include "third_party/musl/include/elf.h"
+#include "elf_reader.h"
+#include "common/using_std_string.h"
+
+// EM_AARCH64 is not defined by elf.h of GRTE v3 on x86.
+// TODO(dougkwan): Remove this when v17 is retired.
+#if !defined(EM_AARCH64)
+#define EM_AARCH64 183 /* ARM AARCH64 */
+#endif
+
+// Map Linux macros to their Apple equivalents.
+#if __APPLE__
+#ifndef __LITTLE_ENDIAN
+#define __LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+#endif // __LITTLE_ENDIAN
+#ifndef __BIG_ENDIAN
+#define __BIG_ENDIAN __ORDER_BIG_ENDIAN__
+#endif // __BIG_ENDIAN
+#ifndef __BYTE_ORDER
+#define __BYTE_ORDER __BYTE_ORDER__
+#endif // __BYTE_ORDER
+#endif // __APPLE__
+
+// TODO(dthomson): Can be removed once all Java code is using the Google3
+// launcher. We need to avoid processing PLT functions as it causes memory
+// fragmentation in malloc, which is fixed in tcmalloc - and if the Google3
+// launcher is used the JVM will then use tcmalloc. b/13735638
+//DEFINE_bool(elfreader_process_dynsyms, true,
+// "Activate PLT function processing");
+
+using std::vector;
+
+namespace {
+
+// The lowest bit of an ARM symbol value is used to indicate a Thumb address.
+const int kARMThumbBitOffset = 0;
+
+// Converts an ARM Thumb symbol value to a true aligned address value.
+template <typename T>
+T AdjustARMThumbSymbolValue(const T& symbol_table_value) {
+ return symbol_table_value & ~(1 << kARMThumbBitOffset);
+}
+
+// Names of PLT-related sections.
+const char kElfPLTRelSectionName[] = ".rel.plt"; // Use Rel struct.
+const char kElfPLTRelaSectionName[] = ".rela.plt"; // Use Rela struct.
+const char kElfPLTSectionName[] = ".plt";
+const char kElfDynSymSectionName[] = ".dynsym";
+
+const int kX86PLTCodeSize = 0x10; // Size of one x86 PLT function in bytes.
+const int kARMPLTCodeSize = 0xc;
+const int kAARCH64PLTCodeSize = 0x10;
+
+const int kX86PLT0Size = 0x10; // Size of the special PLT0 entry.
+const int kARMPLT0Size = 0x14;
+const int kAARCH64PLT0Size = 0x20;
+
+// Suffix for PLT functions when it needs to be explicitly identified as such.
+const char kPLTFunctionSuffix[] = "@plt";
+
+} // namespace
+
+namespace dwarf2reader {
+
+template <class ElfArch> class ElfReaderImpl;
+
+// 32-bit and 64-bit ELF files are processed exactly the same, except
+// for various field sizes. Elf32 and Elf64 encompass all of the
+// differences between the two formats, and all format-specific code
+// in this file is templated on one of them.
+class Elf32 {
+ public:
+ typedef Elf32_Ehdr Ehdr;
+ typedef Elf32_Shdr Shdr;
+ typedef Elf32_Phdr Phdr;
+ typedef Elf32_Word Word;
+ typedef Elf32_Sym Sym;
+ typedef Elf32_Rel Rel;
+ typedef Elf32_Rela Rela;
+
+ // What should be in the EI_CLASS header.
+ static const int kElfClass = ELFCLASS32;
+
+ // Given a symbol pointer, return the binding type (eg STB_WEAK).
+ static char Bind(const Elf32_Sym *sym) {
+ return ELF32_ST_BIND(sym->st_info);
+ }
+ // Given a symbol pointer, return the symbol type (eg STT_FUNC).
+ static char Type(const Elf32_Sym *sym) {
+ return ELF32_ST_TYPE(sym->st_info);
+ }
+
+ // Extract the symbol index from the r_info field of a relocation.
+ static int r_sym(const Elf32_Word r_info) {
+ return ELF32_R_SYM(r_info);
+ }
+};
+
+
+class Elf64 {
+ public:
+ typedef Elf64_Ehdr Ehdr;
+ typedef Elf64_Shdr Shdr;
+ typedef Elf64_Phdr Phdr;
+ typedef Elf64_Word Word;
+ typedef Elf64_Sym Sym;
+ typedef Elf64_Rel Rel;
+ typedef Elf64_Rela Rela;
+
+ // What should be in the EI_CLASS header.
+ static const int kElfClass = ELFCLASS64;
+
+ static char Bind(const Elf64_Sym *sym) {
+ return ELF64_ST_BIND(sym->st_info);
+ }
+ static char Type(const Elf64_Sym *sym) {
+ return ELF64_ST_TYPE(sym->st_info);
+ }
+ static int r_sym(const Elf64_Xword r_info) {
+ return ELF64_R_SYM(r_info);
+ }
+};
+
+
+// ElfSectionReader mmaps a section of an ELF file ("section" is ELF
+// terminology). The ElfReaderImpl object providing the section header
+// must exist for the lifetime of this object.
+//
+// The motivation for mmaping individual sections of the file is that
+// many Google executables are large enough when unstripped that we
+// have to worry about running out of virtual address space.
+//
+// For compressed sections we have no choice but to allocate memory.
+template<class ElfArch>
+class ElfSectionReader {
+ public:
+ ElfSectionReader(const char *name, const string &path, int fd,
+ const typename ElfArch::Shdr &section_header)
+ : contents_aligned_(NULL),
+ contents_(NULL),
+ header_(section_header) {
+ // Back up to the beginning of the page we're interested in.
+ const size_t additional = header_.sh_offset % getpagesize();
+ const size_t offset_aligned = header_.sh_offset - additional;
+ section_size_ = header_.sh_size;
+ size_aligned_ = section_size_ + additional;
+ // If the section has been stripped or is empty, do not attempt
+ // to process its contents.
+ if (header_.sh_type == SHT_NOBITS || header_.sh_size == 0)
+ return;
+ contents_aligned_ = mmap(NULL, size_aligned_, PROT_READ, MAP_SHARED,
+ fd, offset_aligned);
+ // Set where the offset really should begin.
+ contents_ = reinterpret_cast<char *>(contents_aligned_) +
+ (header_.sh_offset - offset_aligned);
+
+ // Check for and handle any compressed contents.
+ //if (strncmp(name, ".zdebug_", strlen(".zdebug_")) == 0)
+ // DecompressZlibContents();
+ // TODO(saugustine): Add support for proposed elf-section flag
+ // "SHF_COMPRESS".
+ }
+
+ ~ElfSectionReader() {
+ if (contents_aligned_ != NULL)
+ munmap(contents_aligned_, size_aligned_);
+ else
+ delete[] contents_;
+ }
+
+ // Return the section header for this section.
+ typename ElfArch::Shdr const &header() const { return header_; }
+
+ // Return memory at the given offset within this section.
+ const char *GetOffset(typename ElfArch::Word bytes) const {
+ return contents_ + bytes;
+ }
+
+ const char *contents() const { return contents_; }
+ size_t section_size() const { return section_size_; }
+
+ private:
+ // page-aligned file contents
+ void *contents_aligned_;
+ // contents as usable by the client. For non-compressed sections,
+ // pointer within contents_aligned_ to where the section data
+ // begins; for compressed sections, pointer to the decompressed
+ // data.
+ char *contents_;
+ // size of contents_aligned_
+ size_t size_aligned_;
+ // size of contents.
+ size_t section_size_;
+ const typename ElfArch::Shdr header_;
+};
+
+// An iterator over symbols in a given section. It handles walking
+// through the entries in the specified section and mapping symbol
+// entries to their names in the appropriate string table (in
+// another section).
+template<class ElfArch>
+class SymbolIterator {
+ public:
+ SymbolIterator(ElfReaderImpl<ElfArch> *reader,
+ typename ElfArch::Word section_type)
+ : symbol_section_(reader->GetSectionByType(section_type)),
+ string_section_(NULL),
+ num_symbols_in_section_(0),
+ symbol_within_section_(0) {
+
+ // If this section type doesn't exist, leave
+ // num_symbols_in_section_ as zero, so this iterator is already
+ // done().
+ if (symbol_section_ != NULL) {
+ num_symbols_in_section_ = symbol_section_->header().sh_size /
+ symbol_section_->header().sh_entsize;
+
+ // Symbol sections have sh_link set to the section number of
+ // the string section containing the symbol names.
+ string_section_ = reader->GetSection(symbol_section_->header().sh_link);
+ }
+ }
+
+ // Return true iff we have passed all symbols in this section.
+ bool done() const {
+ return symbol_within_section_ >= num_symbols_in_section_;
+ }
+
+ // Advance to the next symbol in this section.
+ // REQUIRES: !done()
+ void Next() { ++symbol_within_section_; }
+
+ // Return a pointer to the current symbol.
+ // REQUIRES: !done()
+ const typename ElfArch::Sym *GetSymbol() const {
+ return reinterpret_cast<const typename ElfArch::Sym*>(
+ symbol_section_->GetOffset(symbol_within_section_ *
+ symbol_section_->header().sh_entsize));
+ }
+
+ // Return the name of the current symbol, NULL if it has none.
+ // REQUIRES: !done()
+ const char *GetSymbolName() const {
+ int name_offset = GetSymbol()->st_name;
+ if (name_offset == 0)
+ return NULL;
+ return string_section_->GetOffset(name_offset);
+ }
+
+ int GetCurrentSymbolIndex() const {
+ return symbol_within_section_;
+ }
+
+ private:
+ const ElfSectionReader<ElfArch> *const symbol_section_;
+ const ElfSectionReader<ElfArch> *string_section_;
+ int num_symbols_in_section_;
+ int symbol_within_section_;
+};
+
+
+// Copied from strings/strutil.h. Per chatham,
+// this library should not depend on strings.
+
+static inline bool MyHasSuffixString(const string& str, const string& suffix) {
+ int len = str.length();
+ int suflen = suffix.length();
+ return (suflen <= len) && (str.compare(len-suflen, suflen, suffix) == 0);
+}
+
+
+// ElfReader loads an ELF binary and can provide information about its
+// contents. It is most useful for matching addresses to function
+// names. It does not understand debugging formats (eg dwarf2), so it
+// can't print line numbers. It takes a path to an elf file and a
+// readable file descriptor for that file, which it does not assume
+// ownership of.
+template<class ElfArch>
+class ElfReaderImpl {
+ public:
+ explicit ElfReaderImpl(const string &path, int fd)
+ : path_(path),
+ fd_(fd),
+ section_headers_(NULL),
+ program_headers_(NULL),
+ opd_section_(NULL),
+ base_for_text_(0),
+ plts_supported_(false),
+ plt_code_size_(0),
+ plt0_size_(0),
+ visited_relocation_entries_(false) {
+ string error;
+ is_dwp_ = MyHasSuffixString(path, ".dwp");
+ ParseHeaders(fd, path);
+ // Currently we need some extra information for PowerPC64 binaries
+ // including a way to read the .opd section for function descriptors and a
+ // way to find the linked base for function symbols.
+ if (header_.e_machine == EM_PPC64) {
+ // "opd_section_" must always be checked for NULL before use.
+ opd_section_ = GetSectionInfoByName(".opd", &opd_info_);
+ for (unsigned int k = 0u; k < GetNumSections(); ++k) {
+ const char *name = GetSectionName(section_headers_[k].sh_name);
+ if (strncmp(name, ".text", strlen(".text")) == 0) {
+ base_for_text_ =
+ section_headers_[k].sh_addr - section_headers_[k].sh_offset;
+ break;
+ }
+ }
+ }
+ // Turn on PLTs.
+ if (header_.e_machine == EM_386 || header_.e_machine == EM_X86_64) {
+ plt_code_size_ = kX86PLTCodeSize;
+ plt0_size_ = kX86PLT0Size;
+ plts_supported_ = true;
+ } else if (header_.e_machine == EM_ARM) {
+ plt_code_size_ = kARMPLTCodeSize;
+ plt0_size_ = kARMPLT0Size;
+ plts_supported_ = true;
+ } else if (header_.e_machine == EM_AARCH64) {
+ plt_code_size_ = kAARCH64PLTCodeSize;
+ plt0_size_ = kAARCH64PLT0Size;
+ plts_supported_ = true;
+ }
+ }
+
+ ~ElfReaderImpl() {
+ for (unsigned int i = 0u; i < sections_.size(); ++i)
+ delete sections_[i];
+ delete [] section_headers_;
+ delete [] program_headers_;
+ }
+
+ // Examine the headers of the file and return whether the file looks
+ // like an ELF file for this architecture. Takes an already-open
+ // file descriptor for the candidate file, reading in the prologue
+ // to see if the ELF file appears to match the current
+ // architecture. If error is non-NULL, it will be set with a reason
+ // in case of failure.
+ static bool IsArchElfFile(int fd, string *error) {
+ unsigned char header[EI_NIDENT];
+ if (pread(fd, header, sizeof(header), 0) != sizeof(header)) {
+ if (error != NULL) *error = "Could not read header";
+ return false;
+ }
+
+ if (memcmp(header, ELFMAG, SELFMAG) != 0) {
+ if (error != NULL) *error = "Missing ELF magic";
+ return false;
+ }
+
+ if (header[EI_CLASS] != ElfArch::kElfClass) {
+ if (error != NULL) *error = "Different word size";
+ return false;
+ }
+
+ int endian = 0;
+ if (header[EI_DATA] == ELFDATA2LSB)
+ endian = __LITTLE_ENDIAN;
+ else if (header[EI_DATA] == ELFDATA2MSB)
+ endian = __BIG_ENDIAN;
+ if (endian != __BYTE_ORDER) {
+ if (error != NULL) *error = "Different byte order";
+ return false;
+ }
+
+ return true;
+ }
+
+ // Return true if we can use this symbol in Address-to-Symbol map.
+ bool CanUseSymbol(const char *name, const typename ElfArch::Sym *sym) {
+ // For now we only save FUNC and NOTYPE symbols. For now we just
+ // care about functions, but some functions written in assembler
+ // don't have a proper ELF type attached to them, so we store
+ // NOTYPE symbols as well. The remaining significant type is
+ // OBJECT (eg global variables), which represent about 25% of
+ // the symbols in a typical google3 binary.
+ if (ElfArch::Type(sym) != STT_FUNC &&
+ ElfArch::Type(sym) != STT_NOTYPE) {
+ return false;
+ }
+
+ // Target specific filtering.
+ switch (header_.e_machine) {
+ case EM_AARCH64:
+ case EM_ARM:
+ // Filter out '$x' special local symbols used by tools
+ return name[0] != '$' || ElfArch::Bind(sym) != STB_LOCAL;
+ case EM_X86_64:
+ // Filter out read-only constants like .LC123.
+ return name[0] != '.' || ElfArch::Bind(sym) != STB_LOCAL;
+ default:
+ return true;
+ }
+ }
+
+ // Iterate over the symbols in a section, either SHT_DYNSYM or
+ // SHT_SYMTAB. Add all symbols to the given SymbolMap.
+ /*
+ void GetSymbolPositions(SymbolMap *symbols,
+ typename ElfArch::Word section_type,
+ uint64 mem_offset,
+ uint64 file_offset) {
+ // This map is used to filter out "nested" functions.
+ // See comment below.
+ AddrToSymMap addr_to_sym_map;
+ for (SymbolIterator<ElfArch> it(this, section_type);
+ !it.done(); it.Next()) {
+ const char *name = it.GetSymbolName();
+ if (name == NULL)
+ continue;
+ const typename ElfArch::Sym *sym = it.GetSymbol();
+ if (CanUseSymbol(name, sym)) {
+ const int sec = sym->st_shndx;
+
+ // We don't support special section indices. The most common
+ // is SHN_ABS, for absolute symbols used deep in the bowels of
+ // glibc. Also ignore any undefined symbols.
+ if (sec == SHN_UNDEF ||
+ (sec >= SHN_LORESERVE && sec <= SHN_HIRESERVE)) {
+ continue;
+ }
+
+ const typename ElfArch::Shdr& hdr = section_headers_[sec];
+
+ // Adjust for difference between where we expected to mmap
+ // this section, and where it was actually mmapped.
+ const int64 expected_base = hdr.sh_addr - hdr.sh_offset;
+ const int64 real_base = mem_offset - file_offset;
+ const int64 adjust = real_base - expected_base;
+
+ uint64 start = sym->st_value + adjust;
+
+ // Adjust function symbols for PowerPC64 by dereferencing and adjusting
+ // the function descriptor to get the function address.
+ if (header_.e_machine == EM_PPC64 && ElfArch::Type(sym) == STT_FUNC) {
+ const uint64 opd_addr =
+ AdjustPPC64FunctionDescriptorSymbolValue(sym->st_value);
+ // Only adjust the returned value if the function address was found.
+ if (opd_addr != sym->st_value) {
+ const int64 adjust_function_symbols =
+ real_base - base_for_text_;
+ start = opd_addr + adjust_function_symbols;
+ }
+ }
+
+ addr_to_sym_map.push_back(std::make_pair(start, sym));
+ }
+ }
+ std::sort(addr_to_sym_map.begin(), addr_to_sym_map.end(), &AddrToSymSorter);
+ addr_to_sym_map.erase(std::unique(addr_to_sym_map.begin(),
+ addr_to_sym_map.end(), &AddrToSymEquals),
+ addr_to_sym_map.end());
+
+ // Squeeze out any "nested functions".
+ // Nested functions are not allowed in C, but libc plays tricks.
+ //
+ // For example, here is disassembly of /lib64/tls/libc-2.3.5.so:
+ // 0x00000000000aa380 <read+0>: cmpl $0x0,0x2781b9(%rip)
+ // 0x00000000000aa387 <read+7>: jne 0xaa39b <read+27>
+ // 0x00000000000aa389 <__read_nocancel+0>: mov $0x0,%rax
+ // 0x00000000000aa390 <__read_nocancel+7>: syscall
+ // 0x00000000000aa392 <__read_nocancel+9>: cmp $0xfffffffffffff001,%rax
+ // 0x00000000000aa398 <__read_nocancel+15>: jae 0xaa3ef <read+111>
+ // 0x00000000000aa39a <__read_nocancel+17>: retq
+ // 0x00000000000aa39b <read+27>: sub $0x28,%rsp
+ // 0x00000000000aa39f <read+31>: mov %rdi,0x8(%rsp)
+ // ...
+ // Without removing __read_nocancel, symbolizer will return NULL
+ // given e.g. 0xaa39f (because the lower bound is __read_nocancel,
+ // but 0xaa39f is beyond its end.
+ if (addr_to_sym_map.empty()) {
+ return;
+ }
+ const ElfSectionReader<ElfArch> *const symbol_section =
+ this->GetSectionByType(section_type);
+ const ElfSectionReader<ElfArch> *const string_section =
+ this->GetSection(symbol_section->header().sh_link);
+
+ typename AddrToSymMap::iterator curr = addr_to_sym_map.begin();
+ // Always insert the first symbol.
+ symbols->AddSymbol(string_section->GetOffset(curr->second->st_name),
+ curr->first, curr->second->st_size);
+ typename AddrToSymMap::iterator prev = curr++;
+ for (; curr != addr_to_sym_map.end(); ++curr) {
+ const uint64 prev_addr = prev->first;
+ const uint64 curr_addr = curr->first;
+ const typename ElfArch::Sym *const prev_sym = prev->second;
+ const typename ElfArch::Sym *const curr_sym = curr->second;
+ if (prev_addr + prev_sym->st_size <= curr_addr ||
+ // The next condition is true if two symbols overlap like this:
+ //
+ // Previous symbol |----------------------------|
+ // Current symbol |-------------------------------|
+ //
+ // These symbols are not found in google3 codebase, but in
+ // jdk1.6.0_01_gg1/jre/lib/i386/server/libjvm.so.
+ //
+ // 0619e040 00000046 t CardTableModRefBS::write_region_work()
+ // 0619e070 00000046 t CardTableModRefBS::write_ref_array_work()
+ //
+ // We allow overlapped symbols rather than ignore these.
+ // Due to the way SymbolMap::GetSymbolAtPosition() works,
+ // lookup for any address in [curr_addr, curr_addr + its size)
+ // (e.g. 0619e071) will produce the current symbol,
+ // which is the desired outcome.
+ prev_addr + prev_sym->st_size < curr_addr + curr_sym->st_size) {
+ const char *name = string_section->GetOffset(curr_sym->st_name);
+ symbols->AddSymbol(name, curr_addr, curr_sym->st_size);
+ prev = curr;
+ } else {
+ // Current symbol is "nested" inside previous one like this:
+ //
+ // Previous symbol |----------------------------|
+ // Current symbol |---------------------|
+ //
+ // This happens within glibc, e.g. __read_nocancel is nested
+ // "inside" __read. Ignore "inner" symbol.
+ //DCHECK_LE(curr_addr + curr_sym->st_size,
+ // prev_addr + prev_sym->st_size);
+ ;
+ }
+ }
+ }
+*/
+
+ void VisitSymbols(typename ElfArch::Word section_type,
+ ElfReader::SymbolSink *sink) {
+ VisitSymbols(section_type, sink, -1, -1, false);
+ }
+
+ void VisitSymbols(typename ElfArch::Word section_type,
+ ElfReader::SymbolSink *sink,
+ int symbol_binding,
+ int symbol_type,
+ bool get_raw_symbol_values) {
+ for (SymbolIterator<ElfArch> it(this, section_type);
+ !it.done(); it.Next()) {
+ const char *name = it.GetSymbolName();
+ if (!name) continue;
+ const typename ElfArch::Sym *sym = it.GetSymbol();
+ if ((symbol_binding < 0 || ElfArch::Bind(sym) == symbol_binding) &&
+ (symbol_type < 0 || ElfArch::Type(sym) == symbol_type)) {
+ typename ElfArch::Sym symbol = *sym;
+ // Add a PLT symbol in addition to the main undefined symbol.
+ // Only do this for SHT_DYNSYM, because PLT symbols are dynamic.
+ int symbol_index = it.GetCurrentSymbolIndex();
+ // TODO(dthomson): Can be removed once all Java code is using the
+ // Google3 launcher.
+ if (section_type == SHT_DYNSYM &&
+ static_cast<unsigned int>(symbol_index) < symbols_plt_offsets_.size() &&
+ symbols_plt_offsets_[symbol_index] != 0) {
+ string plt_name = string(name) + kPLTFunctionSuffix;
+ if (plt_function_names_[symbol_index].empty()) {
+ plt_function_names_[symbol_index] = plt_name;
+ } else if (plt_function_names_[symbol_index] != plt_name) {
+ ;
+ }
+ sink->AddSymbol(plt_function_names_[symbol_index].c_str(),
+ symbols_plt_offsets_[it.GetCurrentSymbolIndex()],
+ plt_code_size_);
+ }
+ if (!get_raw_symbol_values)
+ AdjustSymbolValue(&symbol);
+ sink->AddSymbol(name, symbol.st_value, symbol.st_size);
+ }
+ }
+ }
+
+ void VisitRelocationEntries() {
+ if (visited_relocation_entries_) {
+ return;
+ }
+ visited_relocation_entries_ = true;
+
+ if (!plts_supported_) {
+ return;
+ }
+ // First determine if PLTs exist. If not, then there is nothing to do.
+ ElfReader::SectionInfo plt_section_info;
+ const char* plt_section =
+ GetSectionInfoByName(kElfPLTSectionName, &plt_section_info);
+ if (!plt_section) {
+ return;
+ }
+ if (plt_section_info.size == 0) {
+ return;
+ }
+
+ // The PLTs could be referenced by either a Rel or Rela (Rel with Addend)
+ // section.
+ ElfReader::SectionInfo rel_section_info;
+ ElfReader::SectionInfo rela_section_info;
+ const char* rel_section =
+ GetSectionInfoByName(kElfPLTRelSectionName, &rel_section_info);
+ const char* rela_section =
+ GetSectionInfoByName(kElfPLTRelaSectionName, &rela_section_info);
+
+ const typename ElfArch::Rel* rel =
+ reinterpret_cast<const typename ElfArch::Rel*>(rel_section);
+ const typename ElfArch::Rela* rela =
+ reinterpret_cast<const typename ElfArch::Rela*>(rela_section);
+
+ if (!rel_section && !rela_section) {
+ return;
+ }
+
+ // Use either Rel or Rela section, depending on which one exists.
+ size_t section_size = rel_section ? rel_section_info.size
+ : rela_section_info.size;
+ size_t entry_size = rel_section ? sizeof(typename ElfArch::Rel)
+ : sizeof(typename ElfArch::Rela);
+
+ // Determine the number of entries in the dynamic symbol table.
+ ElfReader::SectionInfo dynsym_section_info;
+ const char* dynsym_section =
+ GetSectionInfoByName(kElfDynSymSectionName, &dynsym_section_info);
+ // The dynsym section might not exist, or it might be empty. In either case
+ // there is nothing to be done so return.
+ if (!dynsym_section || dynsym_section_info.size == 0) {
+ return;
+ }
+ size_t num_dynamic_symbols =
+ dynsym_section_info.size / dynsym_section_info.entsize;
+ symbols_plt_offsets_.resize(num_dynamic_symbols, 0);
+
+ // TODO(dthomson): Can be removed once all Java code is using the
+ // Google3 launcher.
+ // Make storage room for PLT function name strings.
+ plt_function_names_.resize(num_dynamic_symbols);
+
+ for (size_t i = 0; i < section_size / entry_size; ++i) {
+ // Determine symbol index from the |r_info| field.
+ int sym_index = ElfArch::r_sym(rel_section ? rel[i].r_info
+ : rela[i].r_info);
+ if (static_cast<unsigned int>(sym_index) >= symbols_plt_offsets_.size()) {
+ continue;
+ }
+ symbols_plt_offsets_[sym_index] =
+ plt_section_info.addr + plt0_size_ + i * plt_code_size_;
+ }
+ }
+
+ // Return an ElfSectionReader for the first section of the given
+ // type by iterating through all section headers. Returns NULL if
+ // the section type is not found.
+ const ElfSectionReader<ElfArch> *GetSectionByType(
+ typename ElfArch::Word section_type) {
+ for (unsigned int k = 0u; k < GetNumSections(); ++k) {
+ if (section_headers_[k].sh_type == section_type) {
+ return GetSection(k);
+ }
+ }
+ return NULL;
+ }
+
+ // Return the name of section "shndx". Returns NULL if the section
+ // is not found.
+ const char *GetSectionNameByIndex(int shndx) {
+ return GetSectionName(section_headers_[shndx].sh_name);
+ }
+
+ // Return a pointer to section "shndx", and store the size in
+ // "size". Returns NULL if the section is not found.
+ const char *GetSectionContentsByIndex(int shndx, size_t *size) {
+ const ElfSectionReader<ElfArch> *section = GetSection(shndx);
+ if (section != NULL) {
+ *size = section->section_size();
+ return section->contents();
+ }
+ return NULL;
+ }
+
+ // Return a pointer to the first section of the given name by
+ // iterating through all section headers, and store the size in
+ // "size". Returns NULL if the section name is not found.
+ const char *GetSectionContentsByName(const string &section_name,
+ size_t *size) {
+ for (unsigned int k = 0u; k < GetNumSections(); ++k) {
+ // When searching for sections in a .dwp file, the sections
+ // we're looking for will always be at the end of the section
+ // table, so reverse the direction of iteration.
+ int shndx = is_dwp_ ? GetNumSections() - k - 1 : k;
+ const char *name = GetSectionName(section_headers_[shndx].sh_name);
+ if (name != NULL && ElfReader::SectionNamesMatch(section_name, name)) {
+ const ElfSectionReader<ElfArch> *section = GetSection(shndx);
+ if (section == NULL) {
+ return NULL;
+ } else {
+ *size = section->section_size();
+ return section->contents();
+ }
+ }
+ }
+ return NULL;
+ }
+
+ // This is like GetSectionContentsByName() but it returns a lot of extra
+ // information about the section.
+ const char *GetSectionInfoByName(const string &section_name,
+ ElfReader::SectionInfo *info) {
+ for (unsigned int k = 0u; k < GetNumSections(); ++k) {
+ // When searching for sections in a .dwp file, the sections
+ // we're looking for will always be at the end of the section
+ // table, so reverse the direction of iteration.
+ int shndx = is_dwp_ ? GetNumSections() - k - 1 : k;
+ const char *name = GetSectionName(section_headers_[shndx].sh_name);
+ if (name != NULL && ElfReader::SectionNamesMatch(section_name, name)) {
+ const ElfSectionReader<ElfArch> *section = GetSection(shndx);
+ if (section == NULL) {
+ return NULL;
+ } else {
+ info->type = section->header().sh_type;
+ info->flags = section->header().sh_flags;
+ info->addr = section->header().sh_addr;
+ info->offset = section->header().sh_offset;
+ info->size = section->header().sh_size;
+ info->link = section->header().sh_link;
+ info->info = section->header().sh_info;
+ info->addralign = section->header().sh_addralign;
+ info->entsize = section->header().sh_entsize;
+ return section->contents();
+ }
+ }
+ }
+ return NULL;
+ }
+
+ // p_vaddr of the first PT_LOAD segment (if any), or 0 if no PT_LOAD
+ // segments are present. This is the address an ELF image was linked
+ // (by static linker) to be loaded at. Usually (but not always) 0 for
+ // shared libraries and position-independent executables.
+ uint64 VaddrOfFirstLoadSegment() const {
+ // Relocatable objects (of type ET_REL) do not have LOAD segments.
+ if (header_.e_type == ET_REL) {
+ return 0;
+ }
+ for (int i = 0; i < GetNumProgramHeaders(); ++i) {
+ if (program_headers_[i].p_type == PT_LOAD) {
+ return program_headers_[i].p_vaddr;
+ }
+ }
+ return 0;
+ }
+
+ // According to the LSB ("ELF special sections"), sections with debug
+ // info are prefixed by ".debug". The names are not specified, but they
+ // look like ".debug_line", ".debug_info", etc.
+ bool HasDebugSections() {
+ // Debug sections are likely to be near the end, so reverse the
+ // direction of iteration.
+ for (int k = GetNumSections() - 1; k >= 0; --k) {
+ const char *name = GetSectionName(section_headers_[k].sh_name);
+ if (strncmp(name, ".debug", strlen(".debug")) == 0) return true;
+ if (strncmp(name, ".zdebug", strlen(".zdebug")) == 0) return true;
+ }
+ return false;
+ }
+
+ bool IsDynamicSharedObject() const {
+ return header_.e_type == ET_DYN;
+ }
+
+ // Return the number of sections.
+ uint64_t GetNumSections() const {
+ if (HasManySections())
+ return first_section_header_.sh_size;
+ return header_.e_shnum;
+ }
+
+ private:
+ typedef vector<pair<uint64, const typename ElfArch::Sym *> > AddrToSymMap;
+
+ static bool AddrToSymSorter(const typename AddrToSymMap::value_type& lhs,
+ const typename AddrToSymMap::value_type& rhs) {
+ return lhs.first < rhs.first;
+ }
+
+ static bool AddrToSymEquals(const typename AddrToSymMap::value_type& lhs,
+ const typename AddrToSymMap::value_type& rhs) {
+ return lhs.first == rhs.first;
+ }
+
+ // Does this ELF file have too many sections to fit in the program header?
+ bool HasManySections() const {
+ return header_.e_shnum == SHN_UNDEF;
+ }
+
+ // Return the number of program headers.
+ int GetNumProgramHeaders() const {
+ if (HasManySections() && header_.e_phnum == 0xffff &&
+ first_section_header_.sh_info != 0)
+ return first_section_header_.sh_info;
+ return header_.e_phnum;
+ }
+
+ // Return the index of the string table.
+ int GetStringTableIndex() const {
+ if (HasManySections()) {
+ if (header_.e_shstrndx == 0xffff)
+ return first_section_header_.sh_link;
+ else if (header_.e_shstrndx >= GetNumSections())
+ return 0;
+ }
+ return header_.e_shstrndx;
+ }
+
+ // Given an offset into the section header string table, return the
+ // section name.
+ const char *GetSectionName(typename ElfArch::Word sh_name) {
+ const ElfSectionReader<ElfArch> *shstrtab =
+ GetSection(GetStringTableIndex());
+ if (shstrtab != NULL) {
+ return shstrtab->GetOffset(sh_name);
+ }
+ return NULL;
+ }
+
+ // Return an ElfSectionReader for the given section. The reader will
+ // be freed when this object is destroyed.
+ const ElfSectionReader<ElfArch> *GetSection(int num) {
+ const char *name;
+ // Hard-coding the name for the section-name string table prevents
+ // infinite recursion.
+ if (num == GetStringTableIndex())
+ name = ".shstrtab";
+ else
+ name = GetSectionNameByIndex(num);
+ ElfSectionReader<ElfArch> *& reader = sections_[num];
+ if (reader == NULL)
+ reader = new ElfSectionReader<ElfArch>(name, path_, fd_,
+ section_headers_[num]);
+ return reader;
+ }
+
+ // Parse out the overall header information from the file and assert
+ // that it looks sane. This contains information like the magic
+ // number and target architecture.
+ bool ParseHeaders(int fd, const string &path) {
+ // Read in the global ELF header.
+ if (pread(fd, &header_, sizeof(header_), 0) != sizeof(header_)) {
+ return false;
+ }
+
+ // Must be an executable, dynamic shared object or relocatable object
+ if (header_.e_type != ET_EXEC &&
+ header_.e_type != ET_DYN &&
+ header_.e_type != ET_REL) {
+ return false;
+ }
+ // Need a section header.
+ if (header_.e_shoff == 0) {
+ return false;
+ }
+
+ if (header_.e_shnum == SHN_UNDEF) {
+ // The number of sections in the program header is only a 16-bit value. In
+ // the event of overflow (greater than SHN_LORESERVE sections), e_shnum
+ // will read SHN_UNDEF and the true number of section header table entries
+ // is found in the sh_size field of the first section header.
+ // See: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html
+ if (pread(fd, &first_section_header_, sizeof(first_section_header_),
+ header_.e_shoff) != sizeof(first_section_header_)) {
+ return false;
+ }
+ }
+
+ // Dynamically allocate enough space to store the section headers
+ // and read them out of the file.
+ const int section_headers_size =
+ GetNumSections() * sizeof(*section_headers_);
+ section_headers_ = new typename ElfArch::Shdr[section_headers_size];
+ if (pread(fd, section_headers_, section_headers_size, header_.e_shoff) !=
+ section_headers_size) {
+ return false;
+ }
+
+ // Dynamically allocate enough space to store the program headers
+ // and read them out of the file.
+ //const int program_headers_size =
+ // GetNumProgramHeaders() * sizeof(*program_headers_);
+ program_headers_ = new typename ElfArch::Phdr[GetNumProgramHeaders()];
+
+ // Presize the sections array for efficiency.
+ sections_.resize(GetNumSections(), NULL);
+ return true;
+ }
+
+ // Given the "value" of a function descriptor return the address of the
+ // function (i.e. the dereferenced value). Otherwise return "value".
+ uint64 AdjustPPC64FunctionDescriptorSymbolValue(uint64 value) {
+ if (opd_section_ != NULL &&
+ opd_info_.addr <= value &&
+ value < opd_info_.addr + opd_info_.size) {
+ uint64 offset = value - opd_info_.addr;
+ return (*reinterpret_cast<const uint64*>(opd_section_ + offset));
+ }
+ return value;
+ }
+
+ void AdjustSymbolValue(typename ElfArch::Sym* sym) {
+ switch (header_.e_machine) {
+ case EM_ARM:
+ // For ARM architecture, if the LSB of the function symbol offset is set,
+ // it indicates a Thumb function. This bit should not be taken literally.
+ // Clear it.
+ if (ElfArch::Type(sym) == STT_FUNC)
+ sym->st_value = AdjustARMThumbSymbolValue(sym->st_value);
+ break;
+ case EM_386:
+ // No adjustment needed for Intel x86 architecture. However, explicitly
+ // define this case as we use it quite often.
+ break;
+ case EM_PPC64:
+ // PowerPC64 currently has function descriptors as part of the ABI.
+ // Function symbols need to be adjusted accordingly.
+ if (ElfArch::Type(sym) == STT_FUNC)
+ sym->st_value = AdjustPPC64FunctionDescriptorSymbolValue(sym->st_value);
+ break;
+ default:
+ break;
+ }
+ }
+
+ friend class SymbolIterator<ElfArch>;
+
+ // The file we're reading.
+ const string path_;
+ // Open file descriptor for path_. Not owned by this object.
+ const int fd_;
+
+ // The global header of the ELF file.
+ typename ElfArch::Ehdr header_;
+
+ // The header of the first section. This may be used to supplement the ELF
+ // file header.
+ typename ElfArch::Shdr first_section_header_;
+
+ // Array of GetNumSections() section headers, allocated when we read
+ // in the global header.
+ typename ElfArch::Shdr *section_headers_;
+
+ // Array of GetNumProgramHeaders() program headers, allocated when we read
+ // in the global header.
+ typename ElfArch::Phdr *program_headers_;
+
+ // An array of pointers to ElfSectionReaders. Sections are
+ // mmaped as they're needed and not released until this object is
+ // destroyed.
+ vector<ElfSectionReader<ElfArch>*> sections_;
+
+ // For PowerPC64 we need to keep track of function descriptors when looking up
+ // values for funtion symbols values. Function descriptors are kept in the
+ // .opd section and are dereferenced to find the function address.
+ ElfReader::SectionInfo opd_info_;
+ const char *opd_section_; // Must be checked for NULL before use.
+ int64 base_for_text_;
+
+ // Read PLT-related sections for the current architecture.
+ bool plts_supported_;
+ // Code size of each PLT function for the current architecture.
+ size_t plt_code_size_;
+ // Size of the special first entry in the .plt section that calls the runtime
+ // loader resolution routine, and that all other entries jump to when doing
+ // lazy symbol binding.
+ size_t plt0_size_;
+
+ // Maps a dynamic symbol index to a PLT offset.
+ // The vector entry index is the dynamic symbol index.
+ std::vector<uint64> symbols_plt_offsets_;
+
+ // Container for PLT function name strings. These strings are passed by
+ // reference to SymbolSink::AddSymbol() so they need to be stored somewhere.
+ std::vector<string> plt_function_names_;
+
+ bool visited_relocation_entries_;
+
+ // True if this is a .dwp file.
+ bool is_dwp_;
+};
+
+ElfReader::ElfReader(const string &path)
+ : path_(path), fd_(-1), impl32_(NULL), impl64_(NULL) {
+ // linux 2.6.XX kernel can show deleted files like this:
+ // /var/run/nscd/dbYLJYaE (deleted)
+ // and the kernel-supplied vdso and vsyscall mappings like this:
+ // [vdso]
+ // [vsyscall]
+ if (MyHasSuffixString(path, " (deleted)"))
+ return;
+ if (path == "[vdso]")
+ return;
+ if (path == "[vsyscall]")
+ return;
+
+ fd_ = open(path.c_str(), O_RDONLY);
+}
+
+ElfReader::~ElfReader() {
+ if (fd_ != -1)
+ close(fd_);
+ if (impl32_ != NULL)
+ delete impl32_;
+ if (impl64_ != NULL)
+ delete impl64_;
+}
+
+
+// The only word-size specific part of this file is IsNativeElfFile().
+#if __WORDSIZE == 32
+#define NATIVE_ELF_ARCH Elf32
+#elif __WORDSIZE == 64
+#define NATIVE_ELF_ARCH Elf64
+#else
+#error "Invalid word size"
+#endif
+
+template <typename ElfArch>
+static bool IsElfFile(const int fd, const string &path) {
+ if (fd < 0)
+ return false;
+ if (!ElfReaderImpl<ElfArch>::IsArchElfFile(fd, NULL)) {
+ // No error message here. IsElfFile gets called many times.
+ return false;
+ }
+ return true;
+}
+
+bool ElfReader::IsNativeElfFile() const {
+ return IsElfFile<NATIVE_ELF_ARCH>(fd_, path_);
+}
+
+bool ElfReader::IsElf32File() const {
+ return IsElfFile<Elf32>(fd_, path_);
+}
+
+bool ElfReader::IsElf64File() const {
+ return IsElfFile<Elf64>(fd_, path_);
+}
+
+/*
+void ElfReader::AddSymbols(SymbolMap *symbols,
+ uint64 mem_offset, uint64 file_offset,
+ uint64 length) {
+ if (fd_ < 0)
+ return;
+ // TODO(chatham): Actually use the information about file offset and
+ // the length of the mapped section. On some machines the data
+ // section gets mapped as executable, and we'll end up reading the
+ // file twice and getting some of the offsets wrong.
+ if (IsElf32File()) {
+ GetImpl32()->GetSymbolPositions(symbols, SHT_SYMTAB,
+ mem_offset, file_offset);
+ GetImpl32()->GetSymbolPositions(symbols, SHT_DYNSYM,
+ mem_offset, file_offset);
+ } else if (IsElf64File()) {
+ GetImpl64()->GetSymbolPositions(symbols, SHT_SYMTAB,
+ mem_offset, file_offset);
+ GetImpl64()->GetSymbolPositions(symbols, SHT_DYNSYM,
+ mem_offset, file_offset);
+ }
+}
+*/
+
+void ElfReader::VisitSymbols(ElfReader::SymbolSink *sink) {
+ VisitSymbols(sink, -1, -1);
+}
+
+void ElfReader::VisitSymbols(ElfReader::SymbolSink *sink,
+ int symbol_binding,
+ int symbol_type) {
+ VisitSymbols(sink, symbol_binding, symbol_type, false);
+}
+
+void ElfReader::VisitSymbols(ElfReader::SymbolSink *sink,
+ int symbol_binding,
+ int symbol_type,
+ bool get_raw_symbol_values) {
+ if (IsElf32File()) {
+ GetImpl32()->VisitRelocationEntries();
+ GetImpl32()->VisitSymbols(SHT_SYMTAB, sink, symbol_binding, symbol_type,
+ get_raw_symbol_values);
+ GetImpl32()->VisitSymbols(SHT_DYNSYM, sink, symbol_binding, symbol_type,
+ get_raw_symbol_values);
+ } else if (IsElf64File()) {
+ GetImpl64()->VisitRelocationEntries();
+ GetImpl64()->VisitSymbols(SHT_SYMTAB, sink, symbol_binding, symbol_type,
+ get_raw_symbol_values);
+ GetImpl64()->VisitSymbols(SHT_DYNSYM, sink, symbol_binding, symbol_type,
+ get_raw_symbol_values);
+ }
+}
+
+uint64 ElfReader::VaddrOfFirstLoadSegment() {
+ if (IsElf32File()) {
+ return GetImpl32()->VaddrOfFirstLoadSegment();
+ } else if (IsElf64File()) {
+ return GetImpl64()->VaddrOfFirstLoadSegment();
+ } else {
+ return 0;
+ }
+}
+
+const char *ElfReader::GetSectionName(int shndx) {
+ if (shndx < 0 || static_cast<unsigned int>(shndx) >= GetNumSections()) return NULL;
+ if (IsElf32File()) {
+ return GetImpl32()->GetSectionNameByIndex(shndx);
+ } else if (IsElf64File()) {
+ return GetImpl64()->GetSectionNameByIndex(shndx);
+ } else {
+ return NULL;
+ }
+}
+
+uint64 ElfReader::GetNumSections() {
+ if (IsElf32File()) {
+ return GetImpl32()->GetNumSections();
+ } else if (IsElf64File()) {
+ return GetImpl64()->GetNumSections();
+ } else {
+ return 0;
+ }
+}
+
+const char *ElfReader::GetSectionByIndex(int shndx, size_t *size) {
+ if (IsElf32File()) {
+ return GetImpl32()->GetSectionContentsByIndex(shndx, size);
+ } else if (IsElf64File()) {
+ return GetImpl64()->GetSectionContentsByIndex(shndx, size);
+ } else {
+ return NULL;
+ }
+}
+
+const char *ElfReader::GetSectionByName(const string &section_name,
+ size_t *size) {
+ if (IsElf32File()) {
+ return GetImpl32()->GetSectionContentsByName(section_name, size);
+ } else if (IsElf64File()) {
+ return GetImpl64()->GetSectionContentsByName(section_name, size);
+ } else {
+ return NULL;
+ }
+}
+
+const char *ElfReader::GetSectionInfoByName(const string &section_name,
+ SectionInfo *info) {
+ if (IsElf32File()) {
+ return GetImpl32()->GetSectionInfoByName(section_name, info);
+ } else if (IsElf64File()) {
+ return GetImpl64()->GetSectionInfoByName(section_name, info);
+ } else {
+ return NULL;
+ }
+}
+
+bool ElfReader::SectionNamesMatch(const string &name, const string &sh_name) {
+ if ((name.find(".debug_", 0) == 0) && (sh_name.find(".zdebug_", 0) == 0)) {
+ const string name_suffix(name, strlen(".debug_"));
+ const string sh_name_suffix(sh_name, strlen(".zdebug_"));
+ return name_suffix == sh_name_suffix;
+ }
+ return name == sh_name;
+}
+
+bool ElfReader::IsDynamicSharedObject() {
+ if (IsElf32File()) {
+ return GetImpl32()->IsDynamicSharedObject();
+ } else if (IsElf64File()) {
+ return GetImpl64()->IsDynamicSharedObject();
+ } else {
+ return false;
+ }
+}
+
+ElfReaderImpl<Elf32> *ElfReader::GetImpl32() {
+ if (impl32_ == NULL) {
+ impl32_ = new ElfReaderImpl<Elf32>(path_, fd_);
+ }
+ return impl32_;
+}
+
+ElfReaderImpl<Elf64> *ElfReader::GetImpl64() {
+ if (impl64_ == NULL) {
+ impl64_ = new ElfReaderImpl<Elf64>(path_, fd_);
+ }
+ return impl64_;
+}
+
+// Return true if file is an ELF binary of ElfArch, with unstripped
+// debug info (debug_only=true) or symbol table (debug_only=false).
+// Otherwise, return false.
+template <typename ElfArch>
+static bool IsNonStrippedELFBinaryImpl(const string &path, const int fd,
+ bool debug_only) {
+ if (!ElfReaderImpl<ElfArch>::IsArchElfFile(fd, NULL)) return false;
+ ElfReaderImpl<ElfArch> elf_reader(path, fd);
+ return debug_only ?
+ elf_reader.HasDebugSections()
+ : (elf_reader.GetSectionByType(SHT_SYMTAB) != NULL);
+}
+
+// Helper for the IsNon[Debug]StrippedELFBinary functions.
+static bool IsNonStrippedELFBinaryHelper(const string &path,
+ bool debug_only) {
+ const int fd = open(path.c_str(), O_RDONLY);
+ if (fd == -1) {
+ return false;
+ }
+
+ if (IsNonStrippedELFBinaryImpl<Elf32>(path, fd, debug_only) ||
+ IsNonStrippedELFBinaryImpl<Elf64>(path, fd, debug_only)) {
+ close(fd);
+ return true;
+ }
+ close(fd);
+ return false;
+}
+
+bool ElfReader::IsNonStrippedELFBinary(const string &path) {
+ return IsNonStrippedELFBinaryHelper(path, false);
+}
+
+bool ElfReader::IsNonDebugStrippedELFBinary(const string &path) {
+ return IsNonStrippedELFBinaryHelper(path, true);
+}
+} // namespace dwarf2reader
diff --git a/3rdParty/Breakpad/src/common/dwarf/elf_reader.h b/3rdParty/Breakpad/src/common/dwarf/elf_reader.h
new file mode 100644
index 0000000..b1bb67a
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/elf_reader.h
@@ -0,0 +1,166 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+// Author: chatham@google.com (Andrew Chatham)
+// Author: satorux@google.com (Satoru Takabayashi)
+//
+// ElfReader handles reading in ELF. It can extract symbols from the
+// current process, which may be used to symbolize stack traces
+// without having to make a potentially dangerous call to fork().
+//
+// ElfReader dynamically allocates memory, so it is not appropriate to
+// use once the address space might be corrupted, such as during
+// process death.
+//
+// ElfReader supports both 32-bit and 64-bit ELF binaries.
+
+#ifndef COMMON_DWARF_ELF_READER_H__
+#define COMMON_DWARF_ELF_READER_H__
+
+#include <string>
+#include <vector>
+
+#include "common/dwarf/types.h"
+#include "common/using_std_string.h"
+
+using std::vector;
+using std::pair;
+
+namespace dwarf2reader {
+
+class SymbolMap;
+class Elf32;
+class Elf64;
+template<typename ElfArch>
+class ElfReaderImpl;
+
+class ElfReader {
+ public:
+ explicit ElfReader(const string &path);
+ ~ElfReader();
+
+ // Parse the ELF prologue of this file and return whether it was
+ // successfully parsed and matches the word size and byte order of
+ // the current process.
+ bool IsNativeElfFile() const;
+
+ // Similar to IsNativeElfFile but checks if it's a 32-bit ELF file.
+ bool IsElf32File() const;
+
+ // Similar to IsNativeElfFile but checks if it's a 64-bit ELF file.
+ bool IsElf64File() const;
+
+ // Checks if it's an ELF file of type ET_DYN (shared object file).
+ bool IsDynamicSharedObject();
+
+ // Add symbols in the given ELF file into the provided SymbolMap,
+ // assuming that the file has been loaded into the specified
+ // offset.
+ //
+ // The remaining arguments are typically taken from a
+ // ProcMapsIterator (base/sysinfo.h) and describe which portions of
+ // the ELF file are mapped into which parts of memory:
+ //
+ // mem_offset - position at which the segment is mapped into memory
+ // file_offset - offset in the file where the mapping begins
+ // length - length of the mapped segment
+ void AddSymbols(SymbolMap *symbols,
+ uint64 mem_offset, uint64 file_offset,
+ uint64 length);
+
+ class SymbolSink {
+ public:
+ virtual ~SymbolSink() {}
+ virtual void AddSymbol(const char *name, uint64 address, uint64 size) = 0;
+ };
+
+ // Like AddSymbols above, but with no address correction.
+ // Processes any SHT_SYMTAB section, followed by any SHT_DYNSYM section.
+ void VisitSymbols(SymbolSink *sink);
+
+ // Like VisitSymbols above, but for a specific symbol binding/type.
+ // A negative value for the binding and type parameters means any
+ // binding or type.
+ void VisitSymbols(SymbolSink *sink, int symbol_binding, int symbol_type);
+
+ // Like VisitSymbols above but can optionally export raw symbol values instead
+ // of adjusted ones.
+ void VisitSymbols(SymbolSink *sink, int symbol_binding, int symbol_type,
+ bool get_raw_symbol_values);
+
+ // p_vaddr of the first PT_LOAD segment (if any), or 0 if no PT_LOAD
+ // segments are present. This is the address an ELF image was linked
+ // (by static linker) to be loaded at. Usually (but not always) 0 for
+ // shared libraries and position-independent executables.
+ uint64 VaddrOfFirstLoadSegment();
+
+ // Return the name of section "shndx". Returns NULL if the section
+ // is not found.
+ const char *GetSectionName(int shndx);
+
+ // Return the number of sections in the given ELF file.
+ uint64 GetNumSections();
+
+ // Get section "shndx" from the given ELF file. On success, return
+ // the pointer to the section and store the size in "size".
+ // On error, return NULL. The returned section data is only valid
+ // until the ElfReader gets destroyed.
+ const char *GetSectionByIndex(int shndx, size_t *size);
+
+ // Get section with "section_name" (ex. ".text", ".symtab") in the
+ // given ELF file. On success, return the pointer to the section
+ // and store the size in "size". On error, return NULL. The
+ // returned section data is only valid until the ElfReader gets
+ // destroyed.
+ const char *GetSectionByName(const string &section_name, size_t *size);
+
+ // This is like GetSectionByName() but it returns a lot of extra information
+ // about the section. The SectionInfo structure is almost identical to
+ // the typedef struct Elf64_Shdr defined in <elf.h>, but is redefined
+ // here so that the many short macro names in <elf.h> don't have to be
+ // added to our already cluttered namespace.
+ struct SectionInfo {
+ uint32 type; // Section type (SHT_xxx constant from elf.h).
+ uint64 flags; // Section flags (SHF_xxx constants from elf.h).
+ uint64 addr; // Section virtual address at execution.
+ uint64 offset; // Section file offset.
+ uint64 size; // Section size in bytes.
+ uint32 link; // Link to another section.
+ uint32 info; // Additional section information.
+ uint64 addralign; // Section alignment.
+ uint64 entsize; // Entry size if section holds a table.
+ };
+ const char *GetSectionInfoByName(const string &section_name,
+ SectionInfo *info);
+
+ // Check if "path" is an ELF binary that has not been stripped of symbol
+ // tables. This function supports both 32-bit and 64-bit ELF binaries.
+ static bool IsNonStrippedELFBinary(const string &path);
+
+ // Check if "path" is an ELF binary that has not been stripped of debug
+ // info. Unlike IsNonStrippedELFBinary, this function will return
+ // false for binaries passed through "strip -S".
+ static bool IsNonDebugStrippedELFBinary(const string &path);
+
+ // Match a requested section name with the section name as it
+ // appears in the elf-file, adjusting for compressed debug section
+ // names. For example, returns true if name == ".debug_abbrev" and
+ // sh_name == ".zdebug_abbrev"
+ static bool SectionNamesMatch(const string &name, const string &sh_name);
+
+ private:
+ // Lazily initialize impl32_ and return it.
+ ElfReaderImpl<Elf32> *GetImpl32();
+ // Ditto for impl64_.
+ ElfReaderImpl<Elf64> *GetImpl64();
+
+ // Path of the file we're reading.
+ const string path_;
+ // Read-only file descriptor for the file. May be -1 if there was an
+ // error during open.
+ int fd_;
+ ElfReaderImpl<Elf32> *impl32_;
+ ElfReaderImpl<Elf64> *impl64_;
+};
+
+} // namespace dwarf2reader
+
+#endif // COMMON_DWARF_ELF_READER_H__
diff --git a/3rdParty/Breakpad/src/common/dwarf/functioninfo.cc b/3rdParty/Breakpad/src/common/dwarf/functioninfo.cc
new file mode 100644
index 0000000..55a255e
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/functioninfo.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This is a client for the dwarf2reader to extract function and line
+// information from the debug info.
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include <map>
+#include <queue>
+#include <vector>
+
+#include "common/dwarf/functioninfo.h"
+#include "common/dwarf/bytereader.h"
+#include "common/scoped_ptr.h"
+#include "common/using_std_string.h"
+
+using google_breakpad::scoped_ptr;
+
+namespace dwarf2reader {
+
+CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files,
+ std::vector<string>* dirs,
+ LineMap* linemap):linemap_(linemap),
+ files_(files),
+ dirs_(dirs) {
+ // The dirs and files are 1 indexed, so just make sure we put
+ // nothing in the 0 vector.
+ assert(dirs->size() == 0);
+ assert(files->size() == 0);
+ dirs->push_back("");
+ SourceFileInfo s;
+ s.name = "";
+ s.lowpc = ULLONG_MAX;
+ files->push_back(s);
+}
+
+void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) {
+ // These should never come out of order, actually
+ assert(dir_num == dirs_->size());
+ dirs_->push_back(name);
+}
+
+void CULineInfoHandler::DefineFile(const string& name,
+ int32 file_num, uint32 dir_num,
+ uint64 mod_time, uint64 length) {
+ assert(dir_num >= 0);
+ assert(dir_num < dirs_->size());
+
+ // These should never come out of order, actually.
+ if (file_num == (int32)files_->size() || file_num == -1) {
+ string dir = dirs_->at(dir_num);
+
+ SourceFileInfo s;
+ s.lowpc = ULLONG_MAX;
+
+ if (dir == "") {
+ s.name = name;
+ } else {
+ s.name = dir + "/" + name;
+ }
+
+ files_->push_back(s);
+ } else {
+ fprintf(stderr, "error in DefineFile");
+ }
+}
+
+void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
+ uint32 line_num, uint32 column_num) {
+ if (file_num < files_->size()) {
+ linemap_->insert(
+ std::make_pair(address,
+ std::make_pair(files_->at(file_num).name.c_str(),
+ line_num)));
+
+ if (address < files_->at(file_num).lowpc) {
+ files_->at(file_num).lowpc = address;
+ }
+ } else {
+ fprintf(stderr, "error in AddLine");
+ }
+}
+
+bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset,
+ uint8 address_size,
+ uint8 offset_size,
+ uint64 cu_length,
+ uint8 dwarf_version) {
+ current_compilation_unit_offset_ = offset;
+ return true;
+}
+
+
+// For function info, we only care about subprograms and inlined
+// subroutines. For line info, the DW_AT_stmt_list lives in the
+// compile unit tag.
+
+bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag) {
+ switch (tag) {
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine: {
+ current_function_info_ = new FunctionInfo;
+ current_function_info_->lowpc = current_function_info_->highpc = 0;
+ current_function_info_->name = "";
+ current_function_info_->line = 0;
+ current_function_info_->file = "";
+ offset_to_funcinfo_->insert(std::make_pair(offset,
+ current_function_info_));
+ };
+ // FALLTHROUGH
+ case DW_TAG_compile_unit:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+// Only care about the name attribute for functions
+
+void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const string &data) {
+ if (current_function_info_) {
+ if (attr == DW_AT_name)
+ current_function_info_->name = data;
+ else if (attr == DW_AT_MIPS_linkage_name)
+ current_function_info_->mangled_name = data;
+ }
+}
+
+void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data) {
+ if (attr == DW_AT_stmt_list) {
+ SectionMap::const_iterator iter = sections_.find("__debug_line");
+ assert(iter != sections_.end());
+
+ scoped_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
+ iter->second.second - data,
+ reader_, linehandler_));
+ lireader->Start();
+ } else if (current_function_info_) {
+ switch (attr) {
+ case DW_AT_low_pc:
+ current_function_info_->lowpc = data;
+ break;
+ case DW_AT_high_pc:
+ current_function_info_->highpc = data;
+ break;
+ case DW_AT_decl_line:
+ current_function_info_->line = data;
+ break;
+ case DW_AT_decl_file:
+ current_function_info_->file = files_->at(data).name;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data) {
+ if (current_function_info_) {
+ switch (attr) {
+ case DW_AT_specification: {
+ // Some functions have a "specification" attribute
+ // which means they were defined elsewhere. The name
+ // attribute is not repeated, and must be taken from
+ // the specification DIE. Here we'll assume that
+ // any DIE referenced in this manner will already have
+ // been seen, but that's not really required by the spec.
+ FunctionMap::iterator iter = offset_to_funcinfo_->find(data);
+ if (iter != offset_to_funcinfo_->end()) {
+ current_function_info_->name = iter->second->name;
+ current_function_info_->mangled_name = iter->second->mangled_name;
+ } else {
+ // If you hit this, this code probably needs to be rewritten.
+ fprintf(stderr,
+ "Error: DW_AT_specification was seen before the referenced "
+ "DIE! (Looking for DIE at offset %08llx, in DIE at "
+ "offset %08llx)\n", data, offset);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void CUFunctionInfoHandler::EndDIE(uint64 offset) {
+ if (current_function_info_ && current_function_info_->lowpc)
+ address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc,
+ current_function_info_));
+}
+
+} // namespace dwarf2reader
diff --git a/3rdParty/Breakpad/src/common/dwarf/functioninfo.h b/3rdParty/Breakpad/src/common/dwarf/functioninfo.h
new file mode 100644
index 0000000..0b08a5f
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf/functioninfo.h
@@ -0,0 +1,188 @@
+// Copyright (c) 2010 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// This file contains the definitions for a DWARF2/3 information
+// collector that uses the DWARF2/3 reader interface to build a mapping
+// of addresses to files, lines, and functions.
+
+#ifndef COMMON_DWARF_FUNCTIONINFO_H__
+#define COMMON_DWARF_FUNCTIONINFO_H__
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "common/dwarf/dwarf2reader.h"
+#include "common/using_std_string.h"
+
+
+namespace dwarf2reader {
+
+struct FunctionInfo {
+ // Name of the function
+ string name;
+ // Mangled name of the function
+ string mangled_name;
+ // File containing this function
+ string file;
+ // Line number for start of function.
+ uint32 line;
+ // Beginning address for this function
+ uint64 lowpc;
+ // End address for this function.
+ uint64 highpc;
+};
+
+struct SourceFileInfo {
+ // Name of the source file name
+ string name;
+ // Low address of source file name
+ uint64 lowpc;
+};
+
+typedef std::map<uint64, FunctionInfo*> FunctionMap;
+typedef std::map<uint64, std::pair<string, uint32> > LineMap;
+
+// This class is a basic line info handler that fills in the dirs,
+// file, and linemap passed into it with the data produced from the
+// LineInfoHandler.
+class CULineInfoHandler: public LineInfoHandler {
+ public:
+
+ //
+ CULineInfoHandler(std::vector<SourceFileInfo>* files,
+ std::vector<string>* dirs,
+ LineMap* linemap);
+ virtual ~CULineInfoHandler() { }
+
+ // Called when we define a directory. We just place NAME into dirs_
+ // at position DIR_NUM.
+ virtual void DefineDir(const string& name, uint32 dir_num);
+
+ // Called when we define a filename. We just place
+ // concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM.
+ virtual void DefineFile(const string& name, int32 file_num,
+ uint32 dir_num, uint64 mod_time, uint64 length);
+
+
+ // Called when the line info reader has a new line, address pair
+ // ready for us. ADDRESS is the address of the code, LENGTH is the
+ // length of its machine code in bytes, FILE_NUM is the file number
+ // containing the code, LINE_NUM is the line number in that file for
+ // the code, and COLUMN_NUM is the column number the code starts at,
+ // if we know it (0 otherwise).
+ virtual void AddLine(uint64 address, uint64 length,
+ uint32 file_num, uint32 line_num, uint32 column_num);
+
+ private:
+ LineMap* linemap_;
+ std::vector<SourceFileInfo>* files_;
+ std::vector<string>* dirs_;
+};
+
+class CUFunctionInfoHandler: public Dwarf2Handler {
+ public:
+ CUFunctionInfoHandler(std::vector<SourceFileInfo>* files,
+ std::vector<string>* dirs,
+ LineMap* linemap,
+ FunctionMap* offset_to_funcinfo,
+ FunctionMap* address_to_funcinfo,
+ CULineInfoHandler* linehandler,
+ const SectionMap& sections,
+ ByteReader* reader)
+ : files_(files), dirs_(dirs), linemap_(linemap),
+ offset_to_funcinfo_(offset_to_funcinfo),
+ address_to_funcinfo_(address_to_funcinfo),
+ linehandler_(linehandler), sections_(sections),
+ reader_(reader), current_function_info_(NULL) { }
+
+ virtual ~CUFunctionInfoHandler() { }
+
+ // Start to process a compilation unit at OFFSET from the beginning of the
+ // .debug_info section. We want to see all compilation units, so we
+ // always return true.
+
+ virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
+ uint8 offset_size, uint64 cu_length,
+ uint8 dwarf_version);
+
+ // Start to process a DIE at OFFSET from the beginning of the
+ // .debug_info section. We only care about function related DIE's.
+ virtual bool StartDIE(uint64 offset, enum DwarfTag tag);
+
+ // Called when we have an attribute with unsigned data to give to
+ // our handler. The attribute is for the DIE at OFFSET from the
+ // beginning of the .debug_info section, has a name of ATTR, a form of
+ // FORM, and the actual data of the attribute is in DATA.
+ virtual void ProcessAttributeUnsigned(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data);
+
+ // Called when we have an attribute with a DIE reference to give to
+ // our handler. The attribute is for the DIE at OFFSET from the
+ // beginning of the .debug_info section, has a name of ATTR, a form of
+ // FORM, and the offset of the referenced DIE from the start of the
+ // .debug_info section is in DATA.
+ virtual void ProcessAttributeReference(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ uint64 data);
+
+ // Called when we have an attribute with string data to give to
+ // our handler. The attribute is for the DIE at OFFSET from the
+ // beginning of the .debug_info section, has a name of ATTR, a form of
+ // FORM, and the actual data of the attribute is in DATA.
+ virtual void ProcessAttributeString(uint64 offset,
+ enum DwarfAttribute attr,
+ enum DwarfForm form,
+ const string& data);
+
+ // Called when finished processing the DIE at OFFSET.
+ // Because DWARF2/3 specifies a tree of DIEs, you may get starts
+ // before ends of the previous DIE, as we process children before
+ // ending the parent.
+ virtual void EndDIE(uint64 offset);
+
+ private:
+ std::vector<SourceFileInfo>* files_;
+ std::vector<string>* dirs_;
+ LineMap* linemap_;
+ FunctionMap* offset_to_funcinfo_;
+ FunctionMap* address_to_funcinfo_;
+ CULineInfoHandler* linehandler_;
+ const SectionMap& sections_;
+ ByteReader* reader_;
+ FunctionInfo* current_function_info_;
+ uint64 current_compilation_unit_offset_;
+};
+
+} // namespace dwarf2reader
+#endif // COMMON_DWARF_FUNCTIONINFO_H__
diff --git a/3rdParty/Breakpad/src/common/dwarf/types.h b/3rdParty/Breakpad/src/common/dwarf/types.h
index 61ca457..59dda31 100644
--- a/3rdParty/Breakpad/src/common/dwarf/types.h
+++ b/3rdParty/Breakpad/src/common/dwarf/types.h
@@ -45,11 +45,7 @@ typedef unsigned short uint16;
typedef unsigned int uint32;
typedef unsigned long long uint64;
-#ifdef __PTRDIFF_TYPE__
-typedef __PTRDIFF_TYPE__ intptr;
-typedef unsigned __PTRDIFF_TYPE__ uintptr;
-#else
-#error "Can't find pointer-sized integral types."
-#endif
+typedef intptr_t intptr;
+typedef uintptr_t uintptr;
#endif // _COMMON_DWARF_TYPES_H__
diff --git a/3rdParty/Breakpad/src/common/dwarf_cfi_to_module.cc b/3rdParty/Breakpad/src/common/dwarf_cfi_to_module.cc
index 15904d7..bd298a2 100644
--- a/3rdParty/Breakpad/src/common/dwarf_cfi_to_module.cc
+++ b/3rdParty/Breakpad/src/common/dwarf_cfi_to_module.cc
@@ -105,6 +105,43 @@ vector<string> DwarfCFIToModule::RegisterNames::ARM() {
return MakeVector(names, sizeof(names) / sizeof(names[0]));
}
+// Per ARM IHI 0057A, section 3.1
+vector<string> DwarfCFIToModule::RegisterNames::ARM64() {
+ static const char *const names[] = {
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+ "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+ "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+ "x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "",
+ "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
+ "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
+ "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
+ "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"
+ };
+
+ return MakeVector(names, sizeof(names) / sizeof(names[0]));
+}
+
+vector<string> DwarfCFIToModule::RegisterNames::MIPS() {
+ static const char* const kRegisterNames[] = {
+ "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
+ "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
+ "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
+ "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
+ "$lo", "$hi", "$pc", "$f0", "$f2", "$f3", "$f4", "$f5",
+ "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", "$f12", "$f13",
+ "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20",
+ "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27",
+ "$f28", "$f29", "$f30", "$f31", "$fcsr", "$fir"
+ };
+
+ return MakeVector(kRegisterNames,
+ sizeof(kRegisterNames) / sizeof(kRegisterNames[0]));
+}
+
bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
uint8 version, const string &augmentation,
unsigned return_address) {
@@ -158,7 +195,7 @@ void DwarfCFIToModule::Record(Module::Address address, int reg,
// Place the name in our global set of strings, and then use the string
// from the set. Even though the assignment looks like a copy, all the
- // major std::string implementations use reference counting internally,
+ // major string implementations use reference counting internally,
// so the effect is to have all our data structures share copies of rules
// whenever possible. Since register names are drawn from a
// vector<string>, register names are already shared.
diff --git a/3rdParty/Breakpad/src/common/dwarf_cfi_to_module.h b/3rdParty/Breakpad/src/common/dwarf_cfi_to_module.h
index 7db552a..a5302e1 100644
--- a/3rdParty/Breakpad/src/common/dwarf_cfi_to_module.h
+++ b/3rdParty/Breakpad/src/common/dwarf_cfi_to_module.h
@@ -109,6 +109,12 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
// ARM.
static vector<string> ARM();
+ // ARM64, aka AARCH64.
+ static vector<string> ARM64();
+
+ // MIPS.
+ static vector<string> MIPS();
+
private:
// Given STRINGS, an array of C strings with SIZE elements, return an
// equivalent vector<string>.
@@ -175,15 +181,15 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
// The names of the return address and canonical frame address. Putting
// these here instead of using string literals allows us to share their
- // texts in reference-counted std::string implementations (all the
+ // texts in reference-counted string implementations (all the
// popular ones). Many, many rules cite these strings.
string cfa_name_, ra_name_;
// A set of strings used by this CFI. Before storing a string in one of
// our data structures, insert it into this set, and then use the string
// from the set.
- //
- // Because std::string uses reference counting internally, simply using
+ //
+ // Because string uses reference counting internally, simply using
// strings from this set, even if passed by value, assigned, or held
// directly in structures and containers (map<string, ...>, for example),
// causes those strings to share a single instance of each distinct piece
diff --git a/3rdParty/Breakpad/src/common/dwarf_cfi_to_module_unittest.cc b/3rdParty/Breakpad/src/common/dwarf_cfi_to_module_unittest.cc
new file mode 100644
index 0000000..807d1b2
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf_cfi_to_module_unittest.cc
@@ -0,0 +1,306 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule.
+
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/dwarf_cfi_to_module.h"
+#include "common/using_std_string.h"
+
+using std::vector;
+
+using google_breakpad::Module;
+using google_breakpad::DwarfCFIToModule;
+using testing::ContainerEq;
+using testing::Test;
+using testing::_;
+
+struct MockCFIReporter: public DwarfCFIToModule::Reporter {
+ MockCFIReporter(const string &file, const string &section)
+ : Reporter(file, section) { }
+ MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg));
+ MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string &reg));
+ MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string &reg));
+};
+
+struct DwarfCFIToModuleFixture {
+ DwarfCFIToModuleFixture()
+ : module("module name", "module os", "module arch", "module id"),
+ reporter("reporter file", "reporter section"),
+ handler(&module, register_names, &reporter) {
+ register_names.push_back("reg0");
+ register_names.push_back("reg1");
+ register_names.push_back("reg2");
+ register_names.push_back("reg3");
+ register_names.push_back("reg4");
+ register_names.push_back("reg5");
+ register_names.push_back("reg6");
+ register_names.push_back("reg7");
+ register_names.push_back("sp");
+ register_names.push_back("pc");
+ register_names.push_back("");
+
+ EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0);
+ EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0);
+ EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0);
+ }
+
+ Module module;
+ vector<string> register_names;
+ MockCFIReporter reporter;
+ DwarfCFIToModule handler;
+ vector<Module::StackFrameEntry *> entries;
+};
+
+class Entry: public DwarfCFIToModuleFixture, public Test { };
+
+TEST_F(Entry, Accept) {
+ ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL,
+ 0xb440ce248169c8d6ULL, 3, "", 0xea93c106));
+ ASSERT_TRUE(handler.End());
+ module.GetStackFrameEntries(&entries);
+ EXPECT_EQ(1U, entries.size());
+ EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address);
+ EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size);
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Entry, AcceptOldVersion) {
+ ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL,
+ 0xc771f44958d40bbcULL, 1, "", 0x093c945e));
+ ASSERT_TRUE(handler.End());
+ module.GetStackFrameEntries(&entries);
+ EXPECT_EQ(1U, entries.size());
+ EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address);
+ EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size);
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+struct RuleFixture: public DwarfCFIToModuleFixture {
+ RuleFixture() : DwarfCFIToModuleFixture() {
+ entry_address = 0x89327ebf86b47492ULL;
+ entry_size = 0x2f8cd573072fe02aULL;
+ return_reg = 0x7886a346;
+ }
+ void StartEntry() {
+ ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size,
+ 3, "", return_reg));
+ }
+ void CheckEntry() {
+ module.GetStackFrameEntries(&entries);
+ EXPECT_EQ(1U, entries.size());
+ EXPECT_EQ(entry_address, entries[0]->address);
+ EXPECT_EQ(entry_size, entries[0]->size);
+ }
+ uint64 entry_address, entry_size;
+ unsigned return_reg;
+};
+
+class Rule: public RuleFixture, public Test { };
+
+TEST_F(Rule, UndefinedRule) {
+ EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7"));
+ StartEntry();
+ ASSERT_TRUE(handler.UndefinedRule(entry_address, 7));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, RegisterWithEmptyName) {
+ EXPECT_CALL(reporter, UnnamedRegister(_, 10));
+ EXPECT_CALL(reporter, UndefinedNotSupported(_, "unnamed_register10"));
+ StartEntry();
+ ASSERT_TRUE(handler.UndefinedRule(entry_address, 10));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, SameValueRule) {
+ StartEntry();
+ ASSERT_TRUE(handler.SameValueRule(entry_address, 6));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial["reg6"] = "reg6";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, OffsetRule) {
+ StartEntry();
+ ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg,
+ DwarfCFIToModule::kCFARegister,
+ 16927065));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ Module::RuleChangeMap expected_changes;
+ expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^";
+ EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
+}
+
+TEST_F(Rule, OffsetRuleNegative) {
+ StartEntry();
+ ASSERT_TRUE(handler.OffsetRule(entry_address + 1,
+ DwarfCFIToModule::kCFARegister, 4, -34530721));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ Module::RuleChangeMap expected_changes;
+ expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^";
+ EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
+}
+
+TEST_F(Rule, ValOffsetRule) {
+ // Use an unnamed register number, to exercise that branch of RegisterName.
+ EXPECT_CALL(reporter, UnnamedRegister(_, 11));
+ StartEntry();
+ ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7,
+ DwarfCFIToModule::kCFARegister,
+ 11, 61812979));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ Module::RuleChangeMap expected_changes;
+ expected_changes[entry_address + 0x5ab7][".cfa"] =
+ "unnamed_register11 61812979 +";
+ EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
+}
+
+TEST_F(Rule, RegisterRule) {
+ StartEntry();
+ ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial[".ra"] = "reg3";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, ExpressionRule) {
+ EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2"));
+ StartEntry();
+ ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2,
+ "it takes two to tango"));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, ValExpressionRule) {
+ EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0"));
+ StartEntry();
+ ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0,
+ "bit off more than he could chew"));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ EXPECT_EQ(0U, entries[0]->initial_rules.size());
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, DefaultReturnAddressRule) {
+ return_reg = 2;
+ StartEntry();
+ ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial[".ra"] = "reg2";
+ expected_initial["reg0"] = "reg1";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, DefaultReturnAddressRuleOverride) {
+ return_reg = 2;
+ StartEntry();
+ ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial[".ra"] = "reg1";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ EXPECT_EQ(0U, entries[0]->rule_changes.size());
+}
+
+TEST_F(Rule, DefaultReturnAddressRuleLater) {
+ return_reg = 2;
+ StartEntry();
+ ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1));
+ ASSERT_TRUE(handler.End());
+ CheckEntry();
+ Module::RuleMap expected_initial;
+ expected_initial[".ra"] = "reg2";
+ EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
+ Module::RuleChangeMap expected_changes;
+ expected_changes[entry_address + 1][".ra"] = "reg1";
+ EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
+}
+
+TEST(RegisterNames, I386) {
+ vector<string> names = DwarfCFIToModule::RegisterNames::I386();
+
+ EXPECT_EQ("$eax", names[0]);
+ EXPECT_EQ("$ecx", names[1]);
+ EXPECT_EQ("$esp", names[4]);
+ EXPECT_EQ("$eip", names[8]);
+}
+
+TEST(RegisterNames, ARM) {
+ vector<string> names = DwarfCFIToModule::RegisterNames::ARM();
+
+ EXPECT_EQ("r0", names[0]);
+ EXPECT_EQ("r10", names[10]);
+ EXPECT_EQ("sp", names[13]);
+ EXPECT_EQ("lr", names[14]);
+ EXPECT_EQ("pc", names[15]);
+}
+
+TEST(RegisterNames, X86_64) {
+ vector<string> names = DwarfCFIToModule::RegisterNames::X86_64();
+
+ EXPECT_EQ("$rax", names[0]);
+ EXPECT_EQ("$rdx", names[1]);
+ EXPECT_EQ("$rbp", names[6]);
+ EXPECT_EQ("$rsp", names[7]);
+ EXPECT_EQ("$rip", names[16]);
+}
diff --git a/3rdParty/Breakpad/src/common/dwarf_cu_to_module.cc b/3rdParty/Breakpad/src/common/dwarf_cu_to_module.cc
index ded5f83..a16bee7 100644
--- a/3rdParty/Breakpad/src/common/dwarf_cu_to_module.cc
+++ b/3rdParty/Breakpad/src/common/dwarf_cu_to_module.cc
@@ -40,23 +40,24 @@
#include <assert.h>
#include <inttypes.h>
+#include <stdint.h>
#include <stdio.h>
#include <algorithm>
-#include <set>
#include <utility>
#include "common/dwarf_line_to_module.h"
+#include "common/unordered.h"
namespace google_breakpad {
using std::map;
using std::pair;
-using std::set;
+using std::sort;
using std::vector;
// Data provided by a DWARF specification DIE.
-//
+//
// In DWARF, the DIE for a definition may contain a DW_AT_specification
// attribute giving the offset of the corresponding declaration DIE, and
// the definition DIE may omit information given in the declaration. For
@@ -73,6 +74,9 @@ using std::vector;
// A Specification holds information gathered from a declaration DIE that
// we may need if we find a DW_AT_specification link pointing to it.
struct DwarfCUToModule::Specification {
+ // The qualified name that can be found by demangling DW_AT_MIPS_linkage_name.
+ string qualified_name;
+
// The name of the enclosing scope, or the empty string if there is none.
string enclosing_name;
@@ -84,7 +88,7 @@ struct DwarfCUToModule::Specification {
// An abstract origin -- base definition of an inline function.
struct AbstractOrigin {
AbstractOrigin() : name() {}
- AbstractOrigin(const string& name) : name(name) {}
+ explicit AbstractOrigin(const string& name) : name(name) {}
string name;
};
@@ -97,13 +101,21 @@ struct DwarfCUToModule::FilePrivate {
// A set of strings used in this CU. Before storing a string in one of
// our data structures, insert it into this set, and then use the string
// from the set.
- //
- // Because std::string uses reference counting internally, simply using
- // strings from this set, even if passed by value, assigned, or held
- // directly in structures and containers (map<string, ...>, for example),
- // causes those strings to share a single instance of each distinct piece
- // of text.
- set<string> common_strings;
+ //
+ // In some STL implementations, strings are reference-counted internally,
+ // meaning that simply using strings from this set, even if passed by
+ // value, assigned, or held directly in structures and containers
+ // (map<string, ...>, for example), causes those strings to share a
+ // single instance of each distinct piece of text. GNU's libstdc++ uses
+ // reference counts, and I believe MSVC did as well, at some point.
+ // However, C++ '11 implementations are moving away from reference
+ // counting.
+ //
+ // In other implementations, string assignments copy the string's text,
+ // so this set will actually hold yet another copy of the string (although
+ // everything will still work). To improve memory consumption portably,
+ // we will probably need to use pointers to strings held in this set.
+ unordered_set<string> common_strings;
// A map from offsets of DIEs within the .debug_info section to
// Specifications describing those DIEs. Specification references can
@@ -113,14 +125,42 @@ struct DwarfCUToModule::FilePrivate {
AbstractOriginByOffset origins;
};
-DwarfCUToModule::FileContext::FileContext(const string &filename_arg,
- Module *module_arg)
- : filename(filename_arg), module(module_arg) {
- file_private = new FilePrivate();
+DwarfCUToModule::FileContext::FileContext(const string &filename,
+ Module *module,
+ bool handle_inter_cu_refs)
+ : filename_(filename),
+ module_(module),
+ handle_inter_cu_refs_(handle_inter_cu_refs),
+ file_private_(new FilePrivate()) {
}
DwarfCUToModule::FileContext::~FileContext() {
- delete file_private;
+}
+
+void DwarfCUToModule::FileContext::AddSectionToSectionMap(
+ const string& name, const uint8_t *contents, uint64 length) {
+ section_map_[name] = std::make_pair(contents, length);
+}
+
+void DwarfCUToModule::FileContext::ClearSectionMapForTest() {
+ section_map_.clear();
+}
+
+const dwarf2reader::SectionMap&
+DwarfCUToModule::FileContext::section_map() const {
+ return section_map_;
+}
+
+void DwarfCUToModule::FileContext::ClearSpecifications() {
+ if (!handle_inter_cu_refs_)
+ file_private_->specifications.clear();
+}
+
+bool DwarfCUToModule::FileContext::IsUnhandledInterCUReference(
+ uint64 offset, uint64 compilation_unit_start) const {
+ if (handle_inter_cu_refs_)
+ return false;
+ return offset < compilation_unit_start;
}
// Information global to the particular compilation unit we're
@@ -130,11 +170,13 @@ struct DwarfCUToModule::CUContext {
CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg)
: file_context(file_context_arg),
reporter(reporter_arg),
- language(Language::CPlusPlus) { }
+ language(Language::CPlusPlus) {}
+
~CUContext() {
for (vector<Module::Function *>::iterator it = functions.begin();
- it != functions.end(); it++)
+ it != functions.end(); ++it) {
delete *it;
+ }
};
// The DWARF-bearing file into which this CU was incorporated.
@@ -218,6 +260,14 @@ class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
DIEContext *parent_context_;
uint64 offset_;
+ // Place the name in the global set of strings. Even though this looks
+ // like a copy, all the major string implementations use reference
+ // counting internally, so the effect is to have all the data structures
+ // share copies of strings whenever possible.
+ // FIXME: Should this return something like a string_ref to avoid the
+ // assumption about how strings are implemented?
+ string AddStringToPool(const string &str);
+
// If this DIE has a DW_AT_declaration attribute, this is its value.
// It is false on DIEs with no DW_AT_declaration attribute.
bool declaration_;
@@ -230,6 +280,11 @@ class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
// The value of the DW_AT_name attribute, or the empty string if the
// DIE has no such attribute.
string name_attribute_;
+
+ // The demangled value of the DW_AT_MIPS_linkage_name attribute, or the empty
+ // string if the DIE has no such attribute or its content could not be
+ // demangled.
+ string demangled_name_;
};
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned(
@@ -248,14 +303,19 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
uint64 data) {
switch (attr) {
case dwarf2reader::DW_AT_specification: {
+ FileContext *file_context = cu_context_->file_context;
+ if (file_context->IsUnhandledInterCUReference(
+ data, cu_context_->reporter->cu_offset())) {
+ cu_context_->reporter->UnhandledInterCUReference(offset_, data);
+ break;
+ }
// Find the Specification to which this attribute refers, and
// set specification_ appropriately. We could do more processing
// here, but it's better to leave the real work to our
// EndAttribute member function, at which point we know we have
// seen all the DIE's attributes.
- FileContext *file_context = cu_context_->file_context;
- SpecificationByOffset *specifications
- = &file_context->file_private->specifications;
+ SpecificationByOffset *specifications =
+ &file_context->file_private_->specifications;
SpecificationByOffset::iterator spec = specifications->find(data);
if (spec != specifications->end()) {
specification_ = &spec->second;
@@ -273,20 +333,37 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
}
}
+string DwarfCUToModule::GenericDIEHandler::AddStringToPool(const string &str) {
+ pair<unordered_set<string>::iterator, bool> result =
+ cu_context_->file_context->file_private_->common_strings.insert(str);
+ return *result.first;
+}
+
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data) {
switch (attr) {
- case dwarf2reader::DW_AT_name: {
- // Place the name in our global set of strings, and then use the
- // string from the set. Even though the assignment looks like a copy,
- // all the major std::string implementations use reference counting
- // internally, so the effect is to have all our data structures share
- // copies of strings whenever possible.
- pair<set<string>::iterator, bool> result =
- cu_context_->file_context->file_private->common_strings.insert(data);
- name_attribute_ = *result.first;
+ case dwarf2reader::DW_AT_name:
+ name_attribute_ = AddStringToPool(data);
+ break;
+ case dwarf2reader::DW_AT_MIPS_linkage_name:
+ case dwarf2reader::DW_AT_linkage_name: {
+ string demangled;
+ Language::DemangleResult result =
+ cu_context_->language->DemangleName(data, &demangled);
+ switch (result) {
+ case Language::kDemangleSuccess:
+ demangled_name_ = AddStringToPool(demangled);
+ break;
+
+ case Language::kDemangleFailure:
+ cu_context_->reporter->DemangleError(data);
+ // fallthrough
+ case Language::kDontDemangle:
+ demangled_name_.clear();
+ break;
+ }
break;
}
default: break;
@@ -294,37 +371,64 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
}
string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
- // Find our unqualified name. If the DIE has its own DW_AT_name
- // attribute, then use that; otherwise, check our specification.
- const string *unqualified_name;
- if (name_attribute_.empty() && specification_)
- unqualified_name = &specification_->unqualified_name;
- else
- unqualified_name = &name_attribute_;
-
- // Find the name of our enclosing context. If we have a
- // specification, it's the specification's enclosing context that
- // counts; otherwise, use this DIE's context.
+ // Use the demangled name, if one is available. Demangled names are
+ // preferable to those inferred from the DWARF structure because they
+ // include argument types.
+ const string *qualified_name = NULL;
+ if (!demangled_name_.empty()) {
+ // Found it is this DIE.
+ qualified_name = &demangled_name_;
+ } else if (specification_ && !specification_->qualified_name.empty()) {
+ // Found it on the specification.
+ qualified_name = &specification_->qualified_name;
+ }
+
+ const string *unqualified_name = NULL;
const string *enclosing_name;
- if (specification_)
- enclosing_name = &specification_->enclosing_name;
- else
- enclosing_name = &parent_context_->name;
+ if (!qualified_name) {
+ // Find the unqualified name. If the DIE has its own DW_AT_name
+ // attribute, then use that; otherwise, check the specification.
+ if (!name_attribute_.empty())
+ unqualified_name = &name_attribute_;
+ else if (specification_)
+ unqualified_name = &specification_->unqualified_name;
+
+ // Find the name of the enclosing context. If this DIE has a
+ // specification, it's the specification's enclosing context that
+ // counts; otherwise, use this DIE's context.
+ if (specification_)
+ enclosing_name = &specification_->enclosing_name;
+ else
+ enclosing_name = &parent_context_->name;
+ }
+
+ // Prepare the return value before upcoming mutations possibly invalidate the
+ // existing pointers.
+ string return_value;
+ if (qualified_name) {
+ return_value = *qualified_name;
+ } else if (unqualified_name && enclosing_name) {
+ // Combine the enclosing name and unqualified name to produce our
+ // own fully-qualified name.
+ return_value = cu_context_->language->MakeQualifiedName(*enclosing_name,
+ *unqualified_name);
+ }
// If this DIE was marked as a declaration, record its names in the
// specification table.
- if (declaration_) {
- FileContext *file_context = cu_context_->file_context;
+ if ((declaration_ && qualified_name) ||
+ (unqualified_name && enclosing_name)) {
Specification spec;
- spec.enclosing_name = *enclosing_name;
- spec.unqualified_name = *unqualified_name;
- file_context->file_private->specifications[offset_] = spec;
+ if (qualified_name) {
+ spec.qualified_name = *qualified_name;
+ } else {
+ spec.enclosing_name = *enclosing_name;
+ spec.unqualified_name = *unqualified_name;
+ }
+ cu_context_->file_context->file_private_->specifications[offset_] = spec;
}
- // Combine the enclosing name and unqualified name to produce our
- // own fully-qualified name.
- return cu_context_->language->MakeQualifiedName(*enclosing_name,
- *unqualified_name);
+ return return_value;
}
// A handler class for DW_TAG_subprogram DIEs.
@@ -333,7 +437,8 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
FuncHandler(CUContext *cu_context, DIEContext *parent_context,
uint64 offset)
: GenericDIEHandler(cu_context, parent_context, offset),
- low_pc_(0), high_pc_(0), abstract_origin_(NULL), inline_(false) { }
+ low_pc_(0), high_pc_(0), high_pc_form_(dwarf2reader::DW_FORM_addr),
+ abstract_origin_(NULL), inline_(false) { }
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
@@ -352,6 +457,7 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
// specification_, parent_context_. Computed in EndAttributes.
string name_;
uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc
+ DwarfForm high_pc_form_; // DW_AT_high_pc can be length or address.
const AbstractOrigin* abstract_origin_;
bool inline_;
};
@@ -367,7 +473,11 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned(
case dwarf2reader::DW_AT_inline: inline_ = true; break;
case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break;
- case dwarf2reader::DW_AT_high_pc: high_pc_ = data; break;
+ case dwarf2reader::DW_AT_high_pc:
+ high_pc_form_ = form;
+ high_pc_ = data;
+ break;
+
default:
GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data);
break;
@@ -393,10 +503,10 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeReference(
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
- switch(attr) {
+ switch (attr) {
case dwarf2reader::DW_AT_abstract_origin: {
const AbstractOriginByOffset& origins =
- cu_context_->file_context->file_private->origins;
+ cu_context_->file_context->file_private_->origins;
AbstractOriginByOffset::const_iterator origin = origins.find(data);
if (origin != origins.end()) {
abstract_origin_ = &(origin->second);
@@ -421,33 +531,39 @@ bool DwarfCUToModule::FuncHandler::EndAttributes() {
}
void DwarfCUToModule::FuncHandler::Finish() {
+ // Make high_pc_ an address, if it isn't already.
+ if (high_pc_form_ != dwarf2reader::DW_FORM_addr) {
+ high_pc_ += low_pc_;
+ }
+
// Did we collect the information we need? Not all DWARF function
// entries have low and high addresses (for example, inlined
// functions that were never used), but all the ones we're
// interested in cover a non-empty range of bytes.
if (low_pc_ < high_pc_) {
- // Create a Module::Function based on the data we've gathered, and
- // add it to the functions_ list.
- Module::Function *func = new Module::Function;
// Malformed DWARF may omit the name, but all Module::Functions must
// have names.
+ string name;
if (!name_.empty()) {
- func->name = name_;
+ name = name_;
} else {
cu_context_->reporter->UnnamedFunction(offset_);
- func->name = "<name omitted>";
+ name = "<name omitted>";
}
- func->address = low_pc_;
+
+ // Create a Module::Function based on the data we've gathered, and
+ // add it to the functions_ list.
+ scoped_ptr<Module::Function> func(new Module::Function(name, low_pc_));
func->size = high_pc_ - low_pc_;
func->parameter_size = 0;
if (func->address) {
// If the function address is zero this is a sign that this function
// description is just empty debug data and should just be discarded.
- cu_context_->functions.push_back(func);
+ cu_context_->functions.push_back(func.release());
}
} else if (inline_) {
AbstractOrigin origin(name_);
- cu_context_->file_context->file_private->origins[offset_] = origin;
+ cu_context_->file_context->file_private_->origins[offset_] = origin;
}
}
@@ -459,8 +575,7 @@ class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler {
uint64 offset)
: GenericDIEHandler(cu_context, parent_context, offset) { }
bool EndAttributes();
- DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
- const AttributeList &attrs);
+ DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag);
private:
DIEContext child_context_; // A context for our children.
@@ -473,8 +588,7 @@ bool DwarfCUToModule::NamedScopeHandler::EndAttributes() {
dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler(
uint64 offset,
- enum DwarfTag tag,
- const AttributeList &attrs) {
+ enum DwarfTag tag) {
switch (tag) {
case dwarf2reader::DW_TAG_subprogram:
return new FuncHandler(cu_context_, &child_context_, offset);
@@ -561,17 +675,31 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
filename_.c_str(), offset);
}
+void DwarfCUToModule::WarningReporter::DemangleError(const string &input) {
+ CUHeading();
+ fprintf(stderr, "%s: warning: failed to demangle %s\n",
+ filename_.c_str(), input.c_str());
+}
+
+void DwarfCUToModule::WarningReporter::UnhandledInterCUReference(
+ uint64 offset, uint64 target) {
+ CUHeading();
+ fprintf(stderr, "%s: warning: the DIE at offset 0x%llx has a "
+ "DW_FORM_ref_addr attribute with an inter-CU reference to "
+ "0x%llx, but inter-CU reference handling is turned off.\n",
+ filename_.c_str(), offset, target);
+}
+
DwarfCUToModule::DwarfCUToModule(FileContext *file_context,
- LineToModuleFunctor *line_reader,
+ LineToModuleHandler *line_reader,
WarningReporter *reporter)
- : line_reader_(line_reader), has_source_line_info_(false) {
- cu_context_ = new CUContext(file_context, reporter);
- child_context_ = new DIEContext();
+ : line_reader_(line_reader),
+ cu_context_(new CUContext(file_context, reporter)),
+ child_context_(new DIEContext()),
+ has_source_line_info_(false) {
}
DwarfCUToModule::~DwarfCUToModule() {
- delete cu_context_;
- delete child_context_;
}
void DwarfCUToModule::ProcessAttributeSigned(enum DwarfAttribute attr,
@@ -605,8 +733,16 @@ void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr,
void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string &data) {
- if (attr == dwarf2reader::DW_AT_name)
- cu_context_->reporter->SetCUName(data);
+ switch (attr) {
+ case dwarf2reader::DW_AT_name:
+ cu_context_->reporter->SetCUName(data);
+ break;
+ case dwarf2reader::DW_AT_comp_dir:
+ line_reader_->StartCompilationUnit(data);
+ break;
+ default:
+ break;
+ }
}
bool DwarfCUToModule::EndAttributes() {
@@ -615,16 +751,17 @@ bool DwarfCUToModule::EndAttributes() {
dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
uint64 offset,
- enum DwarfTag tag,
- const AttributeList &attrs) {
+ enum DwarfTag tag) {
switch (tag) {
case dwarf2reader::DW_TAG_subprogram:
- return new FuncHandler(cu_context_, child_context_, offset);
+ return new FuncHandler(cu_context_.get(), child_context_.get(), offset);
case dwarf2reader::DW_TAG_namespace:
case dwarf2reader::DW_TAG_class_type:
case dwarf2reader::DW_TAG_structure_type:
case dwarf2reader::DW_TAG_union_type:
- return new NamedScopeHandler(cu_context_, child_context_, offset);
+ case dwarf2reader::DW_TAG_module:
+ return new NamedScopeHandler(cu_context_.get(), child_context_.get(),
+ offset);
default:
return NULL;
}
@@ -636,6 +773,14 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
cu_context_->language = Language::Java;
break;
+ case dwarf2reader::DW_LANG_Swift:
+ cu_context_->language = Language::Swift;
+ break;
+
+ case dwarf2reader::DW_LANG_Rust:
+ cu_context_->language = Language::Rust;
+ break;
+
// DWARF has no generic language code for assembly language; this is
// what the GNU toolchain uses.
case dwarf2reader::DW_LANG_Mips_Assembler:
@@ -649,7 +794,7 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
// Objective C and Objective C++ seem to create entries for
// methods whose DW_AT_name values are already fully-qualified:
// "-[Classname method:]". These appear at the top level.
- //
+ //
// DWARF data for C should never include namespaces or functions
// nested in struct types, but if it ever does, then C++'s
// notation is probably not a bad choice for that.
@@ -667,7 +812,7 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
void DwarfCUToModule::ReadSourceLines(uint64 offset) {
const dwarf2reader::SectionMap &section_map
- = cu_context_->file_context->section_map;
+ = cu_context_->file_context->section_map();
dwarf2reader::SectionMap::const_iterator map_entry
= section_map.find(".debug_line");
// Mac OS X puts DWARF data in sections whose names begin with "__"
@@ -678,14 +823,14 @@ void DwarfCUToModule::ReadSourceLines(uint64 offset) {
cu_context_->reporter->MissingSection(".debug_line");
return;
}
- const char *section_start = map_entry->second.first;
+ const uint8_t *section_start = map_entry->second.first;
uint64 section_length = map_entry->second.second;
if (offset >= section_length) {
cu_context_->reporter->BadLineInfoOffset(offset);
return;
}
- (*line_reader_)(section_start + offset, section_length - offset,
- cu_context_->file_context->module, &lines_);
+ line_reader_->ReadProgram(section_start + offset, section_length - offset,
+ cu_context_->file_context->module_, &lines_);
}
namespace {
@@ -715,9 +860,9 @@ void DwarfCUToModule::AssignLinesToFunctions() {
// complexity from here on out is linear.
// Put both our functions and lines in order by address.
- sort(functions->begin(), functions->end(),
- Module::Function::CompareByAddress);
- sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress);
+ std::sort(functions->begin(), functions->end(),
+ Module::Function::CompareByAddress);
+ std::sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress);
// The last line that we used any piece of. We use this only for
// generating warnings.
@@ -852,7 +997,7 @@ void DwarfCUToModule::AssignLinesToFunctions() {
// both func and line begin after CURRENT. The next transition
// is the start of the next function or next line, whichever
// is earliest.
- assert (func || line);
+ assert(func || line);
if (func && line)
next_transition = std::min(func->address, line->address);
else if (func)
@@ -910,12 +1055,14 @@ void DwarfCUToModule::Finish() {
// Add our functions, which now have source lines assigned to them,
// to module_.
- cu_context_->file_context->module->AddFunctions(functions->begin(),
- functions->end());
+ cu_context_->file_context->module_->AddFunctions(functions->begin(),
+ functions->end());
// Ownership of the function objects has shifted from cu_context to
// the Module.
functions->clear();
+
+ cu_context_->file_context->ClearSpecifications();
}
bool DwarfCUToModule::StartCompilationUnit(uint64 offset,
@@ -926,8 +1073,7 @@ bool DwarfCUToModule::StartCompilationUnit(uint64 offset,
return dwarf_version >= 2;
}
-bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag,
- const AttributeList& attrs) {
+bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag) {
// We don't deal with partial compilation units (the only other tag
// likely to be used for root DIE).
return tag == dwarf2reader::DW_TAG_compile_unit;
diff --git a/3rdParty/Breakpad/src/common/dwarf_cu_to_module.h b/3rdParty/Breakpad/src/common/dwarf_cu_to_module.h
index ac62846..a36b82b 100644
--- a/3rdParty/Breakpad/src/common/dwarf_cu_to_module.h
+++ b/3rdParty/Breakpad/src/common/dwarf_cu_to_module.h
@@ -39,6 +39,8 @@
#ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__
#define COMMON_LINUX_DWARF_CU_TO_MODULE_H__
+#include <stdint.h>
+
#include <string>
#include "common/language.h"
@@ -46,11 +48,11 @@
#include "common/dwarf/bytereader.h"
#include "common/dwarf/dwarf2diehandler.h"
#include "common/dwarf/dwarf2reader.h"
+#include "common/scoped_ptr.h"
#include "common/using_std_string.h"
namespace google_breakpad {
-using dwarf2reader::AttributeList;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfLanguage;
@@ -66,7 +68,6 @@ using dwarf2reader::DwarfTag;
class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
struct FilePrivate;
public:
-
// Information global to the DWARF-bearing file we are processing,
// for use by DwarfCUToModule. Each DwarfCUToModule instance deals
// with a single compilation unit within the file, but information
@@ -74,40 +75,75 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// for filling it in appropriately (except for the 'file_private'
// field, which the constructor and destructor take care of), and
// then providing it to the DwarfCUToModule instance for each
- // compilation unit we process in that file.
- struct FileContext {
- FileContext(const string &filename_arg, Module *module_arg);
+ // compilation unit we process in that file. Set HANDLE_INTER_CU_REFS
+ // to true to handle debugging symbols with DW_FORM_ref_addr entries.
+ class FileContext {
+ public:
+ FileContext(const string &filename,
+ Module *module,
+ bool handle_inter_cu_refs);
~FileContext();
+ // Add CONTENTS of size LENGTH to the section map as NAME.
+ void AddSectionToSectionMap(const string& name,
+ const uint8_t *contents,
+ uint64 length);
+
+ // Clear the section map for testing.
+ void ClearSectionMapForTest();
+
+ const dwarf2reader::SectionMap& section_map() const;
+
+ private:
+ friend class DwarfCUToModule;
+
+ // Clears all the Specifications if HANDLE_INTER_CU_REFS_ is false.
+ void ClearSpecifications();
+
+ // Given an OFFSET and a CU that starts at COMPILATION_UNIT_START, returns
+ // true if this is an inter-compilation unit reference that is not being
+ // handled.
+ bool IsUnhandledInterCUReference(uint64 offset,
+ uint64 compilation_unit_start) const;
+
// The name of this file, for use in error messages.
- string filename;
+ const string filename_;
// A map of this file's sections, used for finding other DWARF
// sections that the .debug_info section may refer to.
- dwarf2reader::SectionMap section_map;
+ dwarf2reader::SectionMap section_map_;
// The Module to which we're contributing definitions.
- Module *module;
+ Module *module_;
+
+ // True if we are handling references between compilation units.
+ const bool handle_inter_cu_refs_;
// Inter-compilation unit data used internally by the handlers.
- FilePrivate *file_private;
+ scoped_ptr<FilePrivate> file_private_;
};
- // An abstract base class for functors that handle DWARF line data
+ // An abstract base class for handlers that handle DWARF line data
// for DwarfCUToModule. DwarfCUToModule could certainly just use
// dwarf2reader::LineInfo itself directly, but decoupling things
// this way makes unit testing a little easier.
- class LineToModuleFunctor {
+ class LineToModuleHandler {
public:
- LineToModuleFunctor() { }
- virtual ~LineToModuleFunctor() { }
+ LineToModuleHandler() { }
+ virtual ~LineToModuleHandler() { }
+
+ // Called at the beginning of a new compilation unit, prior to calling
+ // ReadProgram(). compilation_dir will indicate the path that the
+ // current compilation unit was compiled in, consistent with the
+ // DW_AT_comp_dir DIE.
+ virtual void StartCompilationUnit(const string& compilation_dir) = 0;
// Populate MODULE and LINES with source file names and code/line
// mappings, given a pointer to some DWARF line number data
// PROGRAM, and an overestimate of its size. Add no zero-length
// lines to LINES.
- virtual void operator()(const char *program, uint64 length,
- Module *module, vector<Module::Line> *lines) = 0;
+ virtual void ReadProgram(const uint8_t *program, uint64 length,
+ Module *module, vector<Module::Line> *lines) = 0;
};
// The interface DwarfCUToModule uses to report warnings. The member
@@ -165,9 +201,20 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// link.
virtual void UnnamedFunction(uint64 offset);
+ // __cxa_demangle() failed to demangle INPUT.
+ virtual void DemangleError(const string &input);
+
+ // The DW_FORM_ref_addr at OFFSET to TARGET was not handled because
+ // FilePrivate did not retain the inter-CU specification data.
+ virtual void UnhandledInterCUReference(uint64 offset, uint64 target);
+
+ uint64 cu_offset() const {
+ return cu_offset_;
+ }
+
protected:
- string filename_;
- uint64 cu_offset_;
+ const string filename_;
+ const uint64 cu_offset_;
string cu_name_;
bool printed_cu_header_;
bool printed_unpaired_header_;
@@ -187,7 +234,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// unit's line number data. Use REPORTER to report problems with the
// data we find.
DwarfCUToModule(FileContext *file_context,
- LineToModuleFunctor *line_reader,
+ LineToModuleHandler *line_reader,
WarningReporter *reporter);
~DwarfCUToModule();
@@ -201,8 +248,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
enum DwarfForm form,
const string &data);
bool EndAttributes();
- DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
- const AttributeList &attrs);
+ DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag);
// Assign all our source Lines to the Functions that cover their
// addresses, and then add them to module_.
@@ -211,17 +257,14 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
- bool StartRootDIE(uint64 offset, enum DwarfTag tag,
- const AttributeList& attrs);
+ bool StartRootDIE(uint64 offset, enum DwarfTag tag);
private:
-
// Used internally by the handler. Full definitions are in
// dwarf_cu_to_module.cc.
- struct FilePrivate;
- struct Specification;
struct CUContext;
struct DIEContext;
+ struct Specification;
class GenericDIEHandler;
class FuncHandler;
class NamedScopeHandler;
@@ -231,7 +274,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// Set this compilation unit's source language to LANGUAGE.
void SetLanguage(DwarfLanguage language);
-
+
// Read source line information at OFFSET in the .debug_line
// section. Record source files in module_, but record source lines
// in lines_; we apportion them to functions in
@@ -250,14 +293,14 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// owned by this DwarfCUToModule: the constructor sets them, and the
// destructor deletes them.
- // The functor to use to handle line number data.
- LineToModuleFunctor *line_reader_;
+ // The handler to use to handle line number data.
+ LineToModuleHandler *line_reader_;
// This compilation unit's context.
- CUContext *cu_context_;
+ scoped_ptr<CUContext> cu_context_;
// A context for our children.
- DIEContext *child_context_;
+ scoped_ptr<DIEContext> child_context_;
// True if this compilation unit has source line information.
bool has_source_line_info_;
@@ -272,6 +315,6 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
vector<Module::Line> lines_;
};
-} // namespace google_breakpad
+} // namespace google_breakpad
#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__
diff --git a/3rdParty/Breakpad/src/common/dwarf_cu_to_module_unittest.cc b/3rdParty/Breakpad/src/common/dwarf_cu_to_module_unittest.cc
new file mode 100644
index 0000000..2ce4a60
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf_cu_to_module_unittest.cc
@@ -0,0 +1,1852 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule.
+
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/dwarf_cu_to_module.h"
+#include "common/using_std_string.h"
+
+using std::make_pair;
+using std::vector;
+
+using dwarf2reader::DIEHandler;
+using dwarf2reader::DwarfTag;
+using dwarf2reader::DwarfAttribute;
+using dwarf2reader::DwarfForm;
+using dwarf2reader::DwarfInline;
+using dwarf2reader::RootDIEHandler;
+using google_breakpad::DwarfCUToModule;
+using google_breakpad::Module;
+
+using ::testing::_;
+using ::testing::AtMost;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::Test;
+using ::testing::TestWithParam;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+// Mock classes.
+
+class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler {
+ public:
+ MOCK_METHOD1(StartCompilationUnit, void(const string& compilation_dir));
+ MOCK_METHOD4(ReadProgram, void(const uint8_t *program, uint64 length,
+ Module *module, vector<Module::Line> *lines));
+};
+
+class MockWarningReporter: public DwarfCUToModule::WarningReporter {
+ public:
+ MockWarningReporter(const string &filename, uint64 cu_offset)
+ : DwarfCUToModule::WarningReporter(filename, cu_offset) { }
+ MOCK_METHOD1(SetCUName, void(const string &name));
+ MOCK_METHOD2(UnknownSpecification, void(uint64 offset, uint64 target));
+ MOCK_METHOD2(UnknownAbstractOrigin, void(uint64 offset, uint64 target));
+ MOCK_METHOD1(MissingSection, void(const string &section_name));
+ MOCK_METHOD1(BadLineInfoOffset, void(uint64 offset));
+ MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function));
+ MOCK_METHOD1(UncoveredLine, void(const Module::Line &line));
+ MOCK_METHOD1(UnnamedFunction, void(uint64 offset));
+ MOCK_METHOD1(DemangleError, void(const string &input));
+ MOCK_METHOD2(UnhandledInterCUReference, void(uint64 offset, uint64 target));
+};
+
+// A fixture class including all the objects needed to handle a
+// compilation unit, and their entourage. It includes member functions
+// for doing common kinds of setup and tests.
+class CUFixtureBase {
+ public:
+ // If we have:
+ //
+ // vector<Module::Line> lines;
+ // AppendLinesFunctor appender(lines);
+ //
+ // then doing:
+ //
+ // appender(line_program, length, module, line_vector);
+ //
+ // will append lines to the end of line_vector. We can use this with
+ // MockLineToModuleHandler like this:
+ //
+ // MockLineToModuleHandler l2m;
+ // EXPECT_CALL(l2m, ReadProgram(_,_,_,_))
+ // .WillOnce(DoAll(Invoke(appender), Return()));
+ //
+ // in which case calling l2m with some line vector will append lines.
+ class AppendLinesFunctor {
+ public:
+ explicit AppendLinesFunctor(
+ const vector<Module::Line> *lines) : lines_(lines) { }
+ void operator()(const uint8_t *program, uint64 length,
+ Module *module, vector<Module::Line> *lines) {
+ lines->insert(lines->end(), lines_->begin(), lines_->end());
+ }
+ private:
+ const vector<Module::Line> *lines_;
+ };
+
+ CUFixtureBase()
+ : module_("module-name", "module-os", "module-arch", "module-id"),
+ file_context_("dwarf-filename", &module_, true),
+ language_(dwarf2reader::DW_LANG_none),
+ language_signed_(false),
+ appender_(&lines_),
+ reporter_("dwarf-filename", 0xcf8f9bb6443d29b5LL),
+ root_handler_(&file_context_, &line_reader_, &reporter_),
+ functions_filled_(false) {
+ // By default, expect no warnings to be reported, and expect the
+ // compilation unit's name to be provided. The test can override
+ // these expectations.
+ EXPECT_CALL(reporter_, SetCUName("compilation-unit-name")).Times(1);
+ EXPECT_CALL(reporter_, UnknownSpecification(_, _)).Times(0);
+ EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, _)).Times(0);
+ EXPECT_CALL(reporter_, MissingSection(_)).Times(0);
+ EXPECT_CALL(reporter_, BadLineInfoOffset(_)).Times(0);
+ EXPECT_CALL(reporter_, UncoveredFunction(_)).Times(0);
+ EXPECT_CALL(reporter_, UncoveredLine(_)).Times(0);
+ EXPECT_CALL(reporter_, UnnamedFunction(_)).Times(0);
+ EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(0);
+
+ // By default, expect the line program reader not to be invoked. We
+ // may override this in StartCU.
+ EXPECT_CALL(line_reader_, StartCompilationUnit(_)).Times(0);
+ EXPECT_CALL(line_reader_, ReadProgram(_,_,_,_)).Times(0);
+
+ // The handler will consult this section map to decide what to
+ // pass to our line reader.
+ file_context_.AddSectionToSectionMap(".debug_line",
+ dummy_line_program_,
+ dummy_line_size_);
+ }
+
+ // Add a line with the given address, size, filename, and line
+ // number to the end of the statement list the handler will receive
+ // when it invokes its LineToModuleHandler. Call this before calling
+ // StartCU.
+ void PushLine(Module::Address address, Module::Address size,
+ const string &filename, int line_number);
+
+ // Use LANGUAGE for the compilation unit. More precisely, arrange
+ // for StartCU to pass the compilation unit's root DIE a
+ // DW_AT_language attribute whose value is LANGUAGE.
+ void SetLanguage(dwarf2reader::DwarfLanguage language) {
+ language_ = language;
+ }
+
+ // If SIGNED true, have StartCU report DW_AT_language as a signed
+ // attribute; if false, have it report it as unsigned.
+ void SetLanguageSigned(bool is_signed) { language_signed_ = is_signed; }
+
+ // Call the handler this.root_handler_'s StartCompilationUnit and
+ // StartRootDIE member functions, passing it appropriate attributes as
+ // determined by prior calls to PushLine and SetLanguage. Leave
+ // this.root_handler_ ready to hear about children: call
+ // this.root_handler_.EndAttributes, but not this.root_handler_.Finish.
+ void StartCU();
+
+ // Have HANDLER process some strange attribute/form/value triples.
+ void ProcessStrangeAttributes(dwarf2reader::DIEHandler *handler);
+
+ // Start a child DIE of PARENT with the given tag and name. Leave
+ // the handler ready to hear about children: call EndAttributes, but
+ // not Finish.
+ DIEHandler *StartNamedDIE(DIEHandler *parent, DwarfTag tag,
+ const string &name);
+
+ // Start a child DIE of PARENT with the given tag and a
+ // DW_AT_specification attribute whose value is SPECIFICATION. Leave
+ // the handler ready to hear about children: call EndAttributes, but
+ // not Finish. If NAME is non-zero, use it as the DW_AT_name
+ // attribute.
+ DIEHandler *StartSpecifiedDIE(DIEHandler *parent, DwarfTag tag,
+ uint64 specification, const char *name = NULL);
+
+ // Define a function as a child of PARENT with the given name, address, and
+ // size. If high_pc_form is DW_FORM_addr then the DW_AT_high_pc attribute
+ // will be written as an address; otherwise it will be written as the
+ // function's size. Call EndAttributes and Finish; one cannot define
+ // children of the defined function's DIE.
+ void DefineFunction(DIEHandler *parent, const string &name,
+ Module::Address address, Module::Address size,
+ const char* mangled_name,
+ DwarfForm high_pc_form = dwarf2reader::DW_FORM_addr);
+
+ // Create a declaration DIE as a child of PARENT with the given
+ // offset, tag and name. If NAME is the empty string, don't provide
+ // a DW_AT_name attribute. Call EndAttributes and Finish.
+ void DeclarationDIE(DIEHandler *parent, uint64 offset,
+ DwarfTag tag, const string &name,
+ const string &mangled_name);
+
+ // Create a definition DIE as a child of PARENT with the given tag
+ // that refers to the declaration DIE at offset SPECIFICATION as its
+ // specification. If NAME is non-empty, pass it as the DW_AT_name
+ // attribute. If SIZE is non-zero, record ADDRESS and SIZE as
+ // low_pc/high_pc attributes.
+ void DefinitionDIE(DIEHandler *parent, DwarfTag tag,
+ uint64 specification, const string &name,
+ Module::Address address = 0, Module::Address size = 0);
+
+ // Create an inline DW_TAG_subprogram DIE as a child of PARENT. If
+ // SPECIFICATION is non-zero, then the DIE refers to the declaration DIE at
+ // offset SPECIFICATION as its specification. If Name is non-empty, pass it
+ // as the DW_AT_name attribute.
+ void AbstractInstanceDIE(DIEHandler *parent, uint64 offset,
+ DwarfInline type, uint64 specification,
+ const string &name,
+ DwarfForm form = dwarf2reader::DW_FORM_data1);
+
+ // Create a DW_TAG_subprogram DIE as a child of PARENT that refers to
+ // ORIGIN in its DW_AT_abstract_origin attribute. If NAME is the empty
+ // string, don't provide a DW_AT_name attribute.
+ void DefineInlineInstanceDIE(DIEHandler *parent, const string &name,
+ uint64 origin, Module::Address address,
+ Module::Address size);
+
+ // The following Test* functions should be called after calling
+ // this.root_handler_.Finish. After that point, no further calls
+ // should be made on the handler.
+
+ // Test that the number of functions defined in the module this.module_ is
+ // equal to EXPECTED.
+ void TestFunctionCount(size_t expected);
+
+ // Test that the I'th function (ordered by address) in the module
+ // this.module_ has the given name, address, and size, and that its
+ // parameter size is zero.
+ void TestFunction(int i, const string &name,
+ Module::Address address, Module::Address size);
+
+ // Test that the number of source lines owned by the I'th function
+ // in the module this.module_ is equal to EXPECTED.
+ void TestLineCount(int i, size_t expected);
+
+ // Test that the J'th line (ordered by address) of the I'th function
+ // (again, by address) has the given address, size, filename, and
+ // line number.
+ void TestLine(int i, int j, Module::Address address, Module::Address size,
+ const string &filename, int number);
+
+ // Actual objects under test.
+ Module module_;
+ DwarfCUToModule::FileContext file_context_;
+
+ // If this is not DW_LANG_none, we'll pass it as a DW_AT_language
+ // attribute to the compilation unit. This defaults to DW_LANG_none.
+ dwarf2reader::DwarfLanguage language_;
+
+ // If this is true, report DW_AT_language as a signed value; if false,
+ // report it as an unsigned value.
+ bool language_signed_;
+
+ // If this is not empty, we'll give the CU a DW_AT_comp_dir attribute that
+ // indicates the path that this compilation unit was compiled in.
+ string compilation_dir_;
+
+ // If this is not empty, we'll give the CU a DW_AT_stmt_list
+ // attribute that, when passed to line_reader_, adds these lines to the
+ // provided lines array.
+ vector<Module::Line> lines_;
+
+ // Mock line program reader.
+ MockLineToModuleHandler line_reader_;
+ AppendLinesFunctor appender_;
+ static const uint8_t dummy_line_program_[];
+ static const size_t dummy_line_size_;
+
+ MockWarningReporter reporter_;
+ DwarfCUToModule root_handler_;
+
+ private:
+ // Fill functions_, if we haven't already.
+ void FillFunctions();
+
+ // If functions_filled_ is true, this is a table of functions we've
+ // extracted from module_, sorted by address.
+ vector<Module::Function *> functions_;
+ // True if we have filled the above vector with this.module_'s function list.
+ bool functions_filled_;
+};
+
+const uint8_t CUFixtureBase::dummy_line_program_[] = "lots of fun data";
+const size_t CUFixtureBase::dummy_line_size_ =
+ sizeof(CUFixtureBase::dummy_line_program_);
+
+void CUFixtureBase::PushLine(Module::Address address, Module::Address size,
+ const string &filename, int line_number) {
+ Module::Line l;
+ l.address = address;
+ l.size = size;
+ l.file = module_.FindFile(filename);
+ l.number = line_number;
+ lines_.push_back(l);
+}
+
+void CUFixtureBase::StartCU() {
+ if (!compilation_dir_.empty())
+ EXPECT_CALL(line_reader_,
+ StartCompilationUnit(compilation_dir_)).Times(1);
+
+ // If we have lines, make the line reader expect to be invoked at
+ // most once. (Hey, if the handler can pass its tests without
+ // bothering to read the line number data, that's great.)
+ // Have it add the lines passed to PushLine. Otherwise, leave the
+ // initial expectation (no calls) in force.
+ if (!lines_.empty())
+ EXPECT_CALL(line_reader_,
+ ReadProgram(&dummy_line_program_[0], dummy_line_size_,
+ &module_, _))
+ .Times(AtMost(1))
+ .WillOnce(DoAll(Invoke(appender_), Return()));
+
+ ASSERT_TRUE(root_handler_
+ .StartCompilationUnit(0x51182ec307610b51ULL, 0x81, 0x44,
+ 0x4241b4f33720dd5cULL, 3));
+ {
+ ASSERT_TRUE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL,
+ dwarf2reader::DW_TAG_compile_unit));
+ }
+ root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp,
+ "compilation-unit-name");
+ if (!compilation_dir_.empty())
+ root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_comp_dir,
+ dwarf2reader::DW_FORM_strp,
+ compilation_dir_);
+ if (!lines_.empty())
+ root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list,
+ dwarf2reader::DW_FORM_ref4,
+ 0);
+ if (language_ != dwarf2reader::DW_LANG_none) {
+ if (language_signed_)
+ root_handler_.ProcessAttributeSigned(dwarf2reader::DW_AT_language,
+ dwarf2reader::DW_FORM_sdata,
+ language_);
+ else
+ root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_language,
+ dwarf2reader::DW_FORM_udata,
+ language_);
+ }
+ ASSERT_TRUE(root_handler_.EndAttributes());
+}
+
+void CUFixtureBase::ProcessStrangeAttributes(
+ dwarf2reader::DIEHandler *handler) {
+ handler->ProcessAttributeUnsigned((DwarfAttribute) 0xf560dead,
+ (DwarfForm) 0x4106e4db,
+ 0xa592571997facda1ULL);
+ handler->ProcessAttributeSigned((DwarfAttribute) 0x85380095,
+ (DwarfForm) 0x0f16fe87,
+ 0x12602a4e3bf1f446LL);
+ handler->ProcessAttributeReference((DwarfAttribute) 0xf7f7480f,
+ (DwarfForm) 0x829e038a,
+ 0x50fddef44734fdecULL);
+ static const uint8_t buffer[10] = "frobynode";
+ handler->ProcessAttributeBuffer((DwarfAttribute) 0xa55ffb51,
+ (DwarfForm) 0x2f43b041,
+ buffer, sizeof(buffer));
+ handler->ProcessAttributeString((DwarfAttribute) 0x2f43b041,
+ (DwarfForm) 0x895ffa23,
+ "strange string");
+}
+
+DIEHandler *CUFixtureBase::StartNamedDIE(DIEHandler *parent,
+ DwarfTag tag,
+ const string &name) {
+ dwarf2reader::DIEHandler *handler
+ = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag);
+ if (!handler)
+ return NULL;
+ handler->ProcessAttributeString(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp,
+ name);
+ ProcessStrangeAttributes(handler);
+ if (!handler->EndAttributes()) {
+ handler->Finish();
+ delete handler;
+ return NULL;
+ }
+
+ return handler;
+}
+
+DIEHandler *CUFixtureBase::StartSpecifiedDIE(DIEHandler *parent,
+ DwarfTag tag,
+ uint64 specification,
+ const char *name) {
+ dwarf2reader::DIEHandler *handler
+ = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag);
+ if (!handler)
+ return NULL;
+ if (name)
+ handler->ProcessAttributeString(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp,
+ name);
+ handler->ProcessAttributeReference(dwarf2reader::DW_AT_specification,
+ dwarf2reader::DW_FORM_ref4,
+ specification);
+ if (!handler->EndAttributes()) {
+ handler->Finish();
+ delete handler;
+ return NULL;
+ }
+
+ return handler;
+}
+
+void CUFixtureBase::DefineFunction(dwarf2reader::DIEHandler *parent,
+ const string &name, Module::Address address,
+ Module::Address size,
+ const char* mangled_name,
+ DwarfForm high_pc_form) {
+ dwarf2reader::DIEHandler *func
+ = parent->FindChildHandler(0xe34797c7e68590a8LL,
+ dwarf2reader::DW_TAG_subprogram);
+ ASSERT_TRUE(func != NULL);
+ func->ProcessAttributeString(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp,
+ name);
+ func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc,
+ dwarf2reader::DW_FORM_addr,
+ address);
+
+ Module::Address high_pc = size;
+ if (high_pc_form == dwarf2reader::DW_FORM_addr) {
+ high_pc += address;
+ }
+ func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc,
+ high_pc_form,
+ high_pc);
+
+ if (mangled_name)
+ func->ProcessAttributeString(dwarf2reader::DW_AT_MIPS_linkage_name,
+ dwarf2reader::DW_FORM_strp,
+ mangled_name);
+
+ ProcessStrangeAttributes(func);
+ EXPECT_TRUE(func->EndAttributes());
+ func->Finish();
+ delete func;
+}
+
+void CUFixtureBase::DeclarationDIE(DIEHandler *parent, uint64 offset,
+ DwarfTag tag,
+ const string &name,
+ const string &mangled_name) {
+ dwarf2reader::DIEHandler *die = parent->FindChildHandler(offset, tag);
+ ASSERT_TRUE(die != NULL);
+ if (!name.empty())
+ die->ProcessAttributeString(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp,
+ name);
+ if (!mangled_name.empty())
+ die->ProcessAttributeString(dwarf2reader::DW_AT_MIPS_linkage_name,
+ dwarf2reader::DW_FORM_strp,
+ mangled_name);
+
+ die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_declaration,
+ dwarf2reader::DW_FORM_flag,
+ 1);
+ EXPECT_TRUE(die->EndAttributes());
+ die->Finish();
+ delete die;
+}
+
+void CUFixtureBase::DefinitionDIE(DIEHandler *parent,
+ DwarfTag tag,
+ uint64 specification,
+ const string &name,
+ Module::Address address,
+ Module::Address size) {
+ dwarf2reader::DIEHandler *die
+ = parent->FindChildHandler(0x6ccfea031a9e6cc9ULL, tag);
+ ASSERT_TRUE(die != NULL);
+ die->ProcessAttributeReference(dwarf2reader::DW_AT_specification,
+ dwarf2reader::DW_FORM_ref4,
+ specification);
+ if (!name.empty())
+ die->ProcessAttributeString(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp,
+ name);
+ if (size) {
+ die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc,
+ dwarf2reader::DW_FORM_addr,
+ address);
+ die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc,
+ dwarf2reader::DW_FORM_addr,
+ address + size);
+ }
+ EXPECT_TRUE(die->EndAttributes());
+ die->Finish();
+ delete die;
+}
+
+void CUFixtureBase::AbstractInstanceDIE(DIEHandler *parent,
+ uint64 offset,
+ DwarfInline type,
+ uint64 specification,
+ const string &name,
+ DwarfForm form) {
+ dwarf2reader::DIEHandler *die
+ = parent->FindChildHandler(offset, dwarf2reader::DW_TAG_subprogram);
+ ASSERT_TRUE(die != NULL);
+ if (specification != 0ULL)
+ die->ProcessAttributeReference(dwarf2reader::DW_AT_specification,
+ dwarf2reader::DW_FORM_ref4,
+ specification);
+ if (form == dwarf2reader::DW_FORM_sdata) {
+ die->ProcessAttributeSigned(dwarf2reader::DW_AT_inline, form, type);
+ } else {
+ die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_inline, form, type);
+ }
+ if (!name.empty())
+ die->ProcessAttributeString(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp,
+ name);
+
+ EXPECT_TRUE(die->EndAttributes());
+ die->Finish();
+ delete die;
+}
+
+void CUFixtureBase::DefineInlineInstanceDIE(DIEHandler *parent,
+ const string &name,
+ uint64 origin,
+ Module::Address address,
+ Module::Address size) {
+ dwarf2reader::DIEHandler *func
+ = parent->FindChildHandler(0x11c70f94c6e87ccdLL,
+ dwarf2reader::DW_TAG_subprogram);
+ ASSERT_TRUE(func != NULL);
+ if (!name.empty()) {
+ func->ProcessAttributeString(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp,
+ name);
+ }
+ func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc,
+ dwarf2reader::DW_FORM_addr,
+ address);
+ func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc,
+ dwarf2reader::DW_FORM_addr,
+ address + size);
+ func->ProcessAttributeReference(dwarf2reader::DW_AT_abstract_origin,
+ dwarf2reader::DW_FORM_ref4,
+ origin);
+ ProcessStrangeAttributes(func);
+ EXPECT_TRUE(func->EndAttributes());
+ func->Finish();
+ delete func;
+}
+
+void CUFixtureBase::FillFunctions() {
+ if (functions_filled_)
+ return;
+ module_.GetFunctions(&functions_, functions_.end());
+ sort(functions_.begin(), functions_.end(),
+ Module::Function::CompareByAddress);
+ functions_filled_ = true;
+}
+
+void CUFixtureBase::TestFunctionCount(size_t expected) {
+ FillFunctions();
+ ASSERT_EQ(expected, functions_.size());
+}
+
+void CUFixtureBase::TestFunction(int i, const string &name,
+ Module::Address address,
+ Module::Address size) {
+ FillFunctions();
+ ASSERT_LT((size_t) i, functions_.size());
+
+ Module::Function *function = functions_[i];
+ EXPECT_EQ(name, function->name);
+ EXPECT_EQ(address, function->address);
+ EXPECT_EQ(size, function->size);
+ EXPECT_EQ(0U, function->parameter_size);
+}
+
+void CUFixtureBase::TestLineCount(int i, size_t expected) {
+ FillFunctions();
+ ASSERT_LT((size_t) i, functions_.size());
+
+ ASSERT_EQ(expected, functions_[i]->lines.size());
+}
+
+void CUFixtureBase::TestLine(int i, int j,
+ Module::Address address, Module::Address size,
+ const string &filename, int number) {
+ FillFunctions();
+ ASSERT_LT((size_t) i, functions_.size());
+ ASSERT_LT((size_t) j, functions_[i]->lines.size());
+
+ Module::Line *line = &functions_[i]->lines[j];
+ EXPECT_EQ(address, line->address);
+ EXPECT_EQ(size, line->size);
+ EXPECT_EQ(filename, line->file->name.c_str());
+ EXPECT_EQ(number, line->number);
+}
+
+// Include caller locations for our test subroutines.
+#define TRACE(call) do { SCOPED_TRACE("called from here"); call; } while (0)
+#define PushLine(a,b,c,d) TRACE(PushLine((a),(b),(c),(d)))
+#define SetLanguage(a) TRACE(SetLanguage(a))
+#define StartCU() TRACE(StartCU())
+#define DefineFunction(a,b,c,d,e) TRACE(DefineFunction((a),(b),(c),(d),(e)))
+// (DefineFunction) instead of DefineFunction to avoid macro expansion.
+#define DefineFunction6(a,b,c,d,e,f) \
+ TRACE((DefineFunction)((a),(b),(c),(d),(e),(f)))
+#define DeclarationDIE(a,b,c,d,e) TRACE(DeclarationDIE((a),(b),(c),(d),(e)))
+#define DefinitionDIE(a,b,c,d,e,f) \
+ TRACE(DefinitionDIE((a),(b),(c),(d),(e),(f)))
+#define TestFunctionCount(a) TRACE(TestFunctionCount(a))
+#define TestFunction(a,b,c,d) TRACE(TestFunction((a),(b),(c),(d)))
+#define TestLineCount(a,b) TRACE(TestLineCount((a),(b)))
+#define TestLine(a,b,c,d,e,f) TRACE(TestLine((a),(b),(c),(d),(e),(f)))
+
+class SimpleCU: public CUFixtureBase, public Test {
+};
+
+TEST_F(SimpleCU, CompilationDir) {
+ compilation_dir_ = "/src/build/";
+
+ StartCU();
+ root_handler_.Finish();
+}
+
+TEST_F(SimpleCU, OneFunc) {
+ PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772);
+
+ StartCU();
+ DefineFunction(&root_handler_, "function1",
+ 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, NULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL);
+ TestLineCount(0, 1);
+ TestLine(0, 0, 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file",
+ 246571772);
+}
+
+// As above, only DW_AT_high_pc is a length rather than an address.
+TEST_F(SimpleCU, OneFuncHighPcIsLength) {
+ PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772);
+
+ StartCU();
+ DefineFunction6(&root_handler_, "function1",
+ 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, NULL,
+ dwarf2reader::DW_FORM_udata);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL);
+ TestLineCount(0, 1);
+ TestLine(0, 0, 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file",
+ 246571772);
+}
+
+TEST_F(SimpleCU, MangledName) {
+ PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772);
+
+ StartCU();
+ DefineFunction(&root_handler_, "function1",
+ 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "_ZN1n1fEi");
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "n::f(int)", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL);
+}
+
+TEST_F(SimpleCU, IrrelevantRootChildren) {
+ StartCU();
+ EXPECT_FALSE(root_handler_
+ .FindChildHandler(0x7db32bff4e2dcfb1ULL,
+ dwarf2reader::DW_TAG_lexical_block));
+}
+
+TEST_F(SimpleCU, IrrelevantNamedScopeChildren) {
+ StartCU();
+ DIEHandler *class_A_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A");
+ EXPECT_TRUE(class_A_handler != NULL);
+ EXPECT_FALSE(class_A_handler
+ ->FindChildHandler(0x02e55999b865e4e9ULL,
+ dwarf2reader::DW_TAG_lexical_block));
+ delete class_A_handler;
+}
+
+// Verify that FileContexts can safely be deleted unused.
+TEST_F(SimpleCU, UnusedFileContext) {
+ Module m("module-name", "module-os", "module-arch", "module-id");
+ DwarfCUToModule::FileContext fc("dwarf-filename", &m, true);
+
+ // Kludge: satisfy reporter_'s expectation.
+ reporter_.SetCUName("compilation-unit-name");
+}
+
+TEST_F(SimpleCU, InlineFunction) {
+ PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118);
+
+ StartCU();
+ AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL,
+ dwarf2reader::DW_INL_inlined, 0, "inline-name");
+ DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL,
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "inline-name",
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+}
+
+TEST_F(SimpleCU, InlineFunctionSignedAttribute) {
+ PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118);
+
+ StartCU();
+ AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL,
+ dwarf2reader::DW_INL_inlined, 0, "inline-name",
+ dwarf2reader::DW_FORM_sdata);
+ DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL,
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "inline-name",
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+}
+
+// Any DIE with an DW_AT_inline attribute can be cited by
+// DW_AT_abstract_origin attributes --- even if the value of the
+// DW_AT_inline attribute is DW_INL_not_inlined.
+TEST_F(SimpleCU, AbstractOriginNotInlined) {
+ PushLine(0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL, "line-file", 6111581);
+
+ StartCU();
+ AbstractInstanceDIE(&root_handler_, 0x93e9cdad52826b39ULL,
+ dwarf2reader::DW_INL_not_inlined, 0, "abstract-instance");
+ DefineInlineInstanceDIE(&root_handler_, "", 0x93e9cdad52826b39ULL,
+ 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "abstract-instance",
+ 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL);
+}
+
+TEST_F(SimpleCU, UnknownAbstractOrigin) {
+ EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, 1ULL)).WillOnce(Return());
+ EXPECT_CALL(reporter_, UnnamedFunction(0x11c70f94c6e87ccdLL))
+ .WillOnce(Return());
+ PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118);
+
+ StartCU();
+ AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL,
+ dwarf2reader::DW_INL_inlined, 0, "inline-name");
+ DefineInlineInstanceDIE(&root_handler_, "", 1ULL,
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "<name omitted>",
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+}
+
+TEST_F(SimpleCU, UnnamedFunction) {
+ EXPECT_CALL(reporter_, UnnamedFunction(0xe34797c7e68590a8LL))
+ .WillOnce(Return());
+ PushLine(0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL, "line-file", 14044850);
+
+ StartCU();
+ DefineFunction(&root_handler_, "",
+ 0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL, NULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "<name omitted>",
+ 0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL);
+}
+
+// An address range.
+struct Range {
+ Module::Address start, end;
+};
+
+// Test data for pairing functions and lines.
+struct Situation {
+ // Two function intervals, and two line intervals.
+ Range functions[2], lines[2];
+
+ // The number of lines we expect to be assigned to each of the
+ // functions, and the address ranges.
+ int paired_count[2];
+ Range paired[2][2];
+
+ // The number of functions that are not entirely covered by lines,
+ // and vice versa.
+ int uncovered_functions, uncovered_lines;
+};
+
+#define PAIRING(func1_start, func1_end, func2_start, func2_end, \
+ line1_start, line1_end, line2_start, line2_end, \
+ func1_num_lines, func2_num_lines, \
+ func1_line1_start, func1_line1_end, \
+ func1_line2_start, func1_line2_end, \
+ func2_line1_start, func2_line1_end, \
+ func2_line2_start, func2_line2_end, \
+ uncovered_functions, uncovered_lines) \
+ { { { func1_start, func1_end }, { func2_start, func2_end } }, \
+ { { line1_start, line1_end }, { line2_start, line2_end } }, \
+ { func1_num_lines, func2_num_lines }, \
+ { { { func1_line1_start, func1_line1_end }, \
+ { func1_line2_start, func1_line2_end } }, \
+ { { func2_line1_start, func2_line1_end }, \
+ { func2_line2_start, func2_line2_end } } }, \
+ uncovered_functions, uncovered_lines },
+
+Situation situations[] = {
+#include "common/testdata/func-line-pairing.h"
+};
+
+#undef PAIRING
+
+class FuncLinePairing: public CUFixtureBase,
+ public TestWithParam<Situation> { };
+
+INSTANTIATE_TEST_CASE_P(AllSituations, FuncLinePairing,
+ ValuesIn(situations));
+
+TEST_P(FuncLinePairing, Pairing) {
+ const Situation &s = GetParam();
+ PushLine(s.lines[0].start,
+ s.lines[0].end - s.lines[0].start,
+ "line-file", 67636963);
+ PushLine(s.lines[1].start,
+ s.lines[1].end - s.lines[1].start,
+ "line-file", 67636963);
+ if (s.uncovered_functions)
+ EXPECT_CALL(reporter_, UncoveredFunction(_))
+ .Times(s.uncovered_functions)
+ .WillRepeatedly(Return());
+ if (s.uncovered_lines)
+ EXPECT_CALL(reporter_, UncoveredLine(_))
+ .Times(s.uncovered_lines)
+ .WillRepeatedly(Return());
+
+ StartCU();
+ DefineFunction(&root_handler_, "function1",
+ s.functions[0].start,
+ s.functions[0].end - s.functions[0].start, NULL);
+ DefineFunction(&root_handler_, "function2",
+ s.functions[1].start,
+ s.functions[1].end - s.functions[1].start, NULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(2);
+ TestFunction(0, "function1",
+ s.functions[0].start,
+ s.functions[0].end - s.functions[0].start);
+ TestLineCount(0, s.paired_count[0]);
+ for (int i = 0; i < s.paired_count[0]; i++)
+ TestLine(0, i, s.paired[0][i].start,
+ s.paired[0][i].end - s.paired[0][i].start,
+ "line-file", 67636963);
+ TestFunction(1, "function2",
+ s.functions[1].start,
+ s.functions[1].end - s.functions[1].start);
+ TestLineCount(1, s.paired_count[1]);
+ for (int i = 0; i < s.paired_count[1]; i++)
+ TestLine(1, i, s.paired[1][i].start,
+ s.paired[1][i].end - s.paired[1][i].start,
+ "line-file", 67636963);
+}
+
+TEST_F(FuncLinePairing, EmptyCU) {
+ StartCU();
+ root_handler_.Finish();
+
+ TestFunctionCount(0);
+}
+
+TEST_F(FuncLinePairing, LinesNoFuncs) {
+ PushLine(40, 2, "line-file", 82485646);
+ EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return());
+
+ StartCU();
+ root_handler_.Finish();
+
+ TestFunctionCount(0);
+}
+
+TEST_F(FuncLinePairing, FuncsNoLines) {
+ EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
+
+ StartCU();
+ DefineFunction(&root_handler_, "function1", 0x127da12ffcf5c51fULL, 0x1000U,
+ NULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "function1", 0x127da12ffcf5c51fULL, 0x1000U);
+}
+
+TEST_F(FuncLinePairing, GapThenFunction) {
+ PushLine(20, 2, "line-file-2", 174314698);
+ PushLine(10, 2, "line-file-1", 263008005);
+
+ StartCU();
+ DefineFunction(&root_handler_, "function1", 10, 2, NULL);
+ DefineFunction(&root_handler_, "function2", 20, 2, NULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(2);
+ TestFunction(0, "function1", 10, 2);
+ TestLineCount(0, 1);
+ TestLine(0, 0, 10, 2, "line-file-1", 263008005);
+ TestFunction(1, "function2", 20, 2);
+ TestLineCount(1, 1);
+ TestLine(1, 0, 20, 2, "line-file-2", 174314698);
+}
+
+// If GCC emits padding after one function to align the start of
+// the next, then it will attribute the padding instructions to
+// the last source line of function (to reduce the size of the
+// line number info), but omit it from the DW_AT_{low,high}_pc
+// range given in .debug_info (since it costs nothing to be
+// precise there). If we did use at least some of the line
+// we're about to skip, then assume this is what happened, and
+// don't warn.
+TEST_F(FuncLinePairing, GCCAlignmentStretch) {
+ PushLine(10, 10, "line-file", 63351048);
+ PushLine(20, 10, "line-file", 61661044);
+
+ StartCU();
+ DefineFunction(&root_handler_, "function1", 10, 5, NULL);
+ // five-byte gap between functions, covered by line 63351048.
+ // This should not elicit a warning.
+ DefineFunction(&root_handler_, "function2", 20, 10, NULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(2);
+ TestFunction(0, "function1", 10, 5);
+ TestLineCount(0, 1);
+ TestLine(0, 0, 10, 5, "line-file", 63351048);
+ TestFunction(1, "function2", 20, 10);
+ TestLineCount(1, 1);
+ TestLine(1, 0, 20, 10, "line-file", 61661044);
+}
+
+// Unfortunately, neither the DWARF parser's handler interface nor the
+// DIEHandler interface is capable of expressing a function that abuts
+// the end of the address space: the high_pc value looks like zero.
+
+TEST_F(FuncLinePairing, LineAtEndOfAddressSpace) {
+ PushLine(0xfffffffffffffff0ULL, 16, "line-file", 63351048);
+ EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return());
+
+ StartCU();
+ DefineFunction(&root_handler_, "function1", 0xfffffffffffffff0ULL, 6, NULL);
+ DefineFunction(&root_handler_, "function2", 0xfffffffffffffffaULL, 5, NULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(2);
+ TestFunction(0, "function1", 0xfffffffffffffff0ULL, 6);
+ TestLineCount(0, 1);
+ TestLine(0, 0, 0xfffffffffffffff0ULL, 6, "line-file", 63351048);
+ TestFunction(1, "function2", 0xfffffffffffffffaULL, 5);
+ TestLineCount(1, 1);
+ TestLine(1, 0, 0xfffffffffffffffaULL, 5, "line-file", 63351048);
+}
+
+// A function with more than one uncovered area should only be warned
+// about once.
+TEST_F(FuncLinePairing, WarnOnceFunc) {
+ PushLine(20, 1, "line-file-2", 262951329);
+ PushLine(11, 1, "line-file-1", 219964021);
+ EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
+
+ StartCU();
+ DefineFunction(&root_handler_, "function", 10, 11, NULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "function", 10, 11);
+ TestLineCount(0, 2);
+ TestLine(0, 0, 11, 1, "line-file-1", 219964021);
+ TestLine(0, 1, 20, 1, "line-file-2", 262951329);
+}
+
+// A line with more than one uncovered area should only be warned
+// about once.
+TEST_F(FuncLinePairing, WarnOnceLine) {
+ PushLine(10, 20, "filename1", 118581871);
+ EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return());
+
+ StartCU();
+ DefineFunction(&root_handler_, "function1", 11, 1, NULL);
+ DefineFunction(&root_handler_, "function2", 13, 1, NULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(2);
+ TestFunction(0, "function1", 11, 1);
+ TestLineCount(0, 1);
+ TestLine(0, 0, 11, 1, "filename1", 118581871);
+ TestFunction(1, "function2", 13, 1);
+ TestLineCount(1, 1);
+ TestLine(1, 0, 13, 1, "filename1", 118581871);
+}
+
+class CXXQualifiedNames: public CUFixtureBase,
+ public TestWithParam<DwarfTag> { };
+
+INSTANTIATE_TEST_CASE_P(VersusEnclosures, CXXQualifiedNames,
+ Values(dwarf2reader::DW_TAG_class_type,
+ dwarf2reader::DW_TAG_structure_type,
+ dwarf2reader::DW_TAG_union_type,
+ dwarf2reader::DW_TAG_namespace));
+
+TEST_P(CXXQualifiedNames, TwoFunctions) {
+ DwarfTag tag = GetParam();
+
+ SetLanguage(dwarf2reader::DW_LANG_C_plus_plus);
+ PushLine(10, 1, "filename1", 69819327);
+ PushLine(20, 1, "filename2", 95115701);
+
+ StartCU();
+ DIEHandler *enclosure_handler = StartNamedDIE(&root_handler_, tag,
+ "Enclosure");
+ EXPECT_TRUE(enclosure_handler != NULL);
+ DefineFunction(enclosure_handler, "func_B", 10, 1, NULL);
+ DefineFunction(enclosure_handler, "func_C", 20, 1, NULL);
+ enclosure_handler->Finish();
+ delete enclosure_handler;
+ root_handler_.Finish();
+
+ TestFunctionCount(2);
+ TestFunction(0, "Enclosure::func_B", 10, 1);
+ TestFunction(1, "Enclosure::func_C", 20, 1);
+}
+
+TEST_P(CXXQualifiedNames, FuncInEnclosureInNamespace) {
+ DwarfTag tag = GetParam();
+
+ SetLanguage(dwarf2reader::DW_LANG_C_plus_plus);
+ PushLine(10, 1, "line-file", 69819327);
+
+ StartCU();
+ DIEHandler *namespace_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace,
+ "Namespace");
+ EXPECT_TRUE(namespace_handler != NULL);
+ DIEHandler *enclosure_handler = StartNamedDIE(namespace_handler, tag,
+ "Enclosure");
+ EXPECT_TRUE(enclosure_handler != NULL);
+ DefineFunction(enclosure_handler, "function", 10, 1, NULL);
+ enclosure_handler->Finish();
+ delete enclosure_handler;
+ namespace_handler->Finish();
+ delete namespace_handler;
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "Namespace::Enclosure::function", 10, 1);
+}
+
+TEST_F(CXXQualifiedNames, FunctionInClassInStructInNamespace) {
+ SetLanguage(dwarf2reader::DW_LANG_C_plus_plus);
+ PushLine(10, 1, "filename1", 69819327);
+
+ StartCU();
+ DIEHandler *namespace_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace,
+ "namespace_A");
+ EXPECT_TRUE(namespace_handler != NULL);
+ DIEHandler *struct_handler
+ = StartNamedDIE(namespace_handler, dwarf2reader::DW_TAG_structure_type,
+ "struct_B");
+ EXPECT_TRUE(struct_handler != NULL);
+ DIEHandler *class_handler
+ = StartNamedDIE(struct_handler, dwarf2reader::DW_TAG_class_type,
+ "class_C");
+ DefineFunction(class_handler, "function_D", 10, 1, NULL);
+ class_handler->Finish();
+ delete class_handler;
+ struct_handler->Finish();
+ delete struct_handler;
+ namespace_handler->Finish();
+ delete namespace_handler;
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "namespace_A::struct_B::class_C::function_D", 10, 1);
+}
+
+struct LanguageAndQualifiedName {
+ dwarf2reader::DwarfLanguage language;
+ const char *name;
+};
+
+const LanguageAndQualifiedName LanguageAndQualifiedNameCases[] = {
+ { dwarf2reader::DW_LANG_none, "class_A::function_B" },
+ { dwarf2reader::DW_LANG_C, "class_A::function_B" },
+ { dwarf2reader::DW_LANG_C89, "class_A::function_B" },
+ { dwarf2reader::DW_LANG_C99, "class_A::function_B" },
+ { dwarf2reader::DW_LANG_C_plus_plus, "class_A::function_B" },
+ { dwarf2reader::DW_LANG_Java, "class_A.function_B" },
+ { dwarf2reader::DW_LANG_Cobol74, "class_A::function_B" },
+ { dwarf2reader::DW_LANG_Mips_Assembler, NULL }
+};
+
+class QualifiedForLanguage
+ : public CUFixtureBase,
+ public TestWithParam<LanguageAndQualifiedName> { };
+
+INSTANTIATE_TEST_CASE_P(LanguageAndQualifiedName, QualifiedForLanguage,
+ ValuesIn(LanguageAndQualifiedNameCases));
+
+TEST_P(QualifiedForLanguage, MemberFunction) {
+ const LanguageAndQualifiedName &param = GetParam();
+
+ PushLine(10, 1, "line-file", 212966758);
+ SetLanguage(param.language);
+
+ StartCU();
+ DIEHandler *class_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type,
+ "class_A");
+ DefineFunction(class_handler, "function_B", 10, 1, NULL);
+ class_handler->Finish();
+ delete class_handler;
+ root_handler_.Finish();
+
+ if (param.name) {
+ TestFunctionCount(1);
+ TestFunction(0, param.name, 10, 1);
+ } else {
+ TestFunctionCount(0);
+ }
+}
+
+TEST_P(QualifiedForLanguage, MemberFunctionSignedLanguage) {
+ const LanguageAndQualifiedName &param = GetParam();
+
+ PushLine(10, 1, "line-file", 212966758);
+ SetLanguage(param.language);
+ SetLanguageSigned(true);
+
+ StartCU();
+ DIEHandler *class_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type,
+ "class_A");
+ DefineFunction(class_handler, "function_B", 10, 1, NULL);
+ class_handler->Finish();
+ delete class_handler;
+ root_handler_.Finish();
+
+ if (param.name) {
+ TestFunctionCount(1);
+ TestFunction(0, param.name, 10, 1);
+ } else {
+ TestFunctionCount(0);
+ }
+}
+
+class Specifications: public CUFixtureBase, public Test { };
+
+TEST_F(Specifications, Function) {
+ PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
+
+ StartCU();
+ DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
+ dwarf2reader::DW_TAG_subprogram, "declaration-name", "");
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0xcd3c51b946fb1eeeLL, "",
+ 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "declaration-name",
+ 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
+}
+
+TEST_F(Specifications, MangledName) {
+ // Language defaults to C++, so no need to set it here.
+ PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
+
+ StartCU();
+ DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
+ dwarf2reader::DW_TAG_subprogram, "declaration-name",
+ "_ZN1C1fEi");
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0xcd3c51b946fb1eeeLL, "",
+ 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "C::f(int)",
+ 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
+}
+
+TEST_F(Specifications, MangledNameSwift) {
+ // Swift mangled names should pass through untouched.
+ SetLanguage(dwarf2reader::DW_LANG_Swift);
+ PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
+ StartCU();
+ const string kName = "_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si";
+ DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
+ dwarf2reader::DW_TAG_subprogram, "declaration-name",
+ kName);
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0xcd3c51b946fb1eeeLL, "",
+ 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, kName,
+ 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
+}
+
+TEST_F(Specifications, MangledNameRust) {
+ SetLanguage(dwarf2reader::DW_LANG_Rust);
+ PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
+
+ StartCU();
+ const string kName = "_ZN14rustc_demangle8demangle17h373defa94bffacdeE";
+ DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
+ dwarf2reader::DW_TAG_subprogram, "declaration-name",
+ kName);
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0xcd3c51b946fb1eeeLL, "",
+ 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0,
+#ifndef HAVE_RUST_DEMANGLE
+ // Rust mangled names should pass through untouched if not
+ // using rust-demangle.
+ kName,
+#else
+ // If rust-demangle is available this should be properly
+ // demangled.
+ "rustc_demangle::demangle",
+#endif
+ 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
+}
+
+TEST_F(Specifications, MemberFunction) {
+ PushLine(0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL, "line-file", 18116691);
+
+ StartCU();
+ DIEHandler *class_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A");
+ DeclarationDIE(class_handler, 0x7d83028c431406e8ULL,
+ dwarf2reader::DW_TAG_subprogram, "declaration-name", "");
+ class_handler->Finish();
+ delete class_handler;
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0x7d83028c431406e8ULL, "",
+ 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "class_A::declaration-name",
+ 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL);
+}
+
+// This case should gather the name from both the definition and the
+// declaration's parent.
+TEST_F(Specifications, FunctionDeclarationParent) {
+ PushLine(0x463c9ddf405be227ULL, 0x6a47774af5049680ULL, "line-file", 70254922);
+
+ StartCU();
+ {
+ DIEHandler *class_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type,
+ "class_A");
+ ASSERT_TRUE(class_handler != NULL);
+ DeclarationDIE(class_handler, 0x0e0e877c8404544aULL,
+ dwarf2reader::DW_TAG_subprogram, "declaration-name", "");
+ class_handler->Finish();
+ delete class_handler;
+ }
+
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0x0e0e877c8404544aULL, "definition-name",
+ 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL);
+
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "class_A::definition-name",
+ 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL);
+}
+
+// Named scopes should also gather enclosing name components from
+// their declarations.
+TEST_F(Specifications, NamedScopeDeclarationParent) {
+ PushLine(0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL, "line-file", 77392604);
+
+ StartCU();
+ {
+ DIEHandler *space_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace,
+ "space_A");
+ ASSERT_TRUE(space_handler != NULL);
+ DeclarationDIE(space_handler, 0x419bb1d12f9a73a2ULL,
+ dwarf2reader::DW_TAG_class_type, "class-declaration-name",
+ "");
+ space_handler->Finish();
+ delete space_handler;
+ }
+
+ {
+ DIEHandler *class_handler
+ = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type,
+ 0x419bb1d12f9a73a2ULL, "class-definition-name");
+ ASSERT_TRUE(class_handler != NULL);
+ DefineFunction(class_handler, "function",
+ 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL, NULL);
+ class_handler->Finish();
+ delete class_handler;
+ }
+
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "space_A::class-definition-name::function",
+ 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL);
+}
+
+// This test recreates bug 364.
+TEST_F(Specifications, InlineFunction) {
+ PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118);
+
+ StartCU();
+ DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
+ dwarf2reader::DW_TAG_subprogram, "inline-name", "");
+ AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL,
+ dwarf2reader::DW_INL_inlined, 0xcd3c51b946fb1eeeLL, "");
+ DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL,
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "inline-name",
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+}
+
+// An inline function in a namespace should correctly derive its
+// name from its abstract origin, and not just the namespace name.
+TEST_F(Specifications, InlineFunctionInNamespace) {
+ PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118);
+
+ StartCU();
+ DIEHandler* space_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace,
+ "Namespace");
+ ASSERT_TRUE(space_handler != NULL);
+ AbstractInstanceDIE(space_handler, 0x1e8dac5d507ed7abULL,
+ dwarf2reader::DW_INL_inlined, 0LL, "func-name");
+ DefineInlineInstanceDIE(space_handler, "", 0x1e8dac5d507ed7abULL,
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+ space_handler->Finish();
+ delete space_handler;
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "Namespace::func-name",
+ 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL);
+}
+
+// Check name construction for a long chain containing each combination of:
+// - struct, union, class, namespace
+// - direct and definition
+TEST_F(Specifications, LongChain) {
+ PushLine(0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL, "line-file", 21192926);
+ SetLanguage(dwarf2reader::DW_LANG_C_plus_plus);
+
+ StartCU();
+ // The structure we're building here is:
+ // space_A full definition
+ // space_B declaration
+ // space_B definition
+ // struct_C full definition
+ // struct_D declaration
+ // struct_D definition
+ // union_E full definition
+ // union_F declaration
+ // union_F definition
+ // class_G full definition
+ // class_H declaration
+ // class_H definition
+ // func_I declaration
+ // func_I definition
+ //
+ // So:
+ // - space_A, struct_C, union_E, and class_G don't use specifications;
+ // - space_B, struct_D, union_F, and class_H do.
+ // - func_I uses a specification.
+ //
+ // The full name for func_I is thus:
+ //
+ // space_A::space_B::struct_C::struct_D::union_E::union_F::
+ // class_G::class_H::func_I
+ {
+ DIEHandler *space_A_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace,
+ "space_A");
+ DeclarationDIE(space_A_handler, 0x2e111126496596e2ULL,
+ dwarf2reader::DW_TAG_namespace, "space_B", "");
+ space_A_handler->Finish();
+ delete space_A_handler;
+ }
+
+ {
+ DIEHandler *space_B_handler
+ = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace,
+ 0x2e111126496596e2ULL);
+ DIEHandler *struct_C_handler
+ = StartNamedDIE(space_B_handler, dwarf2reader::DW_TAG_structure_type,
+ "struct_C");
+ DeclarationDIE(struct_C_handler, 0x20cd423bf2a25a4cULL,
+ dwarf2reader::DW_TAG_structure_type, "struct_D", "");
+ struct_C_handler->Finish();
+ delete struct_C_handler;
+ space_B_handler->Finish();
+ delete space_B_handler;
+ }
+
+ {
+ DIEHandler *struct_D_handler
+ = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_structure_type,
+ 0x20cd423bf2a25a4cULL);
+ DIEHandler *union_E_handler
+ = StartNamedDIE(struct_D_handler, dwarf2reader::DW_TAG_union_type,
+ "union_E");
+ DeclarationDIE(union_E_handler, 0xe25c84805aa58c32ULL,
+ dwarf2reader::DW_TAG_union_type, "union_F", "");
+ union_E_handler->Finish();
+ delete union_E_handler;
+ struct_D_handler->Finish();
+ delete struct_D_handler;
+ }
+
+ {
+ DIEHandler *union_F_handler
+ = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_union_type,
+ 0xe25c84805aa58c32ULL);
+ DIEHandler *class_G_handler
+ = StartNamedDIE(union_F_handler, dwarf2reader::DW_TAG_class_type,
+ "class_G");
+ DeclarationDIE(class_G_handler, 0xb70d960dcc173b6eULL,
+ dwarf2reader::DW_TAG_class_type, "class_H", "");
+ class_G_handler->Finish();
+ delete class_G_handler;
+ union_F_handler->Finish();
+ delete union_F_handler;
+ }
+
+ {
+ DIEHandler *class_H_handler
+ = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type,
+ 0xb70d960dcc173b6eULL);
+ DeclarationDIE(class_H_handler, 0x27ff829e3bf69f37ULL,
+ dwarf2reader::DW_TAG_subprogram, "func_I", "");
+ class_H_handler->Finish();
+ delete class_H_handler;
+ }
+
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0x27ff829e3bf69f37ULL, "",
+ 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "space_A::space_B::struct_C::struct_D::union_E::union_F"
+ "::class_G::class_H::func_I",
+ 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL);
+}
+
+TEST_F(Specifications, InterCU) {
+ Module m("module-name", "module-os", "module-arch", "module-id");
+ DwarfCUToModule::FileContext fc("dwarf-filename", &m, true);
+ EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
+ MockLineToModuleHandler lr;
+ EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0);
+
+ // Kludge: satisfy reporter_'s expectation.
+ reporter_.SetCUName("compilation-unit-name");
+
+ // First CU. Declares class_A.
+ {
+ DwarfCUToModule root1_handler(&fc, &lr, &reporter_);
+ ASSERT_TRUE(root1_handler.StartCompilationUnit(0, 1, 2, 3, 3));
+ ASSERT_TRUE(root1_handler.StartRootDIE(1,
+ dwarf2reader::DW_TAG_compile_unit));
+ ProcessStrangeAttributes(&root1_handler);
+ ASSERT_TRUE(root1_handler.EndAttributes());
+ DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL,
+ dwarf2reader::DW_TAG_class_type, "class_A", "");
+ root1_handler.Finish();
+ }
+
+ // Second CU. Defines class_A, declares member_func_B.
+ {
+ DwarfCUToModule root2_handler(&fc, &lr, &reporter_);
+ ASSERT_TRUE(root2_handler.StartCompilationUnit(0, 1, 2, 3, 3));
+ ASSERT_TRUE(root2_handler.StartRootDIE(1,
+ dwarf2reader::DW_TAG_compile_unit));
+ ASSERT_TRUE(root2_handler.EndAttributes());
+ DIEHandler *class_A_handler
+ = StartSpecifiedDIE(&root2_handler, dwarf2reader::DW_TAG_class_type,
+ 0xb8fbfdd5f0b26fceULL);
+ DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL,
+ dwarf2reader::DW_TAG_subprogram, "member_func_B", "");
+ class_A_handler->Finish();
+ delete class_A_handler;
+ root2_handler.Finish();
+ }
+
+ // Third CU. Defines member_func_B.
+ {
+ DwarfCUToModule root3_handler(&fc, &lr, &reporter_);
+ ASSERT_TRUE(root3_handler.StartCompilationUnit(0, 1, 2, 3, 3));
+ ASSERT_TRUE(root3_handler.StartRootDIE(1,
+ dwarf2reader::DW_TAG_compile_unit));
+ ASSERT_TRUE(root3_handler.EndAttributes());
+ DefinitionDIE(&root3_handler, dwarf2reader::DW_TAG_subprogram,
+ 0xb01fef8b380bd1a2ULL, "",
+ 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL);
+ root3_handler.Finish();
+ }
+
+ vector<Module::Function *> functions;
+ m.GetFunctions(&functions, functions.end());
+ EXPECT_EQ(1U, functions.size());
+ EXPECT_STREQ("class_A::member_func_B", functions[0]->name.c_str());
+}
+
+TEST_F(Specifications, UnhandledInterCU) {
+ Module m("module-name", "module-os", "module-arch", "module-id");
+ DwarfCUToModule::FileContext fc("dwarf-filename", &m, false);
+ EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return());
+ MockLineToModuleHandler lr;
+ EXPECT_CALL(lr, ReadProgram(_,_,_,_)).Times(0);
+
+ // Kludge: satisfy reporter_'s expectation.
+ reporter_.SetCUName("compilation-unit-name");
+
+ // First CU. Declares class_A.
+ {
+ DwarfCUToModule root1_handler(&fc, &lr, &reporter_);
+ ASSERT_TRUE(root1_handler.StartCompilationUnit(0, 1, 2, 3, 3));
+ ASSERT_TRUE(root1_handler.StartRootDIE(1,
+ dwarf2reader::DW_TAG_compile_unit));
+ ProcessStrangeAttributes(&root1_handler);
+ ASSERT_TRUE(root1_handler.EndAttributes());
+ DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL,
+ dwarf2reader::DW_TAG_class_type, "class_A", "");
+ root1_handler.Finish();
+ }
+
+ // Second CU. Defines class_A, declares member_func_B.
+ {
+ DwarfCUToModule root2_handler(&fc, &lr, &reporter_);
+ ASSERT_TRUE(root2_handler.StartCompilationUnit(0, 1, 2, 3, 3));
+ ASSERT_TRUE(root2_handler.StartRootDIE(1,
+ dwarf2reader::DW_TAG_compile_unit));
+ ASSERT_TRUE(root2_handler.EndAttributes());
+ EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(1);
+ DIEHandler *class_A_handler
+ = StartSpecifiedDIE(&root2_handler, dwarf2reader::DW_TAG_class_type,
+ 0xb8fbfdd5f0b26fceULL);
+ DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL,
+ dwarf2reader::DW_TAG_subprogram, "member_func_B", "");
+ class_A_handler->Finish();
+ delete class_A_handler;
+ root2_handler.Finish();
+ }
+
+ // Third CU. Defines member_func_B.
+ {
+ DwarfCUToModule root3_handler(&fc, &lr, &reporter_);
+ ASSERT_TRUE(root3_handler.StartCompilationUnit(0, 1, 2, 3, 3));
+ ASSERT_TRUE(root3_handler.StartRootDIE(1,
+ dwarf2reader::DW_TAG_compile_unit));
+ ASSERT_TRUE(root3_handler.EndAttributes());
+ EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(1);
+ EXPECT_CALL(reporter_, UnnamedFunction(_)).Times(1);
+ DefinitionDIE(&root3_handler, dwarf2reader::DW_TAG_subprogram,
+ 0xb01fef8b380bd1a2ULL, "",
+ 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL);
+ root3_handler.Finish();
+ }
+}
+
+TEST_F(Specifications, BadOffset) {
+ PushLine(0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL, "line-file", 56636272);
+ EXPECT_CALL(reporter_, UnknownSpecification(_, 0x2be953efa6f9a996ULL))
+ .WillOnce(Return());
+
+ StartCU();
+ DeclarationDIE(&root_handler_, 0xefd7f7752c27b7e4ULL,
+ dwarf2reader::DW_TAG_subprogram, "", "");
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0x2be953efa6f9a996ULL, "function",
+ 0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL);
+ root_handler_.Finish();
+}
+
+TEST_F(Specifications, FunctionDefinitionHasOwnName) {
+ PushLine(0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL, "line-file", 56792403);
+
+ StartCU();
+ DeclarationDIE(&root_handler_, 0xc34ff4786cae78bdULL,
+ dwarf2reader::DW_TAG_subprogram, "declaration-name", "");
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0xc34ff4786cae78bdULL, "definition-name",
+ 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL);
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "definition-name",
+ 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL);
+}
+
+TEST_F(Specifications, ClassDefinitionHasOwnName) {
+ PushLine(0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL, "line-file", 57119241);
+
+ StartCU();
+ DeclarationDIE(&root_handler_, 0xd0fe467ec2f1a58cULL,
+ dwarf2reader::DW_TAG_class_type, "class-declaration-name", "");
+
+ dwarf2reader::DIEHandler *class_definition
+ = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type,
+ 0xd0fe467ec2f1a58cULL, "class-definition-name");
+ ASSERT_TRUE(class_definition);
+ DeclarationDIE(class_definition, 0x6d028229c15623dbULL,
+ dwarf2reader::DW_TAG_subprogram,
+ "function-declaration-name", "");
+ class_definition->Finish();
+ delete class_definition;
+
+ DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
+ 0x6d028229c15623dbULL, "function-definition-name",
+ 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL);
+
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "class-definition-name::function-definition-name",
+ 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL);
+}
+
+// DIEs that cite a specification should prefer the specification's
+// parents over their own when choosing qualified names. In this test,
+// we take the name from our definition but the enclosing scope name
+// from our declaration. I don't see why they'd ever be different, but
+// we want to verify what DwarfCUToModule is looking at.
+TEST_F(Specifications, PreferSpecificationParents) {
+ PushLine(0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL, "line-file", 79488694);
+
+ StartCU();
+ {
+ dwarf2reader::DIEHandler *declaration_class_handler =
+ StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type,
+ "declaration-class");
+ DeclarationDIE(declaration_class_handler, 0x9ddb35517455ef7aULL,
+ dwarf2reader::DW_TAG_subprogram, "function-declaration",
+ "");
+ declaration_class_handler->Finish();
+ delete declaration_class_handler;
+ }
+ {
+ dwarf2reader::DIEHandler *definition_class_handler
+ = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type,
+ "definition-class");
+ DefinitionDIE(definition_class_handler, dwarf2reader::DW_TAG_subprogram,
+ 0x9ddb35517455ef7aULL, "function-definition",
+ 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL);
+ definition_class_handler->Finish();
+ delete definition_class_handler;
+ }
+ root_handler_.Finish();
+
+ TestFunctionCount(1);
+ TestFunction(0, "declaration-class::function-definition",
+ 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL);
+}
+
+class CUErrors: public CUFixtureBase, public Test { };
+
+TEST_F(CUErrors, BadStmtList) {
+ EXPECT_CALL(reporter_, BadLineInfoOffset(dummy_line_size_ + 10)).Times(1);
+
+ ASSERT_TRUE(root_handler_
+ .StartCompilationUnit(0xc591d5b037543d7cULL, 0x11, 0xcd,
+ 0x2d7d19546cf6590cULL, 3));
+ ASSERT_TRUE(root_handler_.StartRootDIE(0xae789dc102cfca54ULL,
+ dwarf2reader::DW_TAG_compile_unit));
+ root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name,
+ dwarf2reader::DW_FORM_strp,
+ "compilation-unit-name");
+ root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list,
+ dwarf2reader::DW_FORM_ref4,
+ dummy_line_size_ + 10);
+ root_handler_.EndAttributes();
+ root_handler_.Finish();
+}
+
+TEST_F(CUErrors, NoLineSection) {
+ EXPECT_CALL(reporter_, MissingSection(".debug_line")).Times(1);
+ PushLine(0x88507fb678052611ULL, 0x42c8e9de6bbaa0faULL, "line-file", 64472290);
+ // Delete the entry for .debug_line added by the fixture class's constructor.
+ file_context_.ClearSectionMapForTest();
+
+ StartCU();
+ root_handler_.Finish();
+}
+
+TEST_F(CUErrors, BadDwarfVersion1) {
+ // Kludge: satisfy reporter_'s expectation.
+ reporter_.SetCUName("compilation-unit-name");
+
+ ASSERT_FALSE(root_handler_
+ .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90,
+ 0xc9de224ccb99ac3eULL, 1));
+}
+
+TEST_F(CUErrors, GoodDwarfVersion2) {
+ // Kludge: satisfy reporter_'s expectation.
+ reporter_.SetCUName("compilation-unit-name");
+
+ ASSERT_TRUE(root_handler_
+ .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90,
+ 0xc9de224ccb99ac3eULL, 2));
+}
+
+TEST_F(CUErrors, GoodDwarfVersion3) {
+ // Kludge: satisfy reporter_'s expectation.
+ reporter_.SetCUName("compilation-unit-name");
+
+ ASSERT_TRUE(root_handler_
+ .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90,
+ 0xc9de224ccb99ac3eULL, 3));
+}
+
+TEST_F(CUErrors, BadCURootDIETag) {
+ // Kludge: satisfy reporter_'s expectation.
+ reporter_.SetCUName("compilation-unit-name");
+
+ ASSERT_TRUE(root_handler_
+ .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90,
+ 0xc9de224ccb99ac3eULL, 3));
+
+ ASSERT_FALSE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL,
+ dwarf2reader::DW_TAG_subprogram));
+}
+
+// Tests for DwarfCUToModule::Reporter. These just produce (or fail to
+// produce) output, so their results need to be checked by hand.
+struct Reporter: public Test {
+ Reporter()
+ : reporter("filename", 0x123456789abcdef0ULL),
+ function("function name", 0x19c45c30770c1eb0ULL),
+ file("source file name") {
+ reporter.SetCUName("compilation-unit-name");
+
+ function.size = 0x89808a5bdfa0a6a3ULL;
+ function.parameter_size = 0x6a329f18683dcd51ULL;
+
+ line.address = 0x3606ac6267aebeccULL;
+ line.size = 0x5de482229f32556aULL;
+ line.file = &file;
+ line.number = 93400201;
+ }
+
+ DwarfCUToModule::WarningReporter reporter;
+ Module::Function function;
+ Module::File file;
+ Module::Line line;
+};
+
+TEST_F(Reporter, UnknownSpecification) {
+ reporter.UnknownSpecification(0x123456789abcdef1ULL, 0x323456789abcdef2ULL);
+}
+
+TEST_F(Reporter, UnknownAbstractOrigin) {
+ reporter.UnknownAbstractOrigin(0x123456789abcdef1ULL, 0x323456789abcdef2ULL);
+}
+
+TEST_F(Reporter, MissingSection) {
+ reporter.MissingSection("section name");
+}
+
+TEST_F(Reporter, BadLineInfoOffset) {
+ reporter.BadLineInfoOffset(0x123456789abcdef1ULL);
+}
+
+TEST_F(Reporter, UncoveredFunctionDisabled) {
+ reporter.UncoveredFunction(function);
+ EXPECT_FALSE(reporter.uncovered_warnings_enabled());
+}
+
+TEST_F(Reporter, UncoveredFunctionEnabled) {
+ reporter.set_uncovered_warnings_enabled(true);
+ reporter.UncoveredFunction(function);
+ EXPECT_TRUE(reporter.uncovered_warnings_enabled());
+}
+
+TEST_F(Reporter, UncoveredLineDisabled) {
+ reporter.UncoveredLine(line);
+ EXPECT_FALSE(reporter.uncovered_warnings_enabled());
+}
+
+TEST_F(Reporter, UncoveredLineEnabled) {
+ reporter.set_uncovered_warnings_enabled(true);
+ reporter.UncoveredLine(line);
+ EXPECT_TRUE(reporter.uncovered_warnings_enabled());
+}
+
+TEST_F(Reporter, UnnamedFunction) {
+ reporter.UnnamedFunction(0x90c0baff9dedb2d9ULL);
+}
+
+// Would be nice to also test:
+// - overlapping lines, functions
diff --git a/3rdParty/Breakpad/src/common/dwarf_line_to_module.cc b/3rdParty/Breakpad/src/common/dwarf_line_to_module.cc
index 962848d..258b0b6 100644
--- a/3rdParty/Breakpad/src/common/dwarf_line_to_module.cc
+++ b/3rdParty/Breakpad/src/common/dwarf_line_to_module.cc
@@ -48,13 +48,17 @@ static bool PathIsAbsolute(const string &path) {
return (path.size() >= 1 && path[0] == '/');
}
+static bool HasTrailingSlash(const string &path) {
+ return (path.size() >= 1 && path[path.size() - 1] == '/');
+}
+
// If PATH is an absolute path, return PATH. If PATH is a relative path,
// treat it as relative to BASE and return the combined path.
static string ExpandPath(const string &path,
const string &base) {
- if (PathIsAbsolute(path))
+ if (PathIsAbsolute(path) || base.empty())
return path;
- return base + "/" + path;
+ return base + (HasTrailingSlash(base) ? "" : "/") + path;
}
namespace google_breakpad {
@@ -63,7 +67,7 @@ void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) {
// Directory number zero is reserved to mean the compilation
// directory. Silently ignore attempts to redefine it.
if (dir_num != 0)
- directories_[dir_num] = name;
+ directories_[dir_num] = ExpandPath(name, compilation_dir_);
}
void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
@@ -74,25 +78,26 @@ void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
else if (file_num > highest_file_number_)
highest_file_number_ = file_num;
- string full_name;
- if (dir_num != 0) {
+ string dir_name;
+ if (dir_num == 0) {
+ // Directory number zero is the compilation directory, and is stored as
+ // an attribute on the compilation unit, rather than in the program table.
+ dir_name = compilation_dir_;
+ } else {
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
if (directory_it != directories_.end()) {
- full_name = ExpandPath(name, directory_it->second);
+ dir_name = directory_it->second;
} else {
if (!warned_bad_directory_number_) {
fprintf(stderr, "warning: DWARF line number data refers to undefined"
" directory numbers\n");
warned_bad_directory_number_ = true;
}
- full_name = name; // just treat name as relative
}
- } else {
- // Directory number zero is the compilation directory; we just report
- // relative paths in that case.
- full_name = name;
}
+ string full_name = ExpandPath(name, dir_name);
+
// Find a Module::File object of the given name, and add it to the
// file table.
files_[file_num] = module_->FindFile(full_name);
diff --git a/3rdParty/Breakpad/src/common/dwarf_line_to_module.h b/3rdParty/Breakpad/src/common/dwarf_line_to_module.h
index 9382e40..1fdd4cb 100644
--- a/3rdParty/Breakpad/src/common/dwarf_line_to_module.h
+++ b/3rdParty/Breakpad/src/common/dwarf_line_to_module.h
@@ -120,8 +120,10 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
// end of the address space, we clip it. It's up to our client to
// sort out which lines belong to which functions; we don't add them
// to any particular function in MODULE ourselves.
- DwarfLineToModule(Module *module, vector<Module::Line> *lines)
+ DwarfLineToModule(Module *module, const string& compilation_dir,
+ vector<Module::Line> *lines)
: module_(module),
+ compilation_dir_(compilation_dir),
lines_(lines),
highest_file_number_(-1),
omitted_line_end_(0),
@@ -146,6 +148,10 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
// client.
Module *module_;
+ // The compilation directory for the current compilation unit whose
+ // lines are being accumulated.
+ string compilation_dir_;
+
// The vector of lines we're accumulating. Owned by our client.
//
// In a Module, as in a breakpad symbol file, lines belong to
diff --git a/3rdParty/Breakpad/src/common/dwarf_line_to_module_unittest.cc b/3rdParty/Breakpad/src/common/dwarf_line_to_module_unittest.cc
new file mode 100644
index 0000000..7c0fcfd
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/dwarf_line_to_module_unittest.cc
@@ -0,0 +1,391 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule.
+
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/dwarf_line_to_module.h"
+
+using std::vector;
+
+using google_breakpad::DwarfLineToModule;
+using google_breakpad::Module;
+using google_breakpad::Module;
+
+TEST(SimpleModule, One) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineFile("file1", 0x30bf0f27, 0, 0, 0);
+ h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
+ 0x4c090cbf, 0x1cf9fe0d);
+
+ vector<Module::File *> files;
+ m.GetFiles(&files);
+ EXPECT_EQ(1U, files.size());
+ EXPECT_STREQ("/file1", files[0]->name.c_str());
+
+ EXPECT_EQ(1U, lines.size());
+ EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address);
+ EXPECT_EQ(0x63c9a14cf556712bULL, lines[0].size);
+ EXPECT_TRUE(lines[0].file == files[0]);
+ EXPECT_EQ(0x4c090cbf, lines[0].number);
+}
+
+TEST(SimpleModule, Many) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineDir("directory1", 0x838299ab);
+ h.DefineDir("directory2", 0xf85de023);
+ h.DefineFile("file1", 0x2b80377a, 0x838299ab, 0, 0);
+ h.DefineFile("file1", 0x63beb4a4, 0xf85de023, 0, 0);
+ h.DefineFile("file2", 0x1d161d56, 0x838299ab, 0, 0);
+ h.DefineFile("file2", 0x1e7a667c, 0xf85de023, 0, 0);
+ h.AddLine(0x69900c5d553b7274ULL, 0x90fded183f0d0d3cULL, 0x2b80377a,
+ 0x15b0f0a9U, 0x3ff5abd6U);
+ h.AddLine(0x45811219a39b7101ULL, 0x25a5e6a924afc41fULL, 0x63beb4a4,
+ 0x4d259ce9U, 0x41c5ee32U);
+ h.AddLine(0xfa90514c1dc9704bULL, 0x0063efeabc02f313ULL, 0x1d161d56,
+ 0x1ee9fa4fU, 0xbf70e46aU);
+ h.AddLine(0x556b55fb6a647b10ULL, 0x3f3089ca2bfd80f5ULL, 0x1e7a667c,
+ 0x77fc280eU, 0x2c4a728cU);
+ h.DefineFile("file3", -1, 0, 0, 0);
+ h.AddLine(0xe2d72a37f8d9403aULL, 0x034dfab5b0d4d236ULL, 0x63beb4a5,
+ 0x75047044U, 0xb6a0016cU);
+
+ vector<Module::File *> files;
+ m.GetFiles(&files);
+ ASSERT_EQ(5U, files.size());
+ EXPECT_STREQ("/directory1/file1", files[0]->name.c_str());
+ EXPECT_STREQ("/directory1/file2", files[1]->name.c_str());
+ EXPECT_STREQ("/directory2/file1", files[2]->name.c_str());
+ EXPECT_STREQ("/directory2/file2", files[3]->name.c_str());
+ EXPECT_STREQ("/file3", files[4]->name.c_str());
+
+ ASSERT_EQ(5U, lines.size());
+
+ EXPECT_EQ(0x69900c5d553b7274ULL, lines[0].address);
+ EXPECT_EQ(0x90fded183f0d0d3cULL, lines[0].size);
+ EXPECT_TRUE(lines[0].file == files[0]);
+ EXPECT_EQ(0x15b0f0a9, lines[0].number);
+
+ EXPECT_EQ(0x45811219a39b7101ULL, lines[1].address);
+ EXPECT_EQ(0x25a5e6a924afc41fULL, lines[1].size);
+ EXPECT_TRUE(lines[1].file == files[2]);
+ EXPECT_EQ(0x4d259ce9, lines[1].number);
+
+ EXPECT_EQ(0xfa90514c1dc9704bULL, lines[2].address);
+ EXPECT_EQ(0x0063efeabc02f313ULL, lines[2].size);
+ EXPECT_TRUE(lines[2].file == files[1]);
+ EXPECT_EQ(0x1ee9fa4f, lines[2].number);
+
+ EXPECT_EQ(0x556b55fb6a647b10ULL, lines[3].address);
+ EXPECT_EQ(0x3f3089ca2bfd80f5ULL, lines[3].size);
+ EXPECT_TRUE(lines[3].file == files[3]);
+ EXPECT_EQ(0x77fc280e, lines[3].number);
+
+ EXPECT_EQ(0xe2d72a37f8d9403aULL, lines[4].address);
+ EXPECT_EQ(0x034dfab5b0d4d236ULL, lines[4].size);
+ EXPECT_TRUE(lines[4].file == files[4]);
+ EXPECT_EQ(0x75047044, lines[4].number);
+}
+
+TEST(Filenames, Absolute) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineDir("directory1", 1);
+ h.DefineFile("/absolute", 1, 1, 0, 0);
+
+ h.AddLine(1, 1, 1, 0, 0);
+
+ vector<Module::File *> files;
+ m.GetFiles(&files);
+ ASSERT_EQ(1U, files.size());
+ EXPECT_STREQ("/absolute", files[0]->name.c_str());
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_TRUE(lines[0].file == files[0]);
+}
+
+TEST(Filenames, Relative) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineDir("directory1", 1);
+ h.DefineFile("relative", 1, 1, 0, 0);
+
+ h.AddLine(1, 1, 1, 0, 0);
+
+ vector<Module::File *> files;
+ m.GetFiles(&files);
+ ASSERT_EQ(1U, files.size());
+ EXPECT_STREQ("/directory1/relative", files[0]->name.c_str());
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_TRUE(lines[0].file == files[0]);
+}
+
+TEST(Filenames, StrangeFile) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineDir("directory1", 1);
+ h.DefineFile("", 1, 1, 0, 0);
+ h.AddLine(1, 1, 1, 0, 0);
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_STREQ("/directory1/", lines[0].file->name.c_str());
+}
+
+TEST(Filenames, StrangeDirectory) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineDir("", 1);
+ h.DefineFile("file1", 1, 1, 0, 0);
+ h.AddLine(1, 1, 1, 0, 0);
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_STREQ("/file1", lines[0].file->name.c_str());
+}
+
+TEST(Filenames, StrangeDirectoryAndFile) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineDir("", 1);
+ h.DefineFile("", 1, 1, 0, 0);
+ h.AddLine(1, 1, 1, 0, 0);
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_STREQ("/", lines[0].file->name.c_str());
+}
+
+// We should use the compilation directory when encountering a file for
+// directory number zero.
+TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "src/build", &lines);
+
+ h.DefineDir("Dir", 1);
+ h.DefineFile("File", 1, 0, 0, 0);
+
+ h.AddLine(1, 1, 1, 0, 0);
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_STREQ("src/build/File", lines[0].file->name.c_str());
+}
+
+// We should treat non-absolute directories as relative to the compilation
+// directory.
+TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "src/build", &lines);
+
+ h.DefineDir("Dir", 1);
+ h.DefineFile("File", 1, 1, 0, 0);
+
+ h.AddLine(1, 1, 1, 0, 0);
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_STREQ("src/build/Dir/File", lines[0].file->name.c_str());
+}
+
+// We should treat absolute directories as absolute, and not relative to
+// the compilation dir.
+TEST(Filenames, IncludeDirectoryAbsolute) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "src/build", &lines);
+
+ h.DefineDir("/Dir", 1);
+ h.DefineFile("File", 1, 1, 0, 0);
+
+ h.AddLine(1, 1, 1, 0, 0);
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_STREQ("/Dir/File", lines[0].file->name.c_str());
+}
+
+// We should silently ignore attempts to define directory number zero,
+// since that is always the compilation directory.
+TEST(ModuleErrors, DirectoryZero) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineDir("directory0", 0); // should be ignored
+ h.DefineFile("relative", 1, 0, 0, 0);
+
+ h.AddLine(1, 1, 1, 0, 0);
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_STREQ("/relative", lines[0].file->name.c_str());
+}
+
+// We should refuse to add lines with bogus file numbers. We should
+// produce only one warning, however.
+TEST(ModuleErrors, BadFileNumber) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineFile("relative", 1, 0, 0, 0);
+ h.AddLine(1, 1, 2, 0, 0); // bad file number
+ h.AddLine(2, 1, 2, 0, 0); // bad file number (no duplicate warning)
+
+ EXPECT_EQ(0U, lines.size());
+}
+
+// We should treat files with bogus directory numbers as relative to
+// the compilation unit.
+TEST(ModuleErrors, BadDirectoryNumber) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineDir("directory1", 1);
+ h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number
+ h.DefineFile("baddirnumber2", 2, 2, 0, 0); // bad dir number (no warning)
+ h.AddLine(1, 1, 1, 0, 0);
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_STREQ("baddirnumber1", lines[0].file->name.c_str());
+}
+
+// We promise not to report empty lines.
+TEST(ModuleErrors, EmptyLine) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineFile("filename1", 1, 0, 0, 0);
+ h.AddLine(1, 0, 1, 0, 0);
+
+ ASSERT_EQ(0U, lines.size());
+}
+
+// We are supposed to clip lines that extend beyond the end of the
+// address space.
+TEST(ModuleErrors, BigLine) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineFile("filename1", 1, 0, 0, 0);
+ h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0);
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_EQ(1U, lines[0].size);
+}
+
+// The 'Omitted' tests verify that we correctly omit line information
+// for code in sections that the linker has dropped. See "GNU
+// toolchain omitted sections support" at the top of the
+// DwarfLineToModule class.
+
+TEST(Omitted, DroppedThenGood) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineFile("filename1", 1, 0, 0, 0);
+ h.AddLine(0, 10, 1, 83816211, 0); // should be omitted
+ h.AddLine(20, 10, 1, 13059195, 0); // should be recorded
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_EQ(13059195, lines[0].number);
+}
+
+TEST(Omitted, GoodThenDropped) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineFile("filename1", 1, 0, 0, 0);
+ h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded
+ h.AddLine(0, 10, 1, 44793413, 0); // should be omitted
+
+ ASSERT_EQ(1U, lines.size());
+ EXPECT_EQ(41454594, lines[0].number);
+}
+
+TEST(Omitted, Mix1) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineFile("filename1", 1, 0, 0, 0);
+ h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded
+ h.AddLine(0xdfb5a72d, 10, 1, 39847385, 0); // should be recorded
+ h.AddLine(0, 0x78, 1, 23053829, 0); // should be omitted
+ h.AddLine(0x78, 0x6a, 1, 65317783, 0); // should be omitted
+ h.AddLine(0x78 + 0x6a, 0x2a, 1, 77601423, 0); // should be omitted
+ h.AddLine(0x9fe0cea5, 10, 1, 91806582, 0); // should be recorded
+ h.AddLine(0x7e41a109, 10, 1, 56169221, 0); // should be recorded
+
+ ASSERT_EQ(4U, lines.size());
+ EXPECT_EQ(58932642, lines[0].number);
+ EXPECT_EQ(39847385, lines[1].number);
+ EXPECT_EQ(91806582, lines[2].number);
+ EXPECT_EQ(56169221, lines[3].number);
+}
+
+TEST(Omitted, Mix2) {
+ Module m("name", "os", "architecture", "id");
+ vector<Module::Line> lines;
+ DwarfLineToModule h(&m, "/", &lines);
+
+ h.DefineFile("filename1", 1, 0, 0, 0);
+ h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted
+ h.AddLine(0xf2, 0xb9, 1, 78958222, 0); // should be omitted
+ h.AddLine(0xf2 + 0xb9, 0xf7, 1, 64861892, 0); // should be omitted
+ h.AddLine(0x4e4d271e, 9, 1, 67355743, 0); // should be recorded
+ h.AddLine(0xdfb5a72d, 30, 1, 23365776, 0); // should be recorded
+ h.AddLine(0, 0x64, 1, 76196762, 0); // should be omitted
+ h.AddLine(0x64, 0x33, 1, 71066611, 0); // should be omitted
+ h.AddLine(0x64 + 0x33, 0xe3, 1, 61749337, 0); // should be omitted
+
+ ASSERT_EQ(2U, lines.size());
+ EXPECT_EQ(67355743, lines[0].number);
+ EXPECT_EQ(23365776, lines[1].number);
+}
diff --git a/3rdParty/Breakpad/src/common/language.cc b/3rdParty/Breakpad/src/common/language.cc
index c2fd81f..978fb85 100644
--- a/3rdParty/Breakpad/src/common/language.cc
+++ b/3rdParty/Breakpad/src/common/language.cc
@@ -34,18 +34,70 @@
#include "common/language.h"
+#include <stdlib.h>
+
+#if !defined(__ANDROID__)
+#include <cxxabi.h>
+#endif
+
+#if defined(HAVE_RUST_DEMANGLE)
+#include <rust_demangle.h>
+#endif
+
+#include <limits>
+
+namespace {
+
+string MakeQualifiedNameWithSeparator(const string& parent_name,
+ const char* separator,
+ const string& name) {
+ if (parent_name.empty()) {
+ return name;
+ }
+
+ return parent_name + separator + name;
+}
+
+} // namespace
+
namespace google_breakpad {
// C++ language-specific operations.
class CPPLanguage: public Language {
public:
CPPLanguage() {}
+
string MakeQualifiedName(const string &parent_name,
const string &name) const {
- if (parent_name.empty())
- return name;
- else
- return parent_name + "::" + name;
+ return MakeQualifiedNameWithSeparator(parent_name, "::", name);
+ }
+
+ virtual DemangleResult DemangleName(const string& mangled,
+ string* demangled) const {
+#if defined(__ANDROID__)
+ // Android NDK doesn't provide abi::__cxa_demangle.
+ demangled->clear();
+ return kDontDemangle;
+#else
+ int status;
+ char* demangled_c =
+ abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
+
+ DemangleResult result;
+ if (status == 0) {
+ result = kDemangleSuccess;
+ demangled->assign(demangled_c);
+ } else {
+ result = kDemangleFailure;
+ demangled->clear();
+ }
+
+ if (demangled_c) {
+ free(reinterpret_cast<void*>(demangled_c));
+ }
+
+ return result;
+#endif
}
};
@@ -54,19 +106,79 @@ CPPLanguage CPPLanguageSingleton;
// Java language-specific operations.
class JavaLanguage: public Language {
public:
+ JavaLanguage() {}
+
string MakeQualifiedName(const string &parent_name,
const string &name) const {
- if (parent_name.empty())
- return name;
- else
- return parent_name + "." + name;
+ return MakeQualifiedNameWithSeparator(parent_name, ".", name);
}
};
JavaLanguage JavaLanguageSingleton;
+// Swift language-specific operations.
+class SwiftLanguage: public Language {
+ public:
+ SwiftLanguage() {}
+
+ string MakeQualifiedName(const string &parent_name,
+ const string &name) const {
+ return MakeQualifiedNameWithSeparator(parent_name, ".", name);
+ }
+
+ virtual DemangleResult DemangleName(const string& mangled,
+ string* demangled) const {
+ // There is no programmatic interface to a Swift demangler. Pass through the
+ // mangled form because it encodes more information than the qualified name
+ // that would have been built by MakeQualifiedName(). The output can be
+ // post-processed by xcrun swift-demangle to transform mangled Swift names
+ // into something more readable.
+ demangled->assign(mangled);
+ return kDemangleSuccess;
+ }
+};
+
+SwiftLanguage SwiftLanguageSingleton;
+
+// Rust language-specific operations.
+class RustLanguage: public Language {
+ public:
+ RustLanguage() {}
+
+ string MakeQualifiedName(const string &parent_name,
+ const string &name) const {
+ return MakeQualifiedNameWithSeparator(parent_name, ".", name);
+ }
+
+ virtual DemangleResult DemangleName(const string& mangled,
+ string* demangled) const {
+ // Rust names use GCC C++ name mangling, but demangling them with
+ // abi_demangle doesn't produce stellar results due to them having
+ // another layer of encoding.
+ // If callers provide rustc-demangle, use that.
+#if defined(HAVE_RUST_DEMANGLE)
+ char* rust_demangled = rust_demangle(mangled.c_str());
+ if (rust_demangled == nullptr) {
+ return kDemangleFailure;
+ }
+ demangled->assign(rust_demangled);
+ free_rust_demangled_name(rust_demangled);
+#else
+ // Otherwise, pass through the mangled name so callers can demangle
+ // after the fact.
+ demangled->assign(mangled);
+#endif
+ return kDemangleSuccess;
+ }
+};
+
+RustLanguage RustLanguageSingleton;
+
// Assembler language-specific operations.
class AssemblerLanguage: public Language {
+ public:
+ AssemblerLanguage() {}
+
bool HasFunctions() const { return false; }
string MakeQualifiedName(const string &parent_name,
const string &name) const {
@@ -78,6 +190,8 @@ AssemblerLanguage AssemblerLanguageSingleton;
const Language * const Language::CPlusPlus = &CPPLanguageSingleton;
const Language * const Language::Java = &JavaLanguageSingleton;
+const Language * const Language::Swift = &SwiftLanguageSingleton;
+const Language * const Language::Rust = &RustLanguageSingleton;
const Language * const Language::Assembler = &AssemblerLanguageSingleton;
} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/language.h b/3rdParty/Breakpad/src/common/language.h
index bbe3033..2d2dbcd 100644
--- a/3rdParty/Breakpad/src/common/language.h
+++ b/3rdParty/Breakpad/src/common/language.h
@@ -77,9 +77,26 @@ class Language {
virtual string MakeQualifiedName (const string &parent_name,
const string &name) const = 0;
+ enum DemangleResult {
+ // Demangling was not performed because it’s not appropriate to attempt.
+ kDontDemangle = -1,
+
+ kDemangleSuccess,
+ kDemangleFailure,
+ };
+
+ // Wraps abi::__cxa_demangle() or similar for languages where appropriate.
+ virtual DemangleResult DemangleName(const string& mangled,
+ string* demangled) const {
+ demangled->clear();
+ return kDontDemangle;
+ }
+
// Instances for specific languages.
static const Language * const CPlusPlus,
* const Java,
+ * const Swift,
+ * const Rust,
* const Assembler;
};
diff --git a/3rdParty/Breakpad/src/common/linux/linux_libc_support.h b/3rdParty/Breakpad/src/common/linux/linux_libc_support.h
index b2f47af..ec5a8d6 100644
--- a/3rdParty/Breakpad/src/common/linux/linux_libc_support.h
+++ b/3rdParty/Breakpad/src/common/linux/linux_libc_support.h
@@ -52,17 +52,16 @@ extern int my_strncmp(const char* a, const char* b, size_t len);
// Return true iff successful.
extern bool my_strtoui(int* result, const char* s);
-// Return the length of the given, non-negative integer when expressed in base
-// 10.
-extern unsigned my_int_len(intmax_t i);
+// Return the length of the given unsigned integer when expressed in base 10.
+extern unsigned my_uint_len(uintmax_t i);
-// Convert a non-negative integer to a string
+// Convert an unsigned integer to a string
// output: (output) the resulting string is written here. This buffer must be
-// large enough to hold the resulting string. Call |my_int_len| to get the
+// large enough to hold the resulting string. Call |my_uint_len| to get the
// required length.
-// i: the non-negative integer to serialise.
-// i_len: the length of the integer in base 10 (see |my_int_len|).
-extern void my_itos(char* output, intmax_t i, unsigned i_len);
+// i: the unsigned integer to serialise.
+// i_len: the length of the integer in base 10 (see |my_uint_len|).
+extern void my_uitos(char* output, uintmax_t i, unsigned i_len);
extern const char* my_strchr(const char* haystack, char needle);
@@ -78,6 +77,8 @@ extern const char* my_read_decimal_ptr(uintptr_t* result, const char* s);
extern void my_memset(void* ip, char c, size_t len);
+extern void* my_memchr(const void* src, int c, size_t len);
+
// The following are considered safe to use in a compromised environment.
// Besides, this gives the compiler an opportunity to optimize their calls.
#define my_memcpy memcpy
diff --git a/3rdParty/Breakpad/src/common/mac/Breakpad.xcconfig b/3rdParty/Breakpad/src/common/mac/Breakpad.xcconfig
new file mode 100644
index 0000000..f091369
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/Breakpad.xcconfig
@@ -0,0 +1,52 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+GCC_C_LANGUAGE_STANDARD = c99
+
+GCC_WARN_CHECK_SWITCH_STATEMENTS = YES
+// TODO(nealsid): Get the code so we can turn on the 64_TO_32 warning.
+GCC_WARN_64_TO_32_BIT_CONVERSION = NO
+GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
+GCC_WARN_ABOUT_RETURN_TYPE = YES
+GCC_WARN_MISSING_PARENTHESES = YES
+
+// Once https://bugs.chromium.org/p/google-breakpad/issues/detail?id=697
+// is fixed this should be reenabled.
+//GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
+GCC_WARN_ABOUT_MISSING_NEWLINE = YES
+GCC_WARN_SIGN_COMPARE = YES
+GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES
+GCC_WARN_UNDECLARED_SELECTOR = YES
+GCC_WARN_UNKNOWN_PRAGMAS = YES
+GCC_WARN_UNUSED_VARIABLE = YES
+GCC_TREAT_WARNINGS_AS_ERRORS = YES
+
+DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
+
+ALWAYS_SEARCH_USER_PATHS = NO
diff --git a/3rdParty/Breakpad/src/common/mac/BreakpadDebug.xcconfig b/3rdParty/Breakpad/src/common/mac/BreakpadDebug.xcconfig
new file mode 100644
index 0000000..94cdd8c
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/BreakpadDebug.xcconfig
@@ -0,0 +1,32 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "Breakpad.xcconfig"
+
+GCC_OPTIMIZATION_LEVEL = 0
diff --git a/3rdParty/Breakpad/src/common/mac/BreakpadRelease.xcconfig b/3rdParty/Breakpad/src/common/mac/BreakpadRelease.xcconfig
new file mode 100644
index 0000000..920f277
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/BreakpadRelease.xcconfig
@@ -0,0 +1,34 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "Breakpad.xcconfig"
+
+GCC_OPTIMIZATION_LEVEL = s
+GCC_WARN_UNINITIALIZED_AUTOS = YES
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) NDEBUG
diff --git a/3rdParty/Breakpad/src/common/mac/GTMDefines.h b/3rdParty/Breakpad/src/common/mac/GTMDefines.h
new file mode 100644
index 0000000..04fcf6d
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/GTMDefines.h
@@ -0,0 +1,456 @@
+//
+// GTMDefines.h
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// ============================================================================
+
+#include <AvailabilityMacros.h>
+#include <TargetConditionals.h>
+
+#ifdef __OBJC__
+#include <Foundation/NSObjCRuntime.h>
+#endif // __OBJC__
+
+#if TARGET_OS_IPHONE
+#include <Availability.h>
+#endif // TARGET_OS_IPHONE
+
+// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
+#ifndef MAC_OS_X_VERSION_10_5
+ #define MAC_OS_X_VERSION_10_5 1050
+#endif
+#ifndef MAC_OS_X_VERSION_10_6
+ #define MAC_OS_X_VERSION_10_6 1060
+#endif
+#ifndef MAC_OS_X_VERSION_10_7
+ #define MAC_OS_X_VERSION_10_7 1070
+#endif
+
+// Not all __IPHONE_X macros defined in past SDKs
+#ifndef __IPHONE_3_0
+ #define __IPHONE_3_0 30000
+#endif
+#ifndef __IPHONE_3_1
+ #define __IPHONE_3_1 30100
+#endif
+#ifndef __IPHONE_3_2
+ #define __IPHONE_3_2 30200
+#endif
+#ifndef __IPHONE_4_0
+ #define __IPHONE_4_0 40000
+#endif
+#ifndef __IPHONE_4_3
+ #define __IPHONE_4_3 40300
+#endif
+#ifndef __IPHONE_5_0
+ #define __IPHONE_5_0 50000
+#endif
+
+// ----------------------------------------------------------------------------
+// CPP symbols that can be overridden in a prefix to control how the toolbox
+// is compiled.
+// ----------------------------------------------------------------------------
+
+
+// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
+// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
+// when a validation fails. If you implement your own validators, you may want
+// to control their internals using the same macros for consistency.
+#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT
+ #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0
+#endif
+
+// Give ourselves a consistent way to do inlines. Apple's macros even use
+// a few different actual definitions, so we're based off of the foundation
+// one.
+#if !defined(GTM_INLINE)
+ #if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__)
+ #define GTM_INLINE static __inline__ __attribute__((always_inline))
+ #else
+ #define GTM_INLINE static __inline__
+ #endif
+#endif
+
+// Give ourselves a consistent way of doing externs that links up nicely
+// when mixing objc and objc++
+#if !defined (GTM_EXTERN)
+ #if defined __cplusplus
+ #define GTM_EXTERN extern "C"
+ #define GTM_EXTERN_C_BEGIN extern "C" {
+ #define GTM_EXTERN_C_END }
+ #else
+ #define GTM_EXTERN extern
+ #define GTM_EXTERN_C_BEGIN
+ #define GTM_EXTERN_C_END
+ #endif
+#endif
+
+// Give ourselves a consistent way of exporting things if we have visibility
+// set to hidden.
+#if !defined (GTM_EXPORT)
+ #define GTM_EXPORT __attribute__((visibility("default")))
+#endif
+
+// Give ourselves a consistent way of declaring something as unused. This
+// doesn't use __unused because that is only supported in gcc 4.2 and greater.
+#if !defined (GTM_UNUSED)
+#define GTM_UNUSED(x) ((void)(x))
+#endif
+
+// _GTMDevLog & _GTMDevAssert
+//
+// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
+// developer level errors. This implementation simply macros to NSLog/NSAssert.
+// It is not intended to be a general logging/reporting system.
+//
+// Please see https://github.com/google/google-toolbox-for-mac/wiki/DevLogNAssert
+// for a little more background on the usage of these macros.
+//
+// _GTMDevLog log some error/problem in debug builds
+// _GTMDevAssert assert if conditon isn't met w/in a method/function
+// in all builds.
+//
+// To replace this system, just provide different macro definitions in your
+// prefix header. Remember, any implementation you provide *must* be thread
+// safe since this could be called by anything in what ever situtation it has
+// been placed in.
+//
+
+// We only define the simple macros if nothing else has defined this.
+#ifndef _GTMDevLog
+
+#ifdef DEBUG
+ #define _GTMDevLog(...) NSLog(__VA_ARGS__)
+#else
+ #define _GTMDevLog(...) do { } while (0)
+#endif
+
+#endif // _GTMDevLog
+
+#ifndef _GTMDevAssert
+// we directly invoke the NSAssert handler so we can pass on the varargs
+// (NSAssert doesn't have a macro we can use that takes varargs)
+#if !defined(NS_BLOCK_ASSERTIONS)
+ #define _GTMDevAssert(condition, ...) \
+ do { \
+ if (!(condition)) { \
+ [[NSAssertionHandler currentHandler] \
+ handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \
+ file:[NSString stringWithUTF8String:__FILE__] \
+ lineNumber:__LINE__ \
+ description:__VA_ARGS__]; \
+ } \
+ } while(0)
+#else // !defined(NS_BLOCK_ASSERTIONS)
+ #define _GTMDevAssert(condition, ...) do { } while (0)
+#endif // !defined(NS_BLOCK_ASSERTIONS)
+
+#endif // _GTMDevAssert
+
+// _GTMCompileAssert
+// _GTMCompileAssert is an assert that is meant to fire at compile time if you
+// want to check things at compile instead of runtime. For example if you
+// want to check that a wchar is 4 bytes instead of 2 you would use
+// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X)
+// Note that the second "arg" is not in quotes, and must be a valid processor
+// symbol in it's own right (no spaces, punctuation etc).
+
+// Wrapping this in an #ifndef allows external groups to define their own
+// compile time assert scheme.
+#ifndef _GTMCompileAssert
+ // We got this technique from here:
+ // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html
+
+ #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg
+ #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg)
+ #define _GTMCompileAssert(test, msg) \
+ typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
+#endif // _GTMCompileAssert
+
+// ----------------------------------------------------------------------------
+// CPP symbols defined based on the project settings so the GTM code has
+// simple things to test against w/o scattering the knowledge of project
+// setting through all the code.
+// ----------------------------------------------------------------------------
+
+// Provide a single constant CPP symbol that all of GTM uses for ifdefing
+// iPhone code.
+#if TARGET_OS_IPHONE // iPhone SDK
+ // For iPhone specific stuff
+ #define GTM_IPHONE_SDK 1
+ #if TARGET_IPHONE_SIMULATOR
+ #define GTM_IPHONE_DEVICE 0
+ #define GTM_IPHONE_SIMULATOR 1
+ #else
+ #define GTM_IPHONE_DEVICE 1
+ #define GTM_IPHONE_SIMULATOR 0
+ #endif // TARGET_IPHONE_SIMULATOR
+ // By default, GTM has provided it's own unittesting support, define this
+ // to use the support provided by Xcode, especially for the Xcode4 support
+ // for unittesting.
+ #ifndef GTM_IPHONE_USE_SENTEST
+ #define GTM_IPHONE_USE_SENTEST 0
+ #endif
+ #define GTM_MACOS_SDK 0
+#else
+ // For MacOS specific stuff
+ #define GTM_MACOS_SDK 1
+ #define GTM_IPHONE_SDK 0
+ #define GTM_IPHONE_SIMULATOR 0
+ #define GTM_IPHONE_DEVICE 0
+ #define GTM_IPHONE_USE_SENTEST 0
+#endif
+
+// Some of our own availability macros
+#if GTM_MACOS_SDK
+#define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE
+#define GTM_AVAILABLE_ONLY_ON_MACOS
+#else
+#define GTM_AVAILABLE_ONLY_ON_IPHONE
+#define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE
+#endif
+
+// GC was dropped by Apple, define the old constant incase anyone still keys
+// off of it.
+#ifndef GTM_SUPPORT_GC
+ #define GTM_SUPPORT_GC 0
+#endif
+
+// To simplify support for 64bit (and Leopard in general), we provide the type
+// defines for non Leopard SDKs
+#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ // NSInteger/NSUInteger and Max/Mins
+ #ifndef NSINTEGER_DEFINED
+ #if (defined(__LP64__) && __LP64__) || NS_BUILD_32_LIKE_64
+ typedef long NSInteger;
+ typedef unsigned long NSUInteger;
+ #else
+ typedef int NSInteger;
+ typedef unsigned int NSUInteger;
+ #endif
+ #define NSIntegerMax LONG_MAX
+ #define NSIntegerMin LONG_MIN
+ #define NSUIntegerMax ULONG_MAX
+ #define NSINTEGER_DEFINED 1
+ #endif // NSINTEGER_DEFINED
+ // CGFloat
+ #ifndef CGFLOAT_DEFINED
+ #if defined(__LP64__) && __LP64__
+ // This really is an untested path (64bit on Tiger?)
+ typedef double CGFloat;
+ #define CGFLOAT_MIN DBL_MIN
+ #define CGFLOAT_MAX DBL_MAX
+ #define CGFLOAT_IS_DOUBLE 1
+ #else /* !defined(__LP64__) || !__LP64__ */
+ typedef float CGFloat;
+ #define CGFLOAT_MIN FLT_MIN
+ #define CGFLOAT_MAX FLT_MAX
+ #define CGFLOAT_IS_DOUBLE 0
+ #endif /* !defined(__LP64__) || !__LP64__ */
+ #define CGFLOAT_DEFINED 1
+ #endif // CGFLOAT_DEFINED
+#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
+
+// Some support for advanced clang static analysis functionality
+// See http://clang-analyzer.llvm.org/annotations.html
+#ifndef __has_feature // Optional.
+ #define __has_feature(x) 0 // Compatibility with non-clang compilers.
+#endif
+
+#ifndef NS_RETURNS_RETAINED
+ #if __has_feature(attribute_ns_returns_retained)
+ #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
+ #else
+ #define NS_RETURNS_RETAINED
+ #endif
+#endif
+
+#ifndef NS_RETURNS_NOT_RETAINED
+ #if __has_feature(attribute_ns_returns_not_retained)
+ #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
+ #else
+ #define NS_RETURNS_NOT_RETAINED
+ #endif
+#endif
+
+#ifndef CF_RETURNS_RETAINED
+ #if __has_feature(attribute_cf_returns_retained)
+ #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
+ #else
+ #define CF_RETURNS_RETAINED
+ #endif
+#endif
+
+#ifndef CF_RETURNS_NOT_RETAINED
+ #if __has_feature(attribute_cf_returns_not_retained)
+ #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
+ #else
+ #define CF_RETURNS_NOT_RETAINED
+ #endif
+#endif
+
+#ifndef NS_CONSUMED
+ #if __has_feature(attribute_ns_consumed)
+ #define NS_CONSUMED __attribute__((ns_consumed))
+ #else
+ #define NS_CONSUMED
+ #endif
+#endif
+
+#ifndef CF_CONSUMED
+ #if __has_feature(attribute_cf_consumed)
+ #define CF_CONSUMED __attribute__((cf_consumed))
+ #else
+ #define CF_CONSUMED
+ #endif
+#endif
+
+#ifndef NS_CONSUMES_SELF
+ #if __has_feature(attribute_ns_consumes_self)
+ #define NS_CONSUMES_SELF __attribute__((ns_consumes_self))
+ #else
+ #define NS_CONSUMES_SELF
+ #endif
+#endif
+
+// Defined on 10.6 and above.
+#ifndef NS_FORMAT_ARGUMENT
+ #define NS_FORMAT_ARGUMENT(A)
+#endif
+
+// Defined on 10.6 and above.
+#ifndef NS_FORMAT_FUNCTION
+ #define NS_FORMAT_FUNCTION(F,A)
+#endif
+
+// Defined on 10.6 and above.
+#ifndef CF_FORMAT_ARGUMENT
+ #define CF_FORMAT_ARGUMENT(A)
+#endif
+
+// Defined on 10.6 and above.
+#ifndef CF_FORMAT_FUNCTION
+ #define CF_FORMAT_FUNCTION(F,A)
+#endif
+
+#ifndef GTM_NONNULL
+ #if defined(__has_attribute)
+ #if __has_attribute(nonnull)
+ #define GTM_NONNULL(x) __attribute__((nonnull x))
+ #else
+ #define GTM_NONNULL(x)
+ #endif
+ #else
+ #define GTM_NONNULL(x)
+ #endif
+#endif
+
+// Invalidates the initializer from which it's called.
+#ifndef GTMInvalidateInitializer
+ #if __has_feature(objc_arc)
+ #define GTMInvalidateInitializer() \
+ do { \
+ [self class]; /* Avoid warning of dead store to |self|. */ \
+ _GTMDevAssert(NO, @"Invalid initializer."); \
+ return nil; \
+ } while (0)
+ #else
+ #define GTMInvalidateInitializer() \
+ do { \
+ [self release]; \
+ _GTMDevAssert(NO, @"Invalid initializer."); \
+ return nil; \
+ } while (0)
+ #endif
+#endif
+
+#ifndef GTMCFAutorelease
+ #if __has_feature(objc_arc)
+ #define GTMCFAutorelease(x) CFBridgingRelease(x)
+ #else
+ #define GTMCFAutorelease(x) ([(id)x autorelease])
+ #endif
+#endif
+
+#ifdef __OBJC__
+
+// Declared here so that it can easily be used for logging tracking if
+// necessary. See GTMUnitTestDevLog.h for details.
+@class NSString;
+GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2);
+
+// Macro to allow you to create NSStrings out of other macros.
+// #define FOO foo
+// NSString *fooString = GTM_NSSTRINGIFY(FOO);
+#if !defined (GTM_NSSTRINGIFY)
+ #define GTM_NSSTRINGIFY_INNER(x) @#x
+ #define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x)
+#endif
+
+// Macro to allow fast enumeration when building for 10.5 or later, and
+// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
+// does keys, so pick the right thing, nothing is done on the FastEnumeration
+// side to be sure you're getting what you wanted.
+#ifndef GTM_FOREACH_OBJECT
+ #if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
+ #define GTM_FOREACH_ENUMEREE(element, enumeration) \
+ for (element in enumeration)
+ #define GTM_FOREACH_OBJECT(element, collection) \
+ for (element in collection)
+ #define GTM_FOREACH_KEY(element, collection) \
+ for (element in collection)
+ #else
+ #define GTM_FOREACH_ENUMEREE(element, enumeration) \
+ for (NSEnumerator *_ ## element ## _enum = enumeration; \
+ (element = [_ ## element ## _enum nextObject]) != nil; )
+ #define GTM_FOREACH_OBJECT(element, collection) \
+ GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator])
+ #define GTM_FOREACH_KEY(element, collection) \
+ GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator])
+ #endif
+#endif
+
+// ============================================================================
+
+// To simplify support for both Leopard and Snow Leopard we declare
+// the Snow Leopard protocols that we need here.
+#if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
+#define GTM_10_6_PROTOCOLS_DEFINED 1
+@protocol NSConnectionDelegate
+@end
+@protocol NSAnimationDelegate
+@end
+@protocol NSImageDelegate
+@end
+@protocol NSTabViewDelegate
+@end
+#endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
+
+// GTM_SEL_STRING is for specifying selector (usually property) names to KVC
+// or KVO methods.
+// In debug it will generate warnings for undeclared selectors if
+// -Wunknown-selector is turned on.
+// In release it will have no runtime overhead.
+#ifndef GTM_SEL_STRING
+ #ifdef DEBUG
+ #define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName))
+ #else
+ #define GTM_SEL_STRING(selName) @#selName
+ #endif // DEBUG
+#endif // GTM_SEL_STRING
+
+#endif // __OBJC__
diff --git a/3rdParty/Breakpad/src/common/mac/GTMLogger.h b/3rdParty/Breakpad/src/common/mac/GTMLogger.h
new file mode 100644
index 0000000..c4fd140
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/GTMLogger.h
@@ -0,0 +1,504 @@
+//
+// GTMLogger.h
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// Key Abstractions
+// ----------------
+//
+// This file declares multiple classes and protocols that are used by the
+// GTMLogger logging system. The 4 main abstractions used in this file are the
+// following:
+//
+// * logger (GTMLogger) - The main logging class that users interact with. It
+// has methods for logging at different levels and uses a log writer, a log
+// formatter, and a log filter to get the job done.
+//
+// * log writer (GTMLogWriter) - Writes a given string to some log file, where
+// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
+// or even some in-memory structure (e.g., a ring buffer).
+//
+// * log formatter (GTMLogFormatter) - Given a format string and arguments as
+// a va_list, returns a single formatted NSString. A "formatted string" could
+// be a string with the date prepended, a string with values in a CSV format,
+// or even a string of XML.
+//
+// * log filter (GTMLogFilter) - Given a formatted log message as an NSString
+// and the level at which the message is to be logged, this class will decide
+// whether the given message should be logged or not. This is a flexible way
+// to filter out messages logged at a certain level, messages that contain
+// certain text, or filter nothing out at all. This gives the caller the
+// flexibility to dynamically enable debug logging in Release builds.
+//
+// This file also declares some classes to handle the common log writer, log
+// formatter, and log filter cases. Callers can also create their own writers,
+// formatters, and filters and they can even build them on top of the ones
+// declared here. Keep in mind that your custom writer/formatter/filter may be
+// called from multiple threads, so it must be thread-safe.
+
+#import <Foundation/Foundation.h>
+#import "GTMDefines.h"
+
+// Predeclaration of used protocols that are declared later in this file.
+@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
+
+// GTMLogger
+//
+// GTMLogger is the primary user-facing class for an object-oriented logging
+// system. It is built on the concept of log formatters (GTMLogFormatter), log
+// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is
+// sent to a GTMLogger to log a message, the message is formatted using the log
+// formatter, then the log filter is consulted to see if the message should be
+// logged, and if so, the message is sent to the log writer to be written out.
+//
+// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
+// flexibility comes from the fact that GTMLogger instances can be customized
+// with user defined formatters, filters, and writers. And these writers,
+// filters, and formatters can be combined, stacked, and customized in arbitrary
+// ways to suit the needs at hand. For example, multiple writers can be used at
+// the same time, and a GTMLogger instance can even be used as another
+// GTMLogger's writer. This allows for arbitrarily deep logging trees.
+//
+// A standard GTMLogger uses a writer that sends messages to standard out, a
+// formatter that smacks a timestamp and a few other bits of interesting
+// information on the message, and a filter that filters out debug messages from
+// release builds. Using the standard log settings, a log message will look like
+// the following:
+//
+// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
+//
+// The output contains the date and time of the log message, the name of the
+// process followed by its process ID/thread ID, the log level at which the
+// message was logged (in the previous example the level was 1:
+// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
+// this case, the log message was @"foo=%@", foo).
+//
+// Multiple instances of GTMLogger can be created, each configured their own
+// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
+// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
+// it convenient for all code in a process to use the same GTMLogger instance.
+// The shared GTMLogger instance can also be configured in an arbitrary, and
+// these configuration changes will affect all code that logs through the shared
+// instance.
+
+//
+// Log Levels
+// ----------
+// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger
+// doesn't take any special action based on the log level; it simply forwards
+// this information on to formatters, filters, and writers, each of which may
+// optionally take action based on the level. Since log level filtering is
+// performed at runtime, log messages are typically not filtered out at compile
+// time. The exception to this rule is that calls to the GTMLoggerDebug() macro
+// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible
+// with behavior that many developers are currently used to. Note that this
+// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
+// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
+//
+// Standard loggers are created with the GTMLogLevelFilter log filter, which
+// filters out certain log messages based on log level, and some other settings.
+//
+// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
+// GTMLogger itself, there are also C macros that make usage of the shared
+// GTMLogger instance very convenient. These macros are:
+//
+// GTMLoggerDebug(...)
+// GTMLoggerInfo(...)
+// GTMLoggerError(...)
+//
+// Again, a notable feature of these macros is that GTMLogDebug() calls *will be
+// compiled out of non-DEBUG builds*.
+//
+// Standard Loggers
+// ----------------
+// GTMLogger has the concept of "standard loggers". A standard logger is simply
+// a logger that is pre-configured with some standard/common writer, formatter,
+// and filter combination. Standard loggers are created using the creation
+// methods beginning with "standard". The alternative to a standard logger is a
+// regular logger, which will send messages to stdout, with no special
+// formatting, and no filtering.
+//
+// How do I use GTMLogger?
+// ----------------------
+// The typical way you will want to use GTMLogger is to simply use the
+// GTMLogger*() macros for logging from code. That way we can easily make
+// changes to the GTMLogger class and simply update the macros accordingly. Only
+// your application startup code (perhaps, somewhere in main()) should use the
+// GTMLogger class directly in order to configure the shared logger, which all
+// of the code using the macros will be using. Again, this is just the typical
+// situation.
+//
+// To be complete, there are cases where you may want to use GTMLogger directly,
+// or even create separate GTMLogger instances for some reason. That's fine,
+// too.
+//
+// Examples
+// --------
+// The following show some common GTMLogger use cases.
+//
+// 1. You want to log something as simply as possible. Also, this call will only
+// appear in debug builds. In non-DEBUG builds it will be completely removed.
+//
+// GTMLoggerDebug(@"foo = %@", foo);
+//
+// 2. The previous example is similar to the following. The major difference is
+// that the previous call (example 1) will be compiled out of Release builds
+// but this statement will not be compiled out.
+//
+// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
+//
+// 3. Send all logging output from the shared logger to a file. We do this by
+// creating an NSFileHandle for writing associated with a file, and setting
+// that file handle as the logger's writer.
+//
+// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
+// create:YES];
+// [[GTMLogger sharedLogger] setWriter:f];
+// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log
+//
+// 4. Create a new GTMLogger that will log to a file. This example differs from
+// the previous one because here we create a new GTMLogger that is different
+// from the shared logger.
+//
+// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"];
+// [logger logInfo:@"hi temp log file"];
+//
+// 5. Create a logger that writes to stdout and does NOT do any formatting to
+// the log message. This might be useful, for example, when writing a help
+// screen for a command-line tool to standard output.
+//
+// GTMLogger *logger = [GTMLogger logger];
+// [logger logInfo:@"%@ version 0.1 usage", progName];
+//
+// 6. Send log output to stdout AND to a log file. The trick here is that
+// NSArrays function as composite log writers, which means when an array is
+// set as the log writer, it forwards all logging messages to all of its
+// contained GTMLogWriters.
+//
+// // Create array of GTMLogWriters
+// NSArray *writers = [NSArray arrayWithObjects:
+// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
+// [NSFileHandle fileHandleWithStandardOutput], nil];
+//
+// GTMLogger *logger = [GTMLogger standardLogger];
+// [logger setWriter:writers];
+// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
+//
+// For futher details on log writers, formatters, and filters, see the
+// documentation below.
+//
+// NOTE: GTMLogger is application level logging. By default it does nothing
+// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose
+// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro
+// definitions in its prefix header (see GTMDefines.h for how one would do
+// that).
+//
+@interface GTMLogger : NSObject {
+ @private
+ id<GTMLogWriter> writer_;
+ id<GTMLogFormatter> formatter_;
+ id<GTMLogFilter> filter_;
+}
+
+//
+// Accessors for the shared logger instance
+//
+
+// Returns a shared/global standard GTMLogger instance. Callers should typically
+// use this method to get a GTMLogger instance, unless they explicitly want
+// their own instance to configure for their own needs. This is the only method
+// that returns a shared instance; all the rest return new GTMLogger instances.
++ (id)sharedLogger;
+
+// Sets the shared logger instance to |logger|. Future calls to +sharedLogger
+// will return |logger| instead.
++ (void)setSharedLogger:(GTMLogger *)logger;
+
+//
+// Creation methods
+//
+
+// Returns a new autoreleased GTMLogger instance that will log to stdout, using
+// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter.
++ (id)standardLogger;
+
+// Same as +standardLogger, but logs to stderr.
++ (id)standardLoggerWithStderr;
+
+// Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to
+// stderr, everything else goes to stdout.
++ (id)standardLoggerWithStdoutAndStderr;
+
+// Returns a new standard GTMLogger instance with a log writer that will
+// write to the file at |path|, and will use the GTMLogStandardFormatter and
+// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
++ (id)standardLoggerWithPath:(NSString *)path;
+
+// Returns an autoreleased GTMLogger instance that will use the specified
+// |writer|, |formatter|, and |filter|.
++ (id)loggerWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter;
+
+// Returns an autoreleased GTMLogger instance that logs to stdout, with the
+// basic formatter, and no filter. The returned logger differs from the logger
+// returned by +standardLogger because this one does not do any filtering and
+// does not do any special log formatting; this is the difference between a
+// "regular" logger and a "standard" logger.
++ (id)logger;
+
+// Designated initializer. This method returns a GTMLogger initialized with the
+// specified |writer|, |formatter|, and |filter|. See the setter methods below
+// for what values will be used if nil is passed for a parameter.
+- (id)initWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter;
+
+//
+// Logging methods
+//
+
+// Logs a message at the debug level (kGTMLoggerLevelDebug).
+- (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
+// Logs a message at the info level (kGTMLoggerLevelInfo).
+- (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
+// Logs a message at the error level (kGTMLoggerLevelError).
+- (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
+// Logs a message at the assert level (kGTMLoggerLevelAssert).
+- (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
+
+
+//
+// Accessors
+//
+
+// Accessor methods for the log writer. If the log writer is set to nil,
+// [NSFileHandle fileHandleWithStandardOutput] is used.
+- (id<GTMLogWriter>)writer;
+- (void)setWriter:(id<GTMLogWriter>)writer;
+
+// Accessor methods for the log formatter. If the log formatter is set to nil,
+// GTMLogBasicFormatter is used. This formatter will format log messages in a
+// plain printf style.
+- (id<GTMLogFormatter>)formatter;
+- (void)setFormatter:(id<GTMLogFormatter>)formatter;
+
+// Accessor methods for the log filter. If the log filter is set to nil,
+// GTMLogNoFilter is used, which allows all log messages through.
+- (id<GTMLogFilter>)filter;
+- (void)setFilter:(id<GTMLogFilter>)filter;
+
+@end // GTMLogger
+
+
+// Helper functions that are used by the convenience GTMLogger*() macros that
+// enable the logging of function names.
+@interface GTMLogger (GTMLoggerMacroHelpers)
+- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
+ NS_FORMAT_FUNCTION(2, 3);
+- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ...
+ NS_FORMAT_FUNCTION(2, 3);
+- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ...
+ NS_FORMAT_FUNCTION(2, 3);
+- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
+ NS_FORMAT_FUNCTION(2, 3);
+@end // GTMLoggerMacroHelpers
+
+
+// The convenience macros are only defined if they haven't already been defined.
+#ifndef GTMLoggerInfo
+
+// Convenience macros that log to the shared GTMLogger instance. These macros
+// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
+// calls will be compiled out of non-Debug builds.
+#define GTMLoggerDebug(...) \
+ [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__]
+#define GTMLoggerInfo(...) \
+ [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__]
+#define GTMLoggerError(...) \
+ [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__]
+#define GTMLoggerAssert(...) \
+ [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__]
+
+// If we're not in a debug build, remove the GTMLoggerDebug statements. This
+// makes calls to GTMLoggerDebug "compile out" of Release builds
+#ifndef DEBUG
+#undef GTMLoggerDebug
+#define GTMLoggerDebug(...) do {} while(0)
+#endif
+
+#endif // !defined(GTMLoggerInfo)
+
+// Log levels.
+typedef enum {
+ kGTMLoggerLevelUnknown,
+ kGTMLoggerLevelDebug,
+ kGTMLoggerLevelInfo,
+ kGTMLoggerLevelError,
+ kGTMLoggerLevelAssert,
+} GTMLoggerLevel;
+
+
+//
+// Log Writers
+//
+
+// Protocol to be implemented by a GTMLogWriter instance.
+@protocol GTMLogWriter <NSObject>
+// Writes the given log message to where the log writer is configured to write.
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level;
+@end // GTMLogWriter
+
+
+// Simple category on NSFileHandle that makes NSFileHandles valid log writers.
+// This is convenient because something like, say, +fileHandleWithStandardError
+// now becomes a valid log writer. Log messages are written to the file handle
+// with a newline appended.
+@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
+// Opens the file at |path| in append mode, and creates the file with |mode|
+// if it didn't previously exist.
++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
+@end // NSFileHandle
+
+
+// This category makes NSArray a GTMLogWriter that can be composed of other
+// GTMLogWriters. This is the classic Composite GoF design pattern. When the
+// GTMLogWriter -logMessage:level: message is sent to the array, the array
+// forwards the message to all of its elements that implement the GTMLogWriter
+// protocol.
+//
+// This is useful in situations where you would like to send log output to
+// multiple log writers at the same time. Simply create an NSArray of the log
+// writers you wish to use, then set the array as the "writer" for your
+// GTMLogger instance.
+@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
+@end // GTMArrayCompositeLogWriter
+
+
+// This category adapts the GTMLogger interface so that it can be used as a log
+// writer; it's an "adapter" in the GoF Adapter pattern sense.
+//
+// This is useful when you want to configure a logger to log to a specific
+// writer with a specific formatter and/or filter. But you want to also compose
+// that with a different log writer that may have its own formatter and/or
+// filter.
+@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
+@end // GTMLoggerLogWriter
+
+
+//
+// Log Formatters
+//
+
+// Protocol to be implemented by a GTMLogFormatter instance.
+@protocol GTMLogFormatter <NSObject>
+// Returns a formatted string using the format specified in |fmt| and the va
+// args specified in |args|.
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
+@end // GTMLogFormatter
+
+
+// A basic log formatter that formats a string the same way that NSLog (or
+// printf) would. It does not do anything fancy, nor does it add any data of its
+// own.
+@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
+
+// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
+- (NSString *)prettyNameForFunc:(NSString *)func;
+
+@end // GTMLogBasicFormatter
+
+
+// A log formatter that formats the log string like the basic formatter, but
+// also prepends a timestamp and some basic process info to the message, as
+// shown in the following sample output.
+// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here
+@interface GTMLogStandardFormatter : GTMLogBasicFormatter {
+ @private
+ NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS
+ NSString *pname_;
+ pid_t pid_;
+}
+@end // GTMLogStandardFormatter
+
+
+//
+// Log Filters
+//
+
+// Protocol to be imlemented by a GTMLogFilter instance.
+@protocol GTMLogFilter <NSObject>
+// Returns YES if |msg| at |level| should be filtered out; NO otherwise.
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level;
+@end // GTMLogFilter
+
+
+// A log filter that filters messages at the kGTMLoggerLevelDebug level out of
+// non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered
+// out of non-debug builds unless GTMVerboseLogging is set in the environment or
+// the processes's defaults. Messages at the kGTMLoggerLevelError level are
+// never filtered.
+@interface GTMLogLevelFilter : NSObject <GTMLogFilter>
+@end // GTMLogLevelFilter
+
+// A simple log filter that does NOT filter anything out;
+// -filterAllowsMessage:level will always return YES. This can be a convenient
+// way to enable debug-level logging in release builds (if you so desire).
+@interface GTMLogNoFilter : NSObject <GTMLogFilter>
+@end // GTMLogNoFilter
+
+
+// Base class for custom level filters. Not for direct use, use the minimum
+// or maximum level subclasses below.
+@interface GTMLogAllowedLevelFilter : NSObject <GTMLogFilter> {
+ @private
+ NSIndexSet *allowedLevels_;
+}
+@end
+
+// A log filter that allows you to set a minimum log level. Messages below this
+// level will be filtered.
+@interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter
+
+// Designated initializer, logs at levels < |level| will be filtered.
+- (id)initWithMinimumLevel:(GTMLoggerLevel)level;
+
+@end
+
+// A log filter that allows you to set a maximum log level. Messages whose level
+// exceeds this level will be filtered. This is really only useful if you have
+// a composite GTMLogger that is sending the other messages elsewhere.
+@interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter
+
+// Designated initializer, logs at levels > |level| will be filtered.
+- (id)initWithMaximumLevel:(GTMLoggerLevel)level;
+
+@end
+
+
+// For subclasses only
+@interface GTMLogger (PrivateMethods)
+
+- (void)logInternalFunc:(const char *)func
+ format:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
+
+@end
+
diff --git a/3rdParty/Breakpad/src/common/mac/GTMLogger.m b/3rdParty/Breakpad/src/common/mac/GTMLogger.m
new file mode 100644
index 0000000..ebc5836
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/GTMLogger.m
@@ -0,0 +1,611 @@
+//
+// GTMLogger.m
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMLogger.h"
+#import <fcntl.h>
+#import <unistd.h>
+#import <stdlib.h>
+#import <pthread.h>
+
+
+#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
+// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting
+// -Wmissing-format-attribute
+// when the function is anything more complex than foo(NSString *fmt, ...).
+// You see the error inside the function when you turn ... into va_args and
+// attempt to call another function (like vsprintf for example).
+// So we just shut off the warning for this file. We reenable it at the end.
+#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
+#endif // !__clang__
+
+// Reference to the shared GTMLogger instance. This is not a singleton, it's
+// just an easy reference to one shared instance.
+static GTMLogger *gSharedLogger = nil;
+
+
+@implementation GTMLogger
+
+// Returns a pointer to the shared logger instance. If none exists, a standard
+// logger is created and returned.
++ (id)sharedLogger {
+ @synchronized(self) {
+ if (gSharedLogger == nil) {
+ gSharedLogger = [[self standardLogger] retain];
+ }
+ }
+ return [[gSharedLogger retain] autorelease];
+}
+
++ (void)setSharedLogger:(GTMLogger *)logger {
+ @synchronized(self) {
+ [gSharedLogger autorelease];
+ gSharedLogger = [logger retain];
+ }
+}
+
++ (id)standardLogger {
+ // Don't trust NSFileHandle not to throw
+ @try {
+ id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
+ id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init]
+ autorelease];
+ id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
+ return [[[self alloc] initWithWriter:writer
+ formatter:fr
+ filter:filter] autorelease];
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ return nil;
+}
+
++ (id)standardLoggerWithStderr {
+ // Don't trust NSFileHandle not to throw
+ @try {
+ id me = [self standardLogger];
+ [me setWriter:[NSFileHandle fileHandleWithStandardError]];
+ return me;
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ return nil;
+}
+
++ (id)standardLoggerWithStdoutAndStderr {
+ // We're going to take advantage of the GTMLogger to GTMLogWriter adaptor
+ // and create a composite logger that an outer "standard" logger can use
+ // as a writer. Our inner loggers should apply no formatting since the main
+ // logger does that and we want the caller to be able to change formatters
+ // or add writers without knowing the inner structure of our composite.
+
+ // Don't trust NSFileHandle not to throw
+ @try {
+ GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init]
+ autorelease];
+ GTMLogger *stdoutLogger =
+ [self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput]
+ formatter:formatter
+ filter:[[[GTMLogMaximumLevelFilter alloc]
+ initWithMaximumLevel:kGTMLoggerLevelInfo]
+ autorelease]];
+ GTMLogger *stderrLogger =
+ [self loggerWithWriter:[NSFileHandle fileHandleWithStandardError]
+ formatter:formatter
+ filter:[[[GTMLogMininumLevelFilter alloc]
+ initWithMinimumLevel:kGTMLoggerLevelError]
+ autorelease]];
+ GTMLogger *compositeWriter =
+ [self loggerWithWriter:[NSArray arrayWithObjects:
+ stdoutLogger, stderrLogger, nil]
+ formatter:formatter
+ filter:[[[GTMLogNoFilter alloc] init] autorelease]];
+ GTMLogger *outerLogger = [self standardLogger];
+ [outerLogger setWriter:compositeWriter];
+ return outerLogger;
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ return nil;
+}
+
++ (id)standardLoggerWithPath:(NSString *)path {
+ @try {
+ NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
+ if (fh == nil) return nil;
+ id me = [self standardLogger];
+ [me setWriter:fh];
+ return me;
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ return nil;
+}
+
++ (id)loggerWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter {
+ return [[[self alloc] initWithWriter:writer
+ formatter:formatter
+ filter:filter] autorelease];
+}
+
++ (id)logger {
+ return [[[self alloc] init] autorelease];
+}
+
+- (id)init {
+ return [self initWithWriter:nil formatter:nil filter:nil];
+}
+
+- (id)initWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter {
+ if ((self = [super init])) {
+ [self setWriter:writer];
+ [self setFormatter:formatter];
+ [self setFilter:filter];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ // Unlikely, but |writer_| may be an NSFileHandle, which can throw
+ @try {
+ [formatter_ release];
+ [filter_ release];
+ [writer_ release];
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ [super dealloc];
+}
+
+- (id<GTMLogWriter>)writer {
+ return [[writer_ retain] autorelease];
+}
+
+- (void)setWriter:(id<GTMLogWriter>)writer {
+ @synchronized(self) {
+ [writer_ autorelease];
+ writer_ = nil;
+ if (writer == nil) {
+ // Try to use stdout, but don't trust NSFileHandle
+ @try {
+ writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
+ }
+ @catch (id e) {
+ // Leave |writer_| nil
+ }
+ } else {
+ writer_ = [writer retain];
+ }
+ }
+}
+
+- (id<GTMLogFormatter>)formatter {
+ return [[formatter_ retain] autorelease];
+}
+
+- (void)setFormatter:(id<GTMLogFormatter>)formatter {
+ @synchronized(self) {
+ [formatter_ autorelease];
+ formatter_ = nil;
+ if (formatter == nil) {
+ @try {
+ formatter_ = [[GTMLogBasicFormatter alloc] init];
+ }
+ @catch (id e) {
+ // Leave |formatter_| nil
+ }
+ } else {
+ formatter_ = [formatter retain];
+ }
+ }
+}
+
+- (id<GTMLogFilter>)filter {
+ return [[filter_ retain] autorelease];
+}
+
+- (void)setFilter:(id<GTMLogFilter>)filter {
+ @synchronized(self) {
+ [filter_ autorelease];
+ filter_ = nil;
+ if (filter == nil) {
+ @try {
+ filter_ = [[GTMLogNoFilter alloc] init];
+ }
+ @catch (id e) {
+ // Leave |filter_| nil
+ }
+ } else {
+ filter_ = [filter retain];
+ }
+ }
+}
+
+- (void)logDebug:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug];
+ va_end(args);
+}
+
+- (void)logInfo:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo];
+ va_end(args);
+}
+
+- (void)logError:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError];
+ va_end(args);
+}
+
+- (void)logAssert:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert];
+ va_end(args);
+}
+
+@end // GTMLogger
+
+@implementation GTMLogger (GTMLoggerMacroHelpers)
+
+- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug];
+ va_end(args);
+}
+
+- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo];
+ va_end(args);
+}
+
+- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError];
+ va_end(args);
+}
+
+- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert];
+ va_end(args);
+}
+
+@end // GTMLoggerMacroHelpers
+
+@implementation GTMLogger (PrivateMethods)
+
+- (void)logInternalFunc:(const char *)func
+ format:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ // Primary point where logging happens, logging should never throw, catch
+ // everything.
+ @try {
+ NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
+ NSString *msg = [formatter_ stringForFunc:fname
+ withFormat:fmt
+ valist:args
+ level:level];
+ if (msg && [filter_ filterAllowsMessage:msg level:level])
+ [writer_ logMessage:msg level:level];
+ }
+ @catch (id e) {
+ // Ignored
+ }
+}
+
+@end // PrivateMethods
+
+
+@implementation NSFileHandle (GTMFileHandleLogWriter)
+
++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode {
+ int fd = -1;
+ if (path) {
+ int flags = O_WRONLY | O_APPEND | O_CREAT;
+ fd = open([path fileSystemRepresentation], flags, mode);
+ }
+ if (fd == -1) return nil;
+ return [[[self alloc] initWithFileDescriptor:fd
+ closeOnDealloc:YES] autorelease];
+}
+
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ @synchronized(self) {
+ // Closed pipes should not generate exceptions in our caller. Catch here
+ // as well [GTMLogger logInternalFunc:...] so that an exception in this
+ // writer does not prevent other writers from having a chance.
+ @try {
+ NSString *line = [NSString stringWithFormat:@"%@\n", msg];
+ [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
+ }
+ @catch (id e) {
+ // Ignored
+ }
+ }
+}
+
+@end // GTMFileHandleLogWriter
+
+
+@implementation NSArray (GTMArrayCompositeLogWriter)
+
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ @synchronized(self) {
+ id<GTMLogWriter> child = nil;
+ GTM_FOREACH_OBJECT(child, self) {
+ if ([child conformsToProtocol:@protocol(GTMLogWriter)])
+ [child logMessage:msg level:level];
+ }
+ }
+}
+
+@end // GTMArrayCompositeLogWriter
+
+
+@implementation GTMLogger (GTMLoggerLogWriter)
+
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ switch (level) {
+ case kGTMLoggerLevelDebug:
+ [self logDebug:@"%@", msg];
+ break;
+ case kGTMLoggerLevelInfo:
+ [self logInfo:@"%@", msg];
+ break;
+ case kGTMLoggerLevelError:
+ [self logError:@"%@", msg];
+ break;
+ case kGTMLoggerLevelAssert:
+ [self logAssert:@"%@", msg];
+ break;
+ default:
+ // Ignore the message.
+ break;
+ }
+}
+
+@end // GTMLoggerLogWriter
+
+
+@implementation GTMLogBasicFormatter
+
+- (NSString *)prettyNameForFunc:(NSString *)func {
+ NSString *name = [func stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ NSString *function = @"(unknown)";
+ if ([name length]) {
+ if (// Objective C __func__ and __PRETTY_FUNCTION__
+ [name hasPrefix:@"-["] || [name hasPrefix:@"+["] ||
+ // C++ __PRETTY_FUNCTION__ and other preadorned formats
+ [name hasSuffix:@")"]) {
+ function = name;
+ } else {
+ // Assume C99 __func__
+ function = [NSString stringWithFormat:@"%@()", name];
+ }
+ }
+ return function;
+}
+
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ // Performance note: We may want to do a quick check here to see if |fmt|
+ // contains a '%', and if not, simply return 'fmt'.
+ if (!(fmt && args)) return nil;
+ return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
+}
+
+@end // GTMLogBasicFormatter
+
+
+@implementation GTMLogStandardFormatter
+
+- (id)init {
+ if ((self = [super init])) {
+ dateFormatter_ = [[NSDateFormatter alloc] init];
+ [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4];
+ [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
+ pname_ = [[[NSProcessInfo processInfo] processName] copy];
+ pid_ = [[NSProcessInfo processInfo] processIdentifier];
+ if (!(dateFormatter_ && pname_)) {
+ [self release];
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [dateFormatter_ release];
+ [pname_ release];
+ [super dealloc];
+}
+
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ NSString *tstamp = nil;
+ @synchronized (dateFormatter_) {
+ tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
+ }
+ return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
+ tstamp, pname_, pid_, pthread_self(),
+ level, [self prettyNameForFunc:func],
+ // |super| has guard for nil |fmt| and |args|
+ [super stringForFunc:func withFormat:fmt valist:args level:level]];
+}
+
+@end // GTMLogStandardFormatter
+
+
+@implementation GTMLogLevelFilter
+
+// Check the environment and the user preferences for the GTMVerboseLogging key
+// to see if verbose logging has been enabled. The environment variable will
+// override the defaults setting, so check the environment first.
+// COV_NF_START
+static BOOL IsVerboseLoggingEnabled(void) {
+ static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
+ NSString *value = [[[NSProcessInfo processInfo] environment]
+ objectForKey:kVerboseLoggingKey];
+ if (value) {
+ // Emulate [NSString boolValue] for pre-10.5
+ value = [value stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if ([[value uppercaseString] hasPrefix:@"Y"] ||
+ [[value uppercaseString] hasPrefix:@"T"] ||
+ [value intValue]) {
+ return YES;
+ } else {
+ return NO;
+ }
+ }
+ return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
+}
+// COV_NF_END
+
+// In DEBUG builds, log everything. If we're not in a debug build we'll assume
+// that we're in a Release build.
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+#if defined(DEBUG) && DEBUG
+ return YES;
+#endif
+
+ BOOL allow = YES;
+
+ switch (level) {
+ case kGTMLoggerLevelDebug:
+ allow = NO;
+ break;
+ case kGTMLoggerLevelInfo:
+ allow = IsVerboseLoggingEnabled();
+ break;
+ case kGTMLoggerLevelError:
+ allow = YES;
+ break;
+ case kGTMLoggerLevelAssert:
+ allow = YES;
+ break;
+ default:
+ allow = YES;
+ break;
+ }
+
+ return allow;
+}
+
+@end // GTMLogLevelFilter
+
+
+@implementation GTMLogNoFilter
+
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ return YES; // Allow everything through
+}
+
+@end // GTMLogNoFilter
+
+
+@implementation GTMLogAllowedLevelFilter
+
+// Private designated initializer
+- (id)initWithAllowedLevels:(NSIndexSet *)levels {
+ self = [super init];
+ if (self != nil) {
+ allowedLevels_ = [levels retain];
+ // Cap min/max level
+ if (!allowedLevels_ ||
+ // NSIndexSet is unsigned so only check the high bound, but need to
+ // check both first and last index because NSIndexSet appears to allow
+ // wraparound.
+ ([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) ||
+ ([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) {
+ [self release];
+ return nil;
+ }
+ }
+ return self;
+}
+
+- (id)init {
+ // Allow all levels in default init
+ return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
+ NSMakeRange(kGTMLoggerLevelUnknown,
+ (kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]];
+}
+
+- (void)dealloc {
+ [allowedLevels_ release];
+ [super dealloc];
+}
+
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ return [allowedLevels_ containsIndex:level];
+}
+
+@end // GTMLogAllowedLevelFilter
+
+
+@implementation GTMLogMininumLevelFilter
+
+- (id)initWithMinimumLevel:(GTMLoggerLevel)level {
+ return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
+ NSMakeRange(level,
+ (kGTMLoggerLevelAssert - level + 1))]];
+}
+
+@end // GTMLogMininumLevelFilter
+
+
+@implementation GTMLogMaximumLevelFilter
+
+- (id)initWithMaximumLevel:(GTMLoggerLevel)level {
+ return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
+ NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]];
+}
+
+@end // GTMLogMaximumLevelFilter
+
+#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
+// See comment at top of file.
+#pragma GCC diagnostic error "-Wmissing-format-attribute"
+#endif // !__clang__
+
diff --git a/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.h b/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.h
new file mode 100644
index 0000000..42e8fed
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// HTTPMultipartUpload: A multipart/form-data HTTP uploader.
+// Each parameter pair is sent as a boundary
+// Each file is sent with a name field in addition to the filename and data
+// The data will be sent synchronously.
+
+#import <Foundation/Foundation.h>
+
+@interface HTTPMultipartUpload : NSObject {
+ @protected
+ NSURL *url_; // The destination URL (STRONG)
+ NSDictionary *parameters_; // The key/value pairs for sending data (STRONG)
+ NSMutableDictionary *files_; // Dictionary of name/file-path (STRONG)
+ NSString *boundary_; // The boundary string (STRONG)
+ NSHTTPURLResponse *response_; // The response from the send (STRONG)
+}
+
+- (id)initWithURL:(NSURL *)url;
+
+- (NSURL *)URL;
+
+- (void)setParameters:(NSDictionary *)parameters;
+- (NSDictionary *)parameters;
+
+- (void)addFileAtPath:(NSString *)path name:(NSString *)name;
+- (void)addFileContents:(NSData *)data name:(NSString *)name;
+- (NSDictionary *)files;
+
+// Set the data and return the response
+- (NSData *)send:(NSError **)error;
+- (NSHTTPURLResponse *)response;
+
+@end
diff --git a/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.m b/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.m
new file mode 100644
index 0000000..f6d081e
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/HTTPMultipartUpload.m
@@ -0,0 +1,267 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "HTTPMultipartUpload.h"
+#import "GTMDefines.h"
+
+// As -[NSString stringByAddingPercentEscapesUsingEncoding:] has been
+// deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements it
+// using -[NSString stringByAddingPercentEncodingWithAllowedCharacters:] when
+// using those SDKs.
+static NSString *PercentEncodeNSString(NSString *key) {
+#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_9_0) && \
+ __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0) || \
+ (defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
+ defined(MAC_OS_X_VERSION_10_11) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
+ return [key stringByAddingPercentEncodingWithAllowedCharacters:
+ [NSCharacterSet URLQueryAllowedCharacterSet]];
+#else
+ return [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+#endif
+}
+
+// As -[NSURLConnection sendSynchronousRequest:returningResponse:error:] has
+// been deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements
+// it using -[NSURLSession dataTaskWithRequest:completionHandler:] which is
+// available on iOS 7+.
+static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
+ NSURLResponse **out_response,
+ NSError **out_error) {
+#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \
+ __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) || \
+ (defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
+ defined(MAC_OS_X_VERSION_10_11) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
+ __block NSData* result = nil;
+ __block NSError* error = nil;
+ __block NSURLResponse* response = nil;
+ dispatch_semaphore_t wait_semaphone = dispatch_semaphore_create(0);
+ [[[NSURLSession sharedSession]
+ dataTaskWithRequest:req
+ completionHandler:^(NSData *data,
+ NSURLResponse *resp,
+ NSError *err) {
+ if (out_error)
+ error = [err retain];
+ if (out_response)
+ response = [resp retain];
+ if (err == nil)
+ result = [data retain];
+ dispatch_semaphore_signal(wait_semaphone);
+ }] resume];
+ dispatch_semaphore_wait(wait_semaphone, DISPATCH_TIME_FOREVER);
+ dispatch_release(wait_semaphone);
+ if (out_error)
+ *out_error = [error autorelease];
+ if (out_response)
+ *out_response = [response autorelease];
+ return [result autorelease];
+#else
+ return [NSURLConnection sendSynchronousRequest:req
+ returningResponse:out_response
+ error:out_error];
+#endif
+}
+@interface HTTPMultipartUpload(PrivateMethods)
+- (NSString *)multipartBoundary;
+// Each of the following methods will append the starting multipart boundary,
+// but not the ending one.
+- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value;
+- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name;
+- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name;
+@end
+
+@implementation HTTPMultipartUpload
+//=============================================================================
+#pragma mark -
+#pragma mark || Private ||
+//=============================================================================
+- (NSString *)multipartBoundary {
+ // The boundary has 27 '-' characters followed by 16 hex digits
+ return [NSString stringWithFormat:@"---------------------------%08X%08X",
+ rand(), rand()];
+}
+
+//=============================================================================
+- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value {
+ NSString *escaped = PercentEncodeNSString(key);
+ NSString *fmt =
+ @"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n";
+ NSString *form = [NSString stringWithFormat:fmt, boundary_, escaped, value];
+
+ return [form dataUsingEncoding:NSUTF8StringEncoding];
+}
+
+//=============================================================================
+- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name {
+ NSMutableData *data = [NSMutableData data];
+ NSString *escaped = PercentEncodeNSString(name);
+ NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; "
+ "filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
+ NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped];
+
+ [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]];
+ [data appendData:contents];
+
+ return data;
+}
+
+//=============================================================================
+- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name {
+ NSData *contents = [NSData dataWithContentsOfFile:file];
+
+ return [self formDataForFileContents:contents name:name];
+}
+
+//=============================================================================
+#pragma mark -
+#pragma mark || Public ||
+//=============================================================================
+- (id)initWithURL:(NSURL *)url {
+ if ((self = [super init])) {
+ url_ = [url copy];
+ boundary_ = [[self multipartBoundary] retain];
+ files_ = [[NSMutableDictionary alloc] init];
+ }
+
+ return self;
+}
+
+//=============================================================================
+- (void)dealloc {
+ [url_ release];
+ [parameters_ release];
+ [files_ release];
+ [boundary_ release];
+ [response_ release];
+
+ [super dealloc];
+}
+
+//=============================================================================
+- (NSURL *)URL {
+ return url_;
+}
+
+//=============================================================================
+- (void)setParameters:(NSDictionary *)parameters {
+ if (parameters != parameters_) {
+ [parameters_ release];
+ parameters_ = [parameters copy];
+ }
+}
+
+//=============================================================================
+- (NSDictionary *)parameters {
+ return parameters_;
+}
+
+//=============================================================================
+- (void)addFileAtPath:(NSString *)path name:(NSString *)name {
+ [files_ setObject:path forKey:name];
+}
+
+//=============================================================================
+- (void)addFileContents:(NSData *)data name:(NSString *)name {
+ [files_ setObject:data forKey:name];
+}
+
+//=============================================================================
+- (NSDictionary *)files {
+ return files_;
+}
+
+//=============================================================================
+- (NSData *)send:(NSError **)error {
+ NSMutableURLRequest *req =
+ [[NSMutableURLRequest alloc]
+ initWithURL:url_ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:10.0 ];
+
+ NSMutableData *postBody = [NSMutableData data];
+
+ [req setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",
+ boundary_] forHTTPHeaderField:@"Content-type"];
+
+ // Add any parameters to the message
+ NSArray *parameterKeys = [parameters_ allKeys];
+ NSString *key;
+
+ NSInteger count = [parameterKeys count];
+ for (NSInteger i = 0; i < count; ++i) {
+ key = [parameterKeys objectAtIndex:i];
+ [postBody appendData:[self formDataForKey:key
+ value:[parameters_ objectForKey:key]]];
+ }
+
+ // Add any files to the message
+ NSArray *fileNames = [files_ allKeys];
+ for (NSString *name in fileNames) {
+ id fileOrData = [files_ objectForKey:name];
+ NSData *fileData;
+
+ // The object can be either the path to a file (NSString) or the contents
+ // of the file (NSData).
+ if ([fileOrData isKindOfClass:[NSData class]])
+ fileData = [self formDataForFileContents:fileOrData name:name];
+ else
+ fileData = [self formDataForFile:fileOrData name:name];
+
+ [postBody appendData:fileData];
+ }
+
+ NSString *epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_];
+ [postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]];
+
+ [req setHTTPBody:postBody];
+ [req setHTTPMethod:@"POST"];
+
+ [response_ release];
+ response_ = nil;
+
+ NSData *data = nil;
+ if ([[req URL] isFileURL]) {
+ [[req HTTPBody] writeToURL:[req URL] options:0 error:error];
+ } else {
+ NSURLResponse *response = nil;
+ data = SendSynchronousNSURLRequest(req, &response, error);
+ response_ = (NSHTTPURLResponse *)[response retain];
+ }
+ [req release];
+
+ return data;
+}
+
+//=============================================================================
+- (NSHTTPURLResponse *)response {
+ return response_;
+}
+
+@end
diff --git a/3rdParty/Breakpad/src/common/mac/MachIPC.h b/3rdParty/Breakpad/src/common/mac/MachIPC.h
index 52bed59..8df9165 100644
--- a/3rdParty/Breakpad/src/common/mac/MachIPC.h
+++ b/3rdParty/Breakpad/src/common/mac/MachIPC.h
@@ -164,11 +164,11 @@ class MachMessage {
public:
// The receiver of the message can retrieve the raw data this way
- u_int8_t *GetData() {
+ uint8_t *GetData() {
return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
}
- u_int32_t GetDataLength() {
+ uint32_t GetDataLength() {
return EndianU32_LtoN(GetDataPacket()->data_length);
}
@@ -210,7 +210,7 @@ class MachMessage {
struct MessageDataPacket {
int32_t id; // little-endian
int32_t data_length; // little-endian
- u_int8_t data[1]; // actual size limited by sizeof(MachMessage)
+ uint8_t data[1]; // actual size limited by sizeof(MachMessage)
};
MessageDataPacket* GetDataPacket();
@@ -223,7 +223,7 @@ class MachMessage {
mach_msg_header_t head;
mach_msg_body_t body;
- u_int8_t padding[1024]; // descriptors and data may be embedded here
+ uint8_t padding[1024]; // descriptors and data may be embedded here
};
//==============================================================================
diff --git a/3rdParty/Breakpad/src/common/mac/arch_utilities.cc b/3rdParty/Breakpad/src/common/mac/arch_utilities.cc
new file mode 100644
index 0000000..c9225e0
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/arch_utilities.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2012, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "common/mac/arch_utilities.h"
+
+#include <mach-o/arch.h>
+#include <mach-o/fat.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef CPU_SUBTYPE_ARM_V7S
+#define CPU_SUBTYPE_ARM_V7S (static_cast<cpu_subtype_t>(11))
+#endif // CPU_SUBTYPE_ARM_V7S
+
+#ifndef CPU_TYPE_ARM64
+#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
+#endif // CPU_TYPE_ARM64
+
+#ifndef CPU_SUBTYPE_ARM64_ALL
+#define CPU_SUBTYPE_ARM64_ALL (static_cast<cpu_subtype_t>(0))
+#endif // CPU_SUBTYPE_ARM64_ALL
+
+namespace {
+
+const NXArchInfo* ArchInfo_arm64() {
+ NXArchInfo* arm64 = new NXArchInfo;
+ *arm64 = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM,
+ CPU_SUBTYPE_ARM_V7);
+ arm64->name = "arm64";
+ arm64->cputype = CPU_TYPE_ARM64;
+ arm64->cpusubtype = CPU_SUBTYPE_ARM64_ALL;
+ arm64->description = "arm 64";
+ return arm64;
+}
+
+const NXArchInfo* ArchInfo_armv7s() {
+ NXArchInfo* armv7s = new NXArchInfo;
+ *armv7s = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM,
+ CPU_SUBTYPE_ARM_V7);
+ armv7s->name = "armv7s";
+ armv7s->cpusubtype = CPU_SUBTYPE_ARM_V7S;
+ armv7s->description = "arm v7s";
+ return armv7s;
+}
+
+} // namespace
+
+namespace google_breakpad {
+
+const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name) {
+ // TODO: Remove this when the OS knows about arm64.
+ if (!strcmp("arm64", arch_name))
+ return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM64,
+ CPU_SUBTYPE_ARM64_ALL);
+
+ // TODO: Remove this when the OS knows about armv7s.
+ if (!strcmp("armv7s", arch_name))
+ return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S);
+
+ return NXGetArchInfoFromName(arch_name);
+}
+
+const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype) {
+ // TODO: Remove this when the OS knows about arm64.
+ if (cpu_type == CPU_TYPE_ARM64 && cpu_subtype == CPU_SUBTYPE_ARM64_ALL) {
+ static const NXArchInfo* arm64 = ArchInfo_arm64();
+ return arm64;
+ }
+
+ // TODO: Remove this when the OS knows about armv7s.
+ if (cpu_type == CPU_TYPE_ARM && cpu_subtype == CPU_SUBTYPE_ARM_V7S) {
+ static const NXArchInfo* armv7s = ArchInfo_armv7s();
+ return armv7s;
+ }
+
+ return NXGetArchInfoFromCpuType(cpu_type, cpu_subtype);
+}
+
+} // namespace google_breakpad
+
+#ifndef __APPLE__
+namespace {
+
+enum Architecture {
+ kArch_i386 = 0,
+ kArch_x86_64,
+ kArch_x86_64h,
+ kArch_arm,
+ kArch_arm64,
+ kArch_ppc,
+ // This must be last.
+ kNumArchitectures
+};
+
+// enum Architecture above and kKnownArchitectures below
+// must be kept in sync.
+const NXArchInfo kKnownArchitectures[] = {
+ {
+ "i386",
+ CPU_TYPE_I386,
+ CPU_SUBTYPE_I386_ALL,
+ NX_LittleEndian,
+ "Intel 80x86"
+ },
+ {
+ "x86_64",
+ CPU_TYPE_X86_64,
+ CPU_SUBTYPE_X86_64_ALL,
+ NX_LittleEndian,
+ "Intel x86-64"
+ },
+ {
+ "x86_64h",
+ CPU_TYPE_X86_64,
+ CPU_SUBTYPE_X86_64_H,
+ NX_LittleEndian,
+ "Intel x86-64h Haswell"
+ },
+ {
+ "arm",
+ CPU_TYPE_ARM,
+ CPU_SUBTYPE_ARM_ALL,
+ NX_LittleEndian,
+ "ARM"
+ },
+ {
+ "arm64",
+ CPU_TYPE_ARM64,
+ CPU_SUBTYPE_ARM64_ALL,
+ NX_LittleEndian,
+ "ARM64"
+ },
+ {
+ "ppc",
+ CPU_TYPE_POWERPC,
+ CPU_SUBTYPE_POWERPC_ALL,
+ NX_BigEndian,
+ "PowerPC"
+ }
+};
+
+} // namespace
+
+const NXArchInfo *NXGetLocalArchInfo(void) {
+ Architecture arch;
+#if defined(__i386__)
+ arch = kArch_i386;
+#elif defined(__x86_64__)
+ arch = kArch_x86_64;
+#elif defined(__arm64)
+ arch = kArch_arm64;
+#elif defined(__arm__)
+ arch = kArch_arm;
+#elif defined(__powerpc__)
+ arch = kArch_ppc;
+#else
+ #error "Unsupported CPU architecture"
+#endif
+ return &kKnownArchitectures[arch];
+}
+
+const NXArchInfo *NXGetArchInfoFromName(const char *name) {
+ for (int arch = 0; arch < kNumArchitectures; ++arch) {
+ if (!strcmp(name, kKnownArchitectures[arch].name)) {
+ return &kKnownArchitectures[arch];
+ }
+ }
+ return NULL;
+}
+
+const NXArchInfo *NXGetArchInfoFromCpuType(cpu_type_t cputype,
+ cpu_subtype_t cpusubtype) {
+ const NXArchInfo *candidate = NULL;
+ for (int arch = 0; arch < kNumArchitectures; ++arch) {
+ if (kKnownArchitectures[arch].cputype == cputype) {
+ if (kKnownArchitectures[arch].cpusubtype == cpusubtype) {
+ return &kKnownArchitectures[arch];
+ }
+ if (!candidate) {
+ candidate = &kKnownArchitectures[arch];
+ }
+ }
+ }
+ return candidate;
+}
+
+struct fat_arch *NXFindBestFatArch(cpu_type_t cputype,
+ cpu_subtype_t cpusubtype,
+ struct fat_arch *fat_archs,
+ uint32_t nfat_archs) {
+ struct fat_arch *candidate = NULL;
+ for (uint32_t f = 0; f < nfat_archs; ++f) {
+ if (fat_archs[f].cputype == cputype) {
+ if (fat_archs[f].cpusubtype == cpusubtype) {
+ return &fat_archs[f];
+ }
+ if (!candidate) {
+ candidate = &fat_archs[f];
+ }
+ }
+ }
+ return candidate;
+}
+#endif // !__APPLE__
diff --git a/3rdParty/Breakpad/src/common/mac/arch_utilities.h b/3rdParty/Breakpad/src/common/mac/arch_utilities.h
new file mode 100644
index 0000000..397c1f5
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/arch_utilities.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// arch_utilities.h: Utilities for architecture introspection for Mac platform.
+
+#ifndef COMMON_MAC_ARCH_UTILITIES_H__
+#define COMMON_MAC_ARCH_UTILITIES_H__
+
+#include <mach-o/arch.h>
+
+namespace google_breakpad {
+
+// Custom implementation of |NXGetArchInfoFromName| and
+// |NXGetArchInfoFromCpuType| that handle newer CPU on older OSes.
+const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name);
+const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype);
+
+} // namespace google_breakpad
+
+#endif // COMMON_MAC_ARCH_UTILITIES_H__
diff --git a/3rdParty/Breakpad/src/common/mac/byteswap.h b/3rdParty/Breakpad/src/common/mac/byteswap.h
index a5d745b..b7bbc0b 100644
--- a/3rdParty/Breakpad/src/common/mac/byteswap.h
+++ b/3rdParty/Breakpad/src/common/mac/byteswap.h
@@ -36,6 +36,7 @@
#ifndef COMMON_MAC_BYTESWAP_H_
#define COMMON_MAC_BYTESWAP_H_
+#ifdef __APPLE__
#include <libkern/OSByteOrder.h>
static inline uint16_t ByteSwap(uint16_t v) { return OSSwapInt16(v); }
@@ -45,4 +46,28 @@ static inline int16_t ByteSwap(int16_t v) { return OSSwapInt16(v); }
static inline int32_t ByteSwap(int32_t v) { return OSSwapInt32(v); }
static inline int64_t ByteSwap(int64_t v) { return OSSwapInt64(v); }
+#elif defined(__linux__)
+// For NXByteOrder
+#include <architecture/byte_order.h>
+#include <stdint.h>
+#include <endian.h>
+#include_next <byteswap.h>
+
+static inline uint16_t ByteSwap(uint16_t v) { return bswap_16(v); }
+static inline uint32_t ByteSwap(uint32_t v) { return bswap_32(v); }
+static inline uint64_t ByteSwap(uint64_t v) { return bswap_64(v); }
+static inline int16_t ByteSwap(int16_t v) { return bswap_16(v); }
+static inline int32_t ByteSwap(int32_t v) { return bswap_32(v); }
+static inline int64_t ByteSwap(int64_t v) { return bswap_64(v); }
+
+static inline NXByteOrder NXHostByteOrder() {
+#ifdef __LITTLE_ENDIAN
+ return NX_LittleEndian;
+#else
+ return NX_BigEndian;
+#endif
+}
+
+#endif // __APPLE__
+
#endif // COMMON_MAC_BYTESWAP_H_
diff --git a/3rdParty/Breakpad/src/common/mac/dump_syms.mm b/3rdParty/Breakpad/src/common/mac/dump_syms.cc
index 9783514..eb92729 100644
--- a/3rdParty/Breakpad/src/common/mac/dump_syms.mm
+++ b/3rdParty/Breakpad/src/common/mac/dump_syms.cc
@@ -31,14 +31,21 @@
// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
-// dump_syms.mm: Create a symbol file for use with minidumps
+// dump_syms.cc: Create a symbol file for use with minidumps
#include "common/mac/dump_syms.h"
-#include <Foundation/Foundation.h>
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
#include <mach-o/arch.h>
#include <mach-o/fat.h>
+#include <stdint.h>
#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <ostream>
#include <string>
@@ -50,15 +57,22 @@
#include "common/dwarf_cu_to_module.h"
#include "common/dwarf_line_to_module.h"
#include "common/mac/file_id.h"
+#include "common/mac/arch_utilities.h"
#include "common/mac/macho_reader.h"
#include "common/module.h"
+#include "common/scoped_ptr.h"
#include "common/stabs_reader.h"
#include "common/stabs_to_module.h"
+#include "common/symbol_data.h"
#ifndef CPU_TYPE_ARM
#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12))
#endif // CPU_TYPE_ARM
+#ifndef CPU_TYPE_ARM64
+#define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228))
+#endif // CPU_TYPE_ARM64
+
using dwarf2reader::ByteReader;
using google_breakpad::DwarfCUToModule;
using google_breakpad::DwarfLineToModule;
@@ -69,109 +83,126 @@ using google_breakpad::mach_o::Segment;
using google_breakpad::Module;
using google_breakpad::StabsReader;
using google_breakpad::StabsToModule;
+using google_breakpad::scoped_ptr;
using std::make_pair;
using std::pair;
using std::string;
using std::vector;
+namespace {
+// Return a vector<string> with absolute paths to all the entries
+// in directory (excluding . and ..).
+vector<string> list_directory(const string& directory) {
+ vector<string> entries;
+ DIR* dir = opendir(directory.c_str());
+ if (!dir) {
+ return entries;
+ }
+
+ string path = directory;
+ if (path[path.length() - 1] != '/') {
+ path += '/';
+ }
+
+ struct dirent* entry = NULL;
+ while ((entry = readdir(dir))) {
+ if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
+ entries.push_back(path + entry->d_name);
+ }
+ }
+
+ closedir(dir);
+ return entries;
+}
+}
+
namespace google_breakpad {
-bool DumpSymbols::Read(NSString *filename) {
- if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) {
- fprintf(stderr, "Object file does not exist: %s\n",
- [filename fileSystemRepresentation]);
+bool DumpSymbols::Read(const string &filename) {
+ struct stat st;
+ if (stat(filename.c_str(), &st) == -1) {
+ fprintf(stderr, "Could not access object file %s: %s\n",
+ filename.c_str(), strerror(errno));
return false;
}
- input_pathname_ = [filename retain];
+ input_pathname_ = filename;
// Does this filename refer to a dSYM bundle?
- NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_];
-
- if (bundle) {
- // Filenames referring to bundles usually have names of the form
- // "<basename>.dSYM"; however, if the user has specified a wrapper
- // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings),
- // then the name may have the form "<basename>.<extension>.dSYM". In
- // either case, the resource name for the file containing the DWARF
- // info within the bundle is <basename>.
- //
- // Since there's no way to tell how much to strip off, remove one
- // extension at a time, and use the first one that
- // pathForResource:ofType:inDirectory likes.
- NSString *base_name = [input_pathname_ lastPathComponent];
- NSString *dwarf_resource;
-
- do {
- NSString *new_base_name = [base_name stringByDeletingPathExtension];
-
- // If stringByDeletingPathExtension returned the name unchanged, then
- // there's nothing more for us to strip off --- lose.
- if ([new_base_name isEqualToString:base_name]) {
- fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n",
- [input_pathname_ fileSystemRepresentation]);
- return false;
- }
-
- // Take the shortened result as our new base_name.
- base_name = new_base_name;
-
- // Try to find a DWARF resource in the bundle under the new base_name.
- dwarf_resource = [bundle pathForResource:base_name
- ofType:nil inDirectory:@"DWARF"];
- } while (!dwarf_resource);
+ string contents_path = input_pathname_ + "/Contents/Resources/DWARF";
+ if (S_ISDIR(st.st_mode) &&
+ access(contents_path.c_str(), F_OK) == 0) {
+ // If there's one file under Contents/Resources/DWARF then use that,
+ // otherwise bail out.
+ const vector<string> entries = list_directory(contents_path);
+ if (entries.size() == 0) {
+ fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n",
+ input_pathname_.c_str());
+ return false;
+ }
+ if (entries.size() > 1) {
+ fprintf(stderr, "Too many DWARF files in bundle: %s\n",
+ input_pathname_.c_str());
+ return false;
+ }
- object_filename_ = [dwarf_resource retain];
+ object_filename_ = entries[0];
} else {
- object_filename_ = [input_pathname_ retain];
+ object_filename_ = input_pathname_;
}
// Read the file's contents into memory.
- //
- // The documentation for dataWithContentsOfMappedFile says:
- //
- // Because of file mapping restrictions, this method should only be
- // used if the file is guaranteed to exist for the duration of the
- // data object’s existence. It is generally safer to use the
- // dataWithContentsOfFile: method.
- //
- // I gather this means that OS X doesn't have (or at least, that method
- // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the
- // process appears to get its own copy of the data, and changes to the
- // file don't affect memory and vice versa).
- NSError *error;
- contents_ = [NSData dataWithContentsOfFile:object_filename_
- options:0
- error:&error];
- if (!contents_) {
+ bool read_ok = true;
+ string error;
+ if (stat(object_filename_.c_str(), &st) != -1) {
+ FILE* f = fopen(object_filename_.c_str(), "rb");
+ if (f) {
+ contents_.reset(new uint8_t[st.st_size]);
+ off_t total = 0;
+ while (total < st.st_size && !feof(f)) {
+ size_t read = fread(&contents_[0] + total, 1, st.st_size - total, f);
+ if (read == 0) {
+ if (ferror(f)) {
+ read_ok = false;
+ error = strerror(errno);
+ }
+ break;
+ }
+ total += read;
+ }
+ fclose(f);
+ } else {
+ error = strerror(errno);
+ }
+ }
+
+ if (!read_ok) {
fprintf(stderr, "Error reading object file: %s: %s\n",
- [object_filename_ fileSystemRepresentation],
- [[error localizedDescription] UTF8String]);
+ object_filename_.c_str(),
+ error.c_str());
return false;
}
- [contents_ retain];
// Get the list of object files present in the file.
- FatReader::Reporter fat_reporter([object_filename_
- fileSystemRepresentation]);
+ FatReader::Reporter fat_reporter(object_filename_);
FatReader fat_reader(&fat_reporter);
- if (!fat_reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]),
- [contents_ length])) {
+ if (!fat_reader.Read(&contents_[0],
+ st.st_size)) {
return false;
}
// Get our own copy of fat_reader's object file list.
size_t object_files_count;
- const struct fat_arch *object_files =
+ const SuperFatArch *object_files =
fat_reader.object_files(&object_files_count);
if (object_files_count == 0) {
fprintf(stderr, "Fat binary file contains *no* architectures: %s\n",
- [object_filename_ fileSystemRepresentation]);
+ object_filename_.c_str());
return false;
}
object_files_.resize(object_files_count);
memcpy(&object_files_[0], object_files,
- sizeof(struct fat_arch) * object_files_count);
+ sizeof(SuperFatArch) * object_files_count);
return true;
}
@@ -179,9 +210,8 @@ bool DumpSymbols::Read(NSString *filename) {
bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype) {
// Find the best match for the architecture the user requested.
- const struct fat_arch *best_match
- = NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0],
- static_cast<uint32_t>(object_files_.size()));
+ const SuperFatArch *best_match = FindBestMatchForArchitecture(
+ cpu_type, cpu_subtype);
if (!best_match) return false;
// Record the selected object file.
@@ -191,20 +221,73 @@ bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type,
bool DumpSymbols::SetArchitecture(const std::string &arch_name) {
bool arch_set = false;
- const NXArchInfo *arch_info = NXGetArchInfoFromName(arch_name.c_str());
+ const NXArchInfo *arch_info =
+ google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str());
if (arch_info) {
arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype);
}
return arch_set;
}
+SuperFatArch* DumpSymbols::FindBestMatchForArchitecture(
+ cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
+ // Check if all the object files can be converted to struct fat_arch.
+ bool can_convert_to_fat_arch = true;
+ vector<struct fat_arch> fat_arch_vector;
+ for (vector<SuperFatArch>::const_iterator it = object_files_.begin();
+ it != object_files_.end();
+ ++it) {
+ struct fat_arch arch;
+ bool success = it->ConvertToFatArch(&arch);
+ if (!success) {
+ can_convert_to_fat_arch = false;
+ break;
+ }
+ fat_arch_vector.push_back(arch);
+ }
+
+ // If all the object files can be converted to struct fat_arch, use
+ // NXFindBestFatArch.
+ if (can_convert_to_fat_arch) {
+ const struct fat_arch *best_match
+ = NXFindBestFatArch(cpu_type, cpu_subtype, &fat_arch_vector[0],
+ static_cast<uint32_t>(fat_arch_vector.size()));
+
+ for (size_t i = 0; i < fat_arch_vector.size(); ++i) {
+ if (best_match == &fat_arch_vector[i])
+ return &object_files_[i];
+ }
+ assert(best_match == NULL);
+ return NULL;
+ }
+
+ // Check for an exact match with cpu_type and cpu_subtype.
+ for (vector<SuperFatArch>::iterator it = object_files_.begin();
+ it != object_files_.end();
+ ++it) {
+ if (static_cast<cpu_type_t>(it->cputype) == cpu_type &&
+ static_cast<cpu_subtype_t>(it->cpusubtype) == cpu_subtype)
+ return &*it;
+ }
+
+ // No exact match found.
+ // TODO(erikchen): If it becomes necessary, we can copy the implementation of
+ // NXFindBestFatArch, located at
+ // http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c.
+ fprintf(stderr, "Failed to find an exact match for an object file with cpu "
+ "type: %d and cpu subtype: %d. Furthermore, at least one object file is "
+ "larger than 2**32.\n", cpu_type, cpu_subtype);
+ return NULL;
+}
+
string DumpSymbols::Identifier() {
- FileID file_id([object_filename_ fileSystemRepresentation]);
+ FileID file_id(object_filename_.c_str());
unsigned char identifier_bytes[16];
cpu_type_t cpu_type = selected_object_file_->cputype;
- if (!file_id.MachoIdentifier(cpu_type, identifier_bytes)) {
+ cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype;
+ if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) {
fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n",
- [object_filename_ fileSystemRepresentation]);
+ object_filename_.c_str());
return "";
}
@@ -224,24 +307,90 @@ string DumpSymbols::Identifier() {
// dwarf2reader::LineInfo and populates a Module and a line vector
// with the results.
class DumpSymbols::DumperLineToModule:
- public DwarfCUToModule::LineToModuleFunctor {
+ public DwarfCUToModule::LineToModuleHandler {
public:
// Create a line-to-module converter using BYTE_READER.
DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
: byte_reader_(byte_reader) { }
- void operator()(const char *program, uint64 length,
- Module *module, vector<Module::Line> *lines) {
- DwarfLineToModule handler(module, lines);
+
+ void StartCompilationUnit(const string& compilation_dir) {
+ compilation_dir_ = compilation_dir;
+ }
+
+ void ReadProgram(const uint8_t *program, uint64 length,
+ Module *module, vector<Module::Line> *lines) {
+ DwarfLineToModule handler(module, compilation_dir_, lines);
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
parser.Start();
}
private:
+ string compilation_dir_;
dwarf2reader::ByteReader *byte_reader_; // WEAK
};
+bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) {
+ // Select an object file, if SetArchitecture hasn't been called to set one
+ // explicitly.
+ if (!selected_object_file_) {
+ // If there's only one architecture, that's the one.
+ if (object_files_.size() == 1)
+ selected_object_file_ = &object_files_[0];
+ else {
+ // Look for an object file whose architecture matches our own.
+ const NXArchInfo *local_arch = NXGetLocalArchInfo();
+ if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) {
+ fprintf(stderr, "%s: object file contains more than one"
+ " architecture, none of which match the current"
+ " architecture; specify an architecture explicitly"
+ " with '-a ARCH' to resolve the ambiguity\n",
+ object_filename_.c_str());
+ return false;
+ }
+ }
+ }
+
+ assert(selected_object_file_);
+
+ // Find the name of the selected file's architecture, to appear in
+ // the MODULE record and in error messages.
+ const NXArchInfo *selected_arch_info =
+ google_breakpad::BreakpadGetArchInfoFromCpuType(
+ selected_object_file_->cputype, selected_object_file_->cpusubtype);
+
+ const char *selected_arch_name = selected_arch_info->name;
+ if (strcmp(selected_arch_name, "i386") == 0)
+ selected_arch_name = "x86";
+
+ // Produce a name to use in error messages that includes the
+ // filename, and the architecture, if there is more than one.
+ selected_object_name_ = object_filename_;
+ if (object_files_.size() > 1) {
+ selected_object_name_ += ", architecture ";
+ selected_object_name_ + selected_arch_name;
+ }
+
+ // Compute a module name, to appear in the MODULE record.
+ string module_name = object_filename_;
+ module_name = basename(&module_name[0]);
+
+ // Choose an identifier string, to appear in the MODULE record.
+ string identifier = Identifier();
+ if (identifier.empty())
+ return false;
+ identifier += "0";
+
+ // Create a module to hold the debugging information.
+ module.reset(new Module(module_name,
+ "mac",
+ selected_arch_name,
+ identifier));
+ return true;
+}
+
bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
const mach_o::Reader &macho_reader,
- const mach_o::SectionMap &dwarf_sections) const {
+ const mach_o::SectionMap &dwarf_sections,
+ bool handle_inter_cu_refs) const {
// Build a byte reader of the appropriate endianness.
ByteReader byte_reader(macho_reader.big_endian()
? dwarf2reader::ENDIANNESS_BIG
@@ -249,19 +398,24 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
// Construct a context for this file.
DwarfCUToModule::FileContext file_context(selected_object_name_,
- module);
+ module,
+ handle_inter_cu_refs);
// Build a dwarf2reader::SectionMap from our mach_o::SectionMap.
for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin();
- it != dwarf_sections.end(); it++) {
- file_context.section_map[it->first] =
- make_pair(reinterpret_cast<const char *>(it->second.contents.start),
- it->second.contents.Size());
+ it != dwarf_sections.end(); ++it) {
+ file_context.AddSectionToSectionMap(
+ it->first,
+ it->second.contents.start,
+ it->second.contents.Size());
}
// Find the __debug_info section.
- std::pair<const char *, uint64> debug_info_section
- = file_context.section_map["__debug_info"];
+ dwarf2reader::SectionMap::const_iterator debug_info_entry =
+ file_context.section_map().find("__debug_info");
+ assert(debug_info_entry != file_context.section_map().end());
+ const std::pair<const uint8_t *, uint64>& debug_info_section =
+ debug_info_entry->second;
// There had better be a __debug_info section!
if (!debug_info_section.first) {
fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n",
@@ -283,7 +437,8 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
// Make a Dwarf2Handler that drives our DIEHandler.
dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
// Make a DWARF parser for the compilation unit at OFFSET.
- dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map,
+ dwarf2reader::CompilationUnit dwarf_reader(selected_object_name_,
+ file_context.section_map(),
offset,
&byte_reader,
&die_dispatcher);
@@ -311,10 +466,12 @@ bool DumpSymbols::ReadCFI(google_breakpad::Module *module,
case CPU_TYPE_ARM:
register_names = DwarfCFIToModule::RegisterNames::ARM();
break;
+ case CPU_TYPE_ARM64:
+ register_names = DwarfCFIToModule::RegisterNames::ARM64();
+ break;
default: {
- const NXArchInfo *arch =
- NXGetArchInfoFromCpuType(macho_reader.cpu_type(),
- macho_reader.cpu_subtype());
+ const NXArchInfo *arch = google_breakpad::BreakpadGetArchInfoFromCpuType(
+ macho_reader.cpu_type(), macho_reader.cpu_subtype());
fprintf(stderr, "%s: cannot convert DWARF call frame information for ",
selected_object_name_.c_str());
if (arch)
@@ -328,7 +485,7 @@ bool DumpSymbols::ReadCFI(google_breakpad::Module *module,
}
// Find the call frame information and its size.
- const char *cfi = reinterpret_cast<const char *>(section.contents.start);
+ const uint8_t *cfi = section.contents.start;
size_t cfi_size = section.contents.Size();
// Plug together the parser, handler, and their entourages.
@@ -362,8 +519,14 @@ class DumpSymbols::LoadCommandDumper:
// file, and adding data to MODULE.
LoadCommandDumper(const DumpSymbols &dumper,
google_breakpad::Module *module,
- const mach_o::Reader &reader)
- : dumper_(dumper), module_(module), reader_(reader) { }
+ const mach_o::Reader &reader,
+ SymbolData symbol_data,
+ bool handle_inter_cu_refs)
+ : dumper_(dumper),
+ module_(module),
+ reader_(reader),
+ symbol_data_(symbol_data),
+ handle_inter_cu_refs_(handle_inter_cu_refs) { }
bool SegmentCommand(const mach_o::Segment &segment);
bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings);
@@ -372,6 +535,8 @@ class DumpSymbols::LoadCommandDumper:
const DumpSymbols &dumper_;
google_breakpad::Module *module_; // WEAK
const mach_o::Reader &reader_;
+ const SymbolData symbol_data_;
+ const bool handle_inter_cu_refs_;
};
bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) {
@@ -381,23 +546,31 @@ bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) {
if (segment.name == "__TEXT") {
module_->SetLoadAddress(segment.vmaddr);
- mach_o::SectionMap::const_iterator eh_frame =
- section_map.find("__eh_frame");
- if (eh_frame != section_map.end()) {
- // If there is a problem reading this, don't treat it as a fatal error.
- dumper_.ReadCFI(module_, reader_, eh_frame->second, true);
+ if (symbol_data_ != NO_CFI) {
+ mach_o::SectionMap::const_iterator eh_frame =
+ section_map.find("__eh_frame");
+ if (eh_frame != section_map.end()) {
+ // If there is a problem reading this, don't treat it as a fatal error.
+ dumper_.ReadCFI(module_, reader_, eh_frame->second, true);
+ }
}
return true;
}
if (segment.name == "__DWARF") {
- if (!dumper_.ReadDwarf(module_, reader_, section_map))
- return false;
- mach_o::SectionMap::const_iterator debug_frame
- = section_map.find("__debug_frame");
- if (debug_frame != section_map.end()) {
- // If there is a problem reading this, don't treat it as a fatal error.
- dumper_.ReadCFI(module_, reader_, debug_frame->second, false);
+ if (symbol_data_ != ONLY_CFI) {
+ if (!dumper_.ReadDwarf(module_, reader_, section_map,
+ handle_inter_cu_refs_)) {
+ return false;
+ }
+ }
+ if (symbol_data_ != NO_CFI) {
+ mach_o::SectionMap::const_iterator debug_frame
+ = section_map.find("__debug_frame");
+ if (debug_frame != section_map.end()) {
+ // If there is a problem reading this, don't treat it as a fatal error.
+ dumper_.ReadCFI(module_, reader_, debug_frame->second, false);
+ }
}
}
@@ -421,64 +594,15 @@ bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries,
return true;
}
-bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) {
- // Select an object file, if SetArchitecture hasn't been called to set one
- // explicitly.
- if (!selected_object_file_) {
- // If there's only one architecture, that's the one.
- if (object_files_.size() == 1)
- selected_object_file_ = &object_files_[0];
- else {
- // Look for an object file whose architecture matches our own.
- const NXArchInfo *local_arch = NXGetLocalArchInfo();
- if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) {
- fprintf(stderr, "%s: object file contains more than one"
- " architecture, none of which match the current"
- " architecture; specify an architecture explicitly"
- " with '-a ARCH' to resolve the ambiguity\n",
- [object_filename_ fileSystemRepresentation]);
- return false;
- }
- }
- }
-
- assert(selected_object_file_);
-
- // Find the name of the selected file's architecture, to appear in
- // the MODULE record and in error messages.
- const NXArchInfo *selected_arch_info
- = NXGetArchInfoFromCpuType(selected_object_file_->cputype,
- selected_object_file_->cpusubtype);
-
- const char *selected_arch_name = selected_arch_info->name;
- if (strcmp(selected_arch_name, "i386") == 0)
- selected_arch_name = "x86";
-
- // Produce a name to use in error messages that includes the
- // filename, and the architecture, if there is more than one.
- selected_object_name_ = [object_filename_ UTF8String];
- if (object_files_.size() > 1) {
- selected_object_name_ += ", architecture ";
- selected_object_name_ + selected_arch_name;
- }
-
- // Compute a module name, to appear in the MODULE record.
- NSString *module_name = [object_filename_ lastPathComponent];
-
- // Choose an identifier string, to appear in the MODULE record.
- string identifier = Identifier();
- if (identifier.empty())
+bool DumpSymbols::ReadSymbolData(Module** out_module) {
+ scoped_ptr<Module> module;
+ if (!CreateEmptyModule(module))
return false;
- identifier += "0";
-
- // Create a module to hold the debugging information.
- Module module([module_name UTF8String], "mac", selected_arch_name,
- identifier);
// Parse the selected object file.
mach_o::Reader::Reporter reporter(selected_object_name_);
mach_o::Reader reader(&reporter);
- if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes])
+ if (!reader.Read(&contents_[0]
+ selected_object_file_->offset,
selected_object_file_->size,
selected_object_file_->cputype,
@@ -486,11 +610,37 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) {
return false;
// Walk its load commands, and deal with whatever is there.
- LoadCommandDumper load_command_dumper(*this, &module, reader);
+ LoadCommandDumper load_command_dumper(*this, module.get(), reader,
+ symbol_data_, handle_inter_cu_refs_);
if (!reader.WalkLoadCommands(&load_command_dumper))
return false;
- return module.Write(stream, cfi);
+ *out_module = module.release();
+
+ return true;
+}
+
+bool DumpSymbols::WriteSymbolFile(std::ostream &stream) {
+ Module* module = NULL;
+
+ if (ReadSymbolData(&module) && module) {
+ bool res = module->Write(stream, symbol_data_);
+ delete module;
+ return res;
+ }
+
+ return false;
+}
+
+// Read the selected object file's debugging information, and write out the
+// header only to |stream|. Return true on success; if an error occurs, report
+// it and return false.
+bool DumpSymbols::WriteSymbolFileHeader(std::ostream &stream) {
+ scoped_ptr<Module> module;
+ if (!CreateEmptyModule(module))
+ return false;
+
+ return module->Write(stream, symbol_data_);
}
} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/mac/dump_syms.h b/3rdParty/Breakpad/src/common/mac/dump_syms.h
index 0e2f464..9463f7d 100644
--- a/3rdParty/Breakpad/src/common/mac/dump_syms.h
+++ b/3rdParty/Breakpad/src/common/mac/dump_syms.h
@@ -35,7 +35,6 @@
// reading debugging information from Mach-O files and writing it out as a
// Breakpad symbol file.
-#include <Foundation/Foundation.h>
#include <mach-o/loader.h>
#include <stdio.h>
#include <stdlib.h>
@@ -46,33 +45,32 @@
#include "common/byte_cursor.h"
#include "common/mac/macho_reader.h"
+#include "common/mac/super_fat_arch.h"
#include "common/module.h"
+#include "common/scoped_ptr.h"
+#include "common/symbol_data.h"
namespace google_breakpad {
class DumpSymbols {
public:
- DumpSymbols()
- : input_pathname_(),
+ DumpSymbols(SymbolData symbol_data, bool handle_inter_cu_refs)
+ : symbol_data_(symbol_data),
+ handle_inter_cu_refs_(handle_inter_cu_refs),
+ input_pathname_(),
object_filename_(),
contents_(),
+ object_files_(),
selected_object_file_(),
selected_object_name_() { }
~DumpSymbols() {
- [input_pathname_ release];
- [object_filename_ release];
- [contents_ release];
}
// Prepare to read debugging information from |filename|. |filename| may be
// the name of a universal binary, a Mach-O file, or a dSYM bundle
// containing either of the above. On success, return true; if there is a
// problem reading |filename|, report it and return false.
- //
- // (This class uses NSString for filenames and related values,
- // because the Mac Foundation framework seems to support
- // filename-related operations more fully on NSString values.)
- bool Read(NSString *filename);
+ bool Read(const std::string &filename);
// If this dumper's file includes an object file for |cpu_type| and
// |cpu_subtype|, then select that object file for dumping, and return
@@ -95,14 +93,14 @@ class DumpSymbols {
// architecture matches that of this dumper program.
bool SetArchitecture(const std::string &arch_name);
- // Return a pointer to an array of 'struct fat_arch' structures,
- // describing the object files contained in this dumper's file. Set
- // *|count| to the number of elements in the array. The returned array is
- // owned by this DumpSymbols instance.
+ // Return a pointer to an array of SuperFatArch structures describing the
+ // object files contained in this dumper's file. Set *|count| to the number
+ // of elements in the array. The returned array is owned by this DumpSymbols
+ // instance.
//
// If there are no available architectures, this function
// may return NULL.
- const struct fat_arch *AvailableArchitectures(size_t *count) {
+ const SuperFatArch* AvailableArchitectures(size_t *count) {
*count = object_files_.size();
if (object_files_.size() > 0)
return &object_files_[0];
@@ -110,24 +108,44 @@ class DumpSymbols {
}
// Read the selected object file's debugging information, and write it out to
- // |stream|. Write the CFI section if |cfi| is true. Return true on success;
- // if an error occurs, report it and return false.
- bool WriteSymbolFile(std::ostream &stream, bool cfi);
+ // |stream|. Return true on success; if an error occurs, report it and
+ // return false.
+ bool WriteSymbolFile(std::ostream &stream);
+
+ // Read the selected object file's debugging information, and write out the
+ // header only to |stream|. Return true on success; if an error occurs, report
+ // it and return false.
+ bool WriteSymbolFileHeader(std::ostream &stream);
+
+ // As above, but simply return the debugging information in module
+ // instead of writing it to a stream. The caller owns the resulting
+ // module object and must delete it when finished.
+ bool ReadSymbolData(Module** module);
private:
// Used internally.
class DumperLineToModule;
class LoadCommandDumper;
+ // This method behaves similarly to NXFindBestFatArch, but it supports
+ // SuperFatArch.
+ SuperFatArch* FindBestMatchForArchitecture(
+ cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
+
// Return an identifier string for the file this DumpSymbols is dumping.
std::string Identifier();
+
+ // Creates an empty module object.
+ bool CreateEmptyModule(scoped_ptr<Module>& module);
+
// Read debugging information from |dwarf_sections|, which was taken from
// |macho_reader|, and add it to |module|. On success, return true;
// on failure, report the problem and return false.
bool ReadDwarf(google_breakpad::Module *module,
const mach_o::Reader &macho_reader,
- const mach_o::SectionMap &dwarf_sections) const;
+ const mach_o::SectionMap &dwarf_sections,
+ bool handle_inter_cu_refs) const;
// Read DWARF CFI or .eh_frame data from |section|, belonging to
// |macho_reader|, and record it in |module|. If |eh_frame| is true,
@@ -139,28 +157,34 @@ class DumpSymbols {
const mach_o::Section &section,
bool eh_frame) const;
+ // The selection of what type of symbol data to read/write.
+ const SymbolData symbol_data_;
+
+ // Whether to handle references between compilation units.
+ const bool handle_inter_cu_refs_;
+
// The name of the file or bundle whose symbols this will dump.
// This is the path given to Read, for use in error messages.
- NSString *input_pathname_;
+ std::string input_pathname_;
// The name of the file this DumpSymbols will actually read debugging
// information from. Normally, this is the same as input_pathname_, but if
// filename refers to a dSYM bundle, then this is the resource file
// within that bundle.
- NSString *object_filename_;
+ std::string object_filename_;
// The complete contents of object_filename_, mapped into memory.
- NSData *contents_;
+ scoped_array<uint8_t> contents_;
- // A vector of fat_arch structures describing the object files
+ // A vector of SuperFatArch structures describing the object files
// object_filename_ contains. If object_filename_ refers to a fat binary,
// this may have more than one element; if it refers to a Mach-O file, this
// has exactly one element.
- vector<struct fat_arch> object_files_;
+ vector<SuperFatArch> object_files_;
// The object file in object_files_ selected to dump, or NULL if
// SetArchitecture hasn't been called yet.
- const struct fat_arch *selected_object_file_;
+ const SuperFatArch *selected_object_file_;
// A string that identifies the selected object file, for use in error
// messages. This is usually object_filename_, but if that refers to a
diff --git a/3rdParty/Breakpad/src/common/mac/file_id.cc b/3rdParty/Breakpad/src/common/mac/file_id.cc
index 50502e4..4661d5d 100644
--- a/3rdParty/Breakpad/src/common/mac/file_id.cc
+++ b/3rdParty/Breakpad/src/common/mac/file_id.cc
@@ -34,6 +34,7 @@
// Author: Dan Waylonis
#include <fcntl.h>
+#include <stdio.h>
#include <string.h>
#include <unistd.h>
@@ -45,7 +46,7 @@ using MacFileUtilities::MachoID;
namespace google_breakpad {
FileID::FileID(const char *path) {
- strlcpy(path_, path, sizeof(path_));
+ snprintf(path_, sizeof(path_), "%s", path);
}
bool FileID::FileIdentifier(unsigned char identifier[16]) {
@@ -61,7 +62,7 @@ bool FileID::FileIdentifier(unsigned char identifier[16]) {
unsigned char buffer[4096 * 2];
size_t buffer_size = sizeof(buffer);
while ((buffer_size = read(fd, buffer, buffer_size) > 0)) {
- MD5Update(&md5, buffer, buffer_size);
+ MD5Update(&md5, buffer, static_cast<unsigned>(buffer_size));
}
close(fd);
@@ -70,13 +71,15 @@ bool FileID::FileIdentifier(unsigned char identifier[16]) {
return true;
}
-bool FileID::MachoIdentifier(int cpu_type, unsigned char identifier[16]) {
+bool FileID::MachoIdentifier(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]) {
MachoID macho(path_);
- if (macho.UUIDCommand(cpu_type, identifier))
+ if (macho.UUIDCommand(cpu_type, cpu_subtype, identifier))
return true;
- return macho.MD5(cpu_type, identifier);
+ return macho.MD5(cpu_type, cpu_subtype, identifier);
}
// static
@@ -90,8 +93,10 @@ void FileID::ConvertIdentifierToString(const unsigned char identifier[16],
if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
buffer[buffer_idx++] = '-';
- buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
- buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
+ buffer[buffer_idx++] =
+ static_cast<char>((hi >= 10) ? ('A' + hi - 10) : ('0' + hi));
+ buffer[buffer_idx++] =
+ static_cast<char>((lo >= 10) ? ('A' + lo - 10) : ('0' + lo));
}
// NULL terminate
diff --git a/3rdParty/Breakpad/src/common/mac/file_id.h b/3rdParty/Breakpad/src/common/mac/file_id.h
index eb06b0d..1d6dfde 100644
--- a/3rdParty/Breakpad/src/common/mac/file_id.h
+++ b/3rdParty/Breakpad/src/common/mac/file_id.h
@@ -35,6 +35,7 @@
#define COMMON_MAC_FILE_ID_H__
#include <limits.h>
+#include <mach/machine.h>
namespace google_breakpad {
@@ -50,15 +51,18 @@ class FileID {
bool FileIdentifier(unsigned char identifier[16]);
// Treat the file as a mach-o file that will contain one or more archicture.
- // Accepted values for |cpu_type| (e.g., CPU_TYPE_X86 or CPU_TYPE_POWERPC)
- // are listed in /usr/include/mach/machine.h.
- // If |cpu_type| is 0, then the native cpu type is used.
- // Returns false if opening the file failed or if the |cpu_type| is not
- // present in the file.
+ // Accepted values for |cpu_type| and |cpu_subtype| (e.g., CPU_TYPE_X86 or
+ // CPU_TYPE_POWERPC) are listed in /usr/include/mach/machine.h.
+ // If |cpu_type| is 0, then the native cpu type is used. If |cpu_subtype| is
+ // CPU_SUBTYPE_MULTIPLE, the match is only done on |cpu_type|.
+ // Returns false if opening the file failed or if the |cpu_type|/|cpu_subtype|
+ // is not present in the file.
// Return the unique identifier in |identifier|.
// The current implementation will look for the (in order of priority):
// LC_UUID, LC_ID_DYLIB, or MD5 hash of the given |cpu_type|.
- bool MachoIdentifier(int cpu_type, unsigned char identifier[16]);
+ bool MachoIdentifier(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]);
// Convert the |identifier| data to a NULL terminated string. The string will
// be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE).
@@ -75,4 +79,3 @@ class FileID {
} // namespace google_breakpad
#endif // COMMON_MAC_FILE_ID_H__
-
diff --git a/3rdParty/Breakpad/src/common/mac/launch_reporter.cc b/3rdParty/Breakpad/src/common/mac/launch_reporter.cc
new file mode 100644
index 0000000..245be82
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/launch_reporter.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2014, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+namespace google_breakpad {
+
+void LaunchReporter(const char *reporter_executable_path,
+ const char *config_file_path) {
+ const char* argv[] = { reporter_executable_path, config_file_path, NULL };
+
+ // Launch the reporter
+ pid_t pid = fork();
+
+ if (pid == -1) {
+ perror("fork");
+ fprintf(stderr, "Failed to fork reporter process\n");
+ return;
+ }
+
+ // If we're in the child, load in our new executable and run.
+ // The parent will not wait for the child to complete.
+ if (pid == 0) {
+ execv(argv[0], (char* const*)argv);
+ perror("exec");
+ fprintf(stderr,
+ "Failed to launch reporter process from path %s\n",
+ reporter_executable_path);
+ unlink(config_file_path); // launch failed - get rid of config file
+ _exit(1);
+ }
+
+ // Wait until the Reporter child process exits.
+ //
+
+ // We'll use a timeout of one minute.
+ int timeout_count = 60; // 60 seconds
+
+ while (timeout_count-- > 0) {
+ int status;
+ pid_t result = waitpid(pid, &status, WNOHANG);
+
+ if (result == 0) {
+ // The child has not yet finished.
+ sleep(1);
+ } else if (result == -1) {
+ // error occurred.
+ break;
+ } else {
+ // child has finished
+ break;
+ }
+ }
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/mac/launch_reporter.h b/3rdParty/Breakpad/src/common/mac/launch_reporter.h
new file mode 100644
index 0000000..4531123
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/launch_reporter.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2014, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef COMMON_MAC_LAUNCH_REPORTER_H__
+#define COMMON_MAC_LAUNCH_REPORTER_H__
+
+namespace google_breakpad {
+
+// Launch the crash dump sender app.
+// |reporter_executable_path| is the path to the sender executable.
+// |config_file_path| is the path to the config file.
+void LaunchReporter(const char *reporter_executable_path,
+ const char *config_file_path);
+
+} // namespace google_breakpad
+
+#endif // COMMON_MAC_LAUNCH_REPORTER_H__
diff --git a/3rdParty/Breakpad/src/common/mac/macho_id.cc b/3rdParty/Breakpad/src/common/mac/macho_id.cc
index abe1fab..c396ad8 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_id.cc
+++ b/3rdParty/Breakpad/src/common/mac/macho_id.cc
@@ -33,17 +33,15 @@
//
// Author: Dan Waylonis
-extern "C" { // necessary for Leopard
- #include <fcntl.h>
- #include <mach-o/loader.h>
- #include <mach-o/swap.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <unistd.h>
-}
+
+#include <fcntl.h>
+#include <mach-o/loader.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "common/mac/macho_id.h"
#include "common/mac/macho_walker.h"
@@ -61,7 +59,7 @@ MachoID::MachoID(const char *path)
crc_(0),
md5_context_(),
update_function_(NULL) {
- strlcpy(path_, path, sizeof(path_));
+ snprintf(path_, sizeof(path_), "%s", path);
}
MachoID::MachoID(const char *path, void *memory, size_t size)
@@ -70,7 +68,7 @@ MachoID::MachoID(const char *path, void *memory, size_t size)
crc_(0),
md5_context_(),
update_function_(NULL) {
- strlcpy(path_, path, sizeof(path_));
+ snprintf(path_, sizeof(path_), "%s", path);
}
MachoID::~MachoID() {
@@ -125,7 +123,7 @@ void MachoID::UpdateCRC(unsigned char *bytes, size_t size) {
}
void MachoID::UpdateMD5(unsigned char *bytes, size_t size) {
- MD5Update(&md5_context_, bytes, size);
+ MD5Update(&md5_context_, bytes, static_cast<unsigned>(size));
}
void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) {
@@ -153,10 +151,12 @@ void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) {
}
}
-bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) {
+bool MachoID::UUIDCommand(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char bytes[16]) {
struct breakpad_uuid_command uuid_cmd;
uuid_cmd.cmd = 0;
- if (!WalkHeader(cpu_type, UUIDWalkerCB, &uuid_cmd))
+ if (!WalkHeader(cpu_type, cpu_subtype, UUIDWalkerCB, &uuid_cmd))
return false;
// If we found the command, we'll have initialized the uuid_command
@@ -169,10 +169,12 @@ bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) {
return false;
}
-bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) {
+bool MachoID::IDCommand(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]) {
struct dylib_command dylib_cmd;
dylib_cmd.cmd = 0;
- if (!WalkHeader(cpu_type, IDWalkerCB, &dylib_cmd))
+ if (!WalkHeader(cpu_type, cpu_subtype, IDWalkerCB, &dylib_cmd))
return false;
// If we found the command, we'll have initialized the dylib_command
@@ -210,37 +212,38 @@ bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) {
return false;
}
-uint32_t MachoID::Adler32(int cpu_type) {
+uint32_t MachoID::Adler32(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
update_function_ = &MachoID::UpdateCRC;
crc_ = 0;
- if (!WalkHeader(cpu_type, WalkerCB, this))
+ if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
return 0;
return crc_;
}
-bool MachoID::MD5(int cpu_type, unsigned char identifier[16]) {
+bool MachoID::MD5(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, unsigned char identifier[16]) {
update_function_ = &MachoID::UpdateMD5;
MD5Init(&md5_context_);
- if (!WalkHeader(cpu_type, WalkerCB, this))
+ if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
return false;
MD5Final(identifier, &md5_context_);
return true;
}
-bool MachoID::WalkHeader(int cpu_type,
+bool MachoID::WalkHeader(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
MachoWalker::LoadCommandCallback callback,
void *context) {
if (memory_) {
MachoWalker walker(memory_, memory_size_, callback, context);
- return walker.WalkHeader(cpu_type);
+ return walker.WalkHeader(cpu_type, cpu_subtype);
} else {
MachoWalker walker(path_, callback, context);
- return walker.WalkHeader(cpu_type);
+ return walker.WalkHeader(cpu_type, cpu_subtype);
}
}
@@ -256,7 +259,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- swap_segment_command(&seg, NXHostByteOrder());
+ breakpad_swap_segment_command(&seg);
struct mach_header_64 header;
off_t header_offset;
@@ -273,7 +276,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- swap_section(&sec, 1, NXHostByteOrder());
+ breakpad_swap_section(&sec, 1);
// sections of type S_ZEROFILL are "virtual" and contain no data
// in the file itself
@@ -289,7 +292,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- breakpad_swap_segment_command_64(&seg64, NXHostByteOrder());
+ breakpad_swap_segment_command_64(&seg64);
struct mach_header_64 header;
off_t header_offset;
@@ -306,7 +309,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- breakpad_swap_section_64(&sec64, 1, NXHostByteOrder());
+ breakpad_swap_section_64(&sec64, 1);
// sections of type S_ZEROFILL are "virtual" and contain no data
// in the file itself
@@ -335,7 +338,7 @@ bool MachoID::UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- breakpad_swap_uuid_command(uuid_cmd, NXHostByteOrder());
+ breakpad_swap_uuid_command(uuid_cmd);
return false;
}
@@ -354,7 +357,7 @@ bool MachoID::IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
return false;
if (swap)
- swap_dylib_command(dylib_cmd, NXHostByteOrder());
+ breakpad_swap_dylib_command(dylib_cmd);
return false;
}
diff --git a/3rdParty/Breakpad/src/common/mac/macho_id.h b/3rdParty/Breakpad/src/common/mac/macho_id.h
index ccb126d..1037549 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_id.h
+++ b/3rdParty/Breakpad/src/common/mac/macho_id.h
@@ -35,6 +35,7 @@
#define COMMON_MAC_MACHO_ID_H__
#include <limits.h>
+#include <mach/machine.h>
#include <mach-o/loader.h>
#include "common/mac/macho_walker.h"
@@ -48,22 +49,32 @@ class MachoID {
MachoID(const char *path, void *memory, size_t size);
~MachoID();
- // For the given |cpu_type|, return a UUID from the LC_UUID command.
+ // For the given |cpu_type| and |cpu_subtype|, return a UUID from the LC_UUID
+ // command.
// Return false if there isn't a LC_UUID command.
- bool UUIDCommand(int cpu_type, unsigned char identifier[16]);
+ bool UUIDCommand(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]);
- // For the given |cpu_type|, return a UUID from the LC_ID_DYLIB command.
+ // For the given |cpu_type| and |cpu_subtype|, return a UUID from the
+ // LC_ID_DYLIB command.
// Return false if there isn't a LC_ID_DYLIB command.
- bool IDCommand(int cpu_type, unsigned char identifier[16]);
+ bool IDCommand(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]);
- // For the given |cpu_type|, return the Adler32 CRC for the mach-o data
- // segment(s).
+ // For the given |cpu_type| and |cpu_subtype|, return the Adler32 CRC for the
+ // mach-o data segment(s).
// Return 0 on error (e.g., if the file is not a mach-o file)
- uint32_t Adler32(int cpu_type);
+ uint32_t Adler32(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype);
- // For the given |cpu_type|, return the MD5 for the mach-o data segment(s).
+ // For the given |cpu_type|, and |cpu_subtype| return the MD5 for the mach-o
+ // data segment(s).
// Return true on success, false otherwise
- bool MD5(int cpu_type, unsigned char identifier[16]);
+ bool MD5(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ unsigned char identifier[16]);
private:
// Signature of class member function to be called with data read from file
@@ -81,8 +92,8 @@ class MachoID {
void Update(MachoWalker *walker, off_t offset, size_t size);
// Factory for the MachoWalker
- bool WalkHeader(int cpu_type, MachoWalker::LoadCommandCallback callback,
- void *context);
+ bool WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype,
+ MachoWalker::LoadCommandCallback callback, void *context);
// The callback from the MachoWalker for CRC and MD5
static bool WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
diff --git a/3rdParty/Breakpad/src/common/mac/macho_reader.cc b/3rdParty/Breakpad/src/common/mac/macho_reader.cc
index f1f0a17..52f3c41 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_reader.cc
+++ b/3rdParty/Breakpad/src/common/mac/macho_reader.cc
@@ -43,6 +43,10 @@
#define CPU_TYPE_ARM 12
#endif
+#if !defined(CPU_TYPE_ARM_64)
+#define CPU_TYPE_ARM_64 16777228
+#endif
+
namespace google_breakpad {
namespace mach_o {
@@ -97,22 +101,26 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) {
// Read the list of object files.
object_files_.resize(object_files_count);
for (size_t i = 0; i < object_files_count; i++) {
- struct fat_arch *objfile = &object_files_[i];
+ struct fat_arch objfile;
// Read this object file entry, byte-swapping as appropriate.
- cursor >> objfile->cputype
- >> objfile->cpusubtype
- >> objfile->offset
- >> objfile->size
- >> objfile->align;
+ cursor >> objfile.cputype
+ >> objfile.cpusubtype
+ >> objfile.offset
+ >> objfile.size
+ >> objfile.align;
+
+ SuperFatArch super_fat_arch(objfile);
+ object_files_[i] = super_fat_arch;
+
if (!cursor) {
reporter_->TooShort();
return false;
}
// Does the file actually have the bytes this entry refers to?
size_t fat_size = buffer_.Size();
- if (objfile->offset > fat_size ||
- objfile->size > fat_size - objfile->offset) {
+ if (objfile.offset > fat_size ||
+ objfile.size > fat_size - objfile.offset) {
reporter_->MisplacedObjectFile();
return false;
}
@@ -135,16 +143,14 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) {
}
object_files_[0].offset = 0;
- object_files_[0].size = static_cast<uint32_t>(buffer_.Size());
+ object_files_[0].size = static_cast<uint64_t>(buffer_.Size());
// This alignment is correct for 32 and 64-bit x86 and ppc.
// See get_align in the lipo source for other architectures:
// http://www.opensource.apple.com/source/cctools/cctools-773/misc/lipo.c
object_files_[0].align = 12; // 2^12 == 4096
-
return true;
}
}
-
reporter_->BadHeader();
return false;
}
@@ -175,15 +181,15 @@ void Reader::Reporter::LoadCommandRegionTruncated() {
void Reader::Reporter::LoadCommandsOverrun(size_t claimed, size_t i,
LoadCommandType type) {
- fprintf(stderr, "%s: file's header claims there are %ld"
- " load commands, but load command #%ld",
+ fprintf(stderr, "%s: file's header claims there are %zu"
+ " load commands, but load command #%zu",
filename_.c_str(), claimed, i);
if (type) fprintf(stderr, ", of type %d,", type);
fprintf(stderr, " extends beyond the end of the load command region\n");
}
void Reader::Reporter::LoadCommandTooShort(size_t i, LoadCommandType type) {
- fprintf(stderr, "%s: the contents of load command #%ld, of type %d,"
+ fprintf(stderr, "%s: the contents of load command #%zu, of type %d,"
" extend beyond the size given in the load command's header\n",
filename_.c_str(), i, type);
}
@@ -242,6 +248,7 @@ bool Reader::Read(const uint8_t *buffer,
case CPU_TYPE_POWERPC:
expected_magic = MH_MAGIC;
break;
+ case CPU_TYPE_ARM_64:
case CPU_TYPE_X86_64:
expected_magic = MH_CIGAM_64;
break;
@@ -310,7 +317,7 @@ bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const {
// remainder of the load command series.
ByteBuffer command(list_cursor.here(), list_cursor.Available());
ByteCursor cursor(&command, big_endian_);
-
+
// Read the command type and size --- fields common to all commands.
uint32_t type, size;
if (!(cursor >> type)) {
@@ -395,7 +402,7 @@ bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const {
return false;
break;
}
-
+
default: {
if (!handler->UnknownCommand(type, command))
return false;
@@ -414,7 +421,7 @@ class Reader::SegmentFinder : public LoadCommandHandler {
public:
// Create a load command handler that looks for a segment named NAME,
// and sets SEGMENT to describe it if found.
- SegmentFinder(const string &name, Segment *segment)
+ SegmentFinder(const string &name, Segment *segment)
: name_(name), segment_(segment), found_() { }
// Return true if the traversal found the segment, false otherwise.
@@ -474,10 +481,12 @@ bool Reader::WalkSegmentSections(const Segment &segment,
reporter_->SectionsMissing(segment.name);
return false;
}
- if ((section.flags & SECTION_TYPE) == S_ZEROFILL) {
+ const uint32_t section_type = section.flags & SECTION_TYPE;
+ if (section_type == S_ZEROFILL || section_type == S_THREAD_LOCAL_ZEROFILL ||
+ section_type == S_GB_ZEROFILL) {
// Zero-fill sections have a size, but no contents.
section.contents.start = section.contents.end = NULL;
- } else if (segment.contents.start == NULL &&
+ } else if (segment.contents.start == NULL &&
segment.contents.end == NULL) {
// Mach-O files in .dSYM bundles have the contents of the loaded
// segments removed, and their file offsets and file sizes zeroed
diff --git a/3rdParty/Breakpad/src/common/mac/macho_reader.h b/3rdParty/Breakpad/src/common/mac/macho_reader.h
index 7537648..30db742 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_reader.h
+++ b/3rdParty/Breakpad/src/common/mac/macho_reader.h
@@ -47,6 +47,7 @@
#include <vector>
#include "common/byte_cursor.h"
+#include "common/mac/super_fat_arch.h"
namespace google_breakpad {
namespace mach_o {
@@ -93,7 +94,7 @@ class FatReader {
// complete header, or the header implies that contents are present
// beyond the actual end of the file.
virtual void TooShort();
-
+
private:
// The filename to which the reader should attribute problems.
string filename_;
@@ -101,7 +102,7 @@ class FatReader {
// Create a fat binary file reader that uses |reporter| to report problems.
explicit FatReader(Reporter *reporter) : reporter_(reporter) { }
-
+
// Read the |size| bytes at |buffer| as a fat binary file. On success,
// return true; on failure, report the problem to reporter_ and return
// false.
@@ -111,13 +112,13 @@ class FatReader {
// single object file is the Mach-O file.
bool Read(const uint8_t *buffer, size_t size);
- // Return an array of 'struct fat_arch' structures describing the
+ // Return an array of 'SuperFatArch' structures describing the
// object files present in this fat binary file. Set |size| to the
// number of elements in the array.
//
- // Assuming Read returned true, the entries are validated: it is
- // safe to assume that the offsets and sizes in each 'struct
- // fat_arch' refer to subranges of the bytes passed to Read.
+ // Assuming Read returned true, the entries are validated: it is safe to
+ // assume that the offsets and sizes in each SuperFatArch refer to subranges
+ // of the bytes passed to Read.
//
// If there are no object files in this fat binary, then this
// function can return NULL.
@@ -129,7 +130,7 @@ class FatReader {
// possible to use the result with OS X functions like NXFindBestFatArch,
// so that the symbol dumper will behave consistently with other OS X
// utilities that work with fat binaries.
- const struct fat_arch *object_files(size_t *count) const {
+ const SuperFatArch* object_files(size_t *count) const {
*count = object_files_.size();
if (object_files_.size() > 0)
return &object_files_[0];
@@ -149,7 +150,7 @@ class FatReader {
// The list of object files in this binary.
// object_files_.size() == fat_header.nfat_arch
- vector<struct fat_arch> object_files_;
+ vector<SuperFatArch> object_files_;
};
// A segment in a Mach-O file. All these fields have been byte-swapped as
@@ -177,7 +178,7 @@ struct Segment {
// The maximum and initial VM protection of this segment's contents.
uint32_t maxprot;
uint32_t initprot;
-
+
// The number of sections in section_list.
uint32_t nsects;
@@ -376,7 +377,7 @@ class Reader {
return Read(buffer.start,
buffer.Size(),
expected_cpu_type,
- expected_cpu_subtype);
+ expected_cpu_subtype);
}
// Return this file's characteristics, as found in the Mach-O header.
diff --git a/3rdParty/Breakpad/src/common/mac/macho_reader_unittest.cc b/3rdParty/Breakpad/src/common/mac/macho_reader_unittest.cc
new file mode 100644
index 0000000..d8459d8
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/macho_reader_unittest.cc
@@ -0,0 +1,1902 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// macho_reader_unittest.cc: Unit tests for google_breakpad::Mach_O::FatReader
+// and google_breakpad::Mach_O::Reader.
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/mac/macho_reader.h"
+#include "common/test_assembler.h"
+
+namespace mach_o = google_breakpad::mach_o;
+namespace test_assembler = google_breakpad::test_assembler;
+
+using mach_o::FatReader;
+using mach_o::FileFlags;
+using mach_o::FileType;
+using mach_o::LoadCommandType;
+using mach_o::Reader;
+using mach_o::Section;
+using mach_o::SectionMap;
+using mach_o::Segment;
+using test_assembler::Endianness;
+using test_assembler::Label;
+using test_assembler::kBigEndian;
+using test_assembler::kLittleEndian;
+using test_assembler::kUnsetEndian;
+using google_breakpad::ByteBuffer;
+using std::map;
+using std::string;
+using std::vector;
+using testing::AllOf;
+using testing::DoAll;
+using testing::Field;
+using testing::InSequence;
+using testing::Matcher;
+using testing::Return;
+using testing::SaveArg;
+using testing::Test;
+using testing::_;
+
+
+// Mock classes for the reader's various reporters and handlers.
+
+class MockFatReaderReporter: public FatReader::Reporter {
+ public:
+ MockFatReaderReporter(const string &filename)
+ : FatReader::Reporter(filename) { }
+ MOCK_METHOD0(BadHeader, void());
+ MOCK_METHOD0(MisplacedObjectFile, void());
+ MOCK_METHOD0(TooShort, void());
+};
+
+class MockReaderReporter: public Reader::Reporter {
+ public:
+ MockReaderReporter(const string &filename) : Reader::Reporter(filename) { }
+ MOCK_METHOD0(BadHeader, void());
+ MOCK_METHOD4(CPUTypeMismatch, void(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ cpu_type_t expected_cpu_type,
+ cpu_subtype_t expected_cpu_subtype));
+ MOCK_METHOD0(HeaderTruncated, void());
+ MOCK_METHOD0(LoadCommandRegionTruncated, void());
+ MOCK_METHOD3(LoadCommandsOverrun, void(size_t claimed, size_t i,
+ LoadCommandType type));
+ MOCK_METHOD2(LoadCommandTooShort, void(size_t i, LoadCommandType type));
+ MOCK_METHOD1(SectionsMissing, void(const string &name));
+ MOCK_METHOD1(MisplacedSegmentData, void(const string &name));
+ MOCK_METHOD2(MisplacedSectionData, void(const string &section,
+ const string &segment));
+ MOCK_METHOD0(MisplacedSymbolTable, void());
+ MOCK_METHOD1(UnsupportedCPUType, void(cpu_type_t cpu_type));
+};
+
+class MockLoadCommandHandler: public Reader::LoadCommandHandler {
+ public:
+ MOCK_METHOD2(UnknownCommand, bool(LoadCommandType, const ByteBuffer &));
+ MOCK_METHOD1(SegmentCommand, bool(const Segment &));
+ MOCK_METHOD2(SymtabCommand, bool(const ByteBuffer &, const ByteBuffer &));
+};
+
+class MockSectionHandler: public Reader::SectionHandler {
+ public:
+ MOCK_METHOD1(HandleSection, bool(const Section &section));
+};
+
+
+// Tests for mach_o::FatReader.
+
+// Since the effect of these functions is to write to stderr, the
+// results of these tests must be inspected by hand.
+TEST(FatReaderReporter, BadHeader) {
+ FatReader::Reporter reporter("filename");
+ reporter.BadHeader();
+}
+
+TEST(FatReaderReporter, MisplacedObjectFile) {
+ FatReader::Reporter reporter("filename");
+ reporter.MisplacedObjectFile();
+}
+
+TEST(FatReaderReporter, TooShort) {
+ FatReader::Reporter reporter("filename");
+ reporter.TooShort();
+}
+
+TEST(MachOReaderReporter, BadHeader) {
+ Reader::Reporter reporter("filename");
+ reporter.BadHeader();
+}
+
+TEST(MachOReaderReporter, CPUTypeMismatch) {
+ Reader::Reporter reporter("filename");
+ reporter.CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL,
+ CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL);
+}
+
+TEST(MachOReaderReporter, HeaderTruncated) {
+ Reader::Reporter reporter("filename");
+ reporter.HeaderTruncated();
+}
+
+TEST(MachOReaderReporter, LoadCommandRegionTruncated) {
+ Reader::Reporter reporter("filename");
+ reporter.LoadCommandRegionTruncated();
+}
+
+TEST(MachOReaderReporter, LoadCommandsOverrun) {
+ Reader::Reporter reporter("filename");
+ reporter.LoadCommandsOverrun(10, 9, LC_DYSYMTAB);
+ reporter.LoadCommandsOverrun(10, 9, 0);
+}
+
+TEST(MachOReaderReporter, LoadCommandTooShort) {
+ Reader::Reporter reporter("filename");
+ reporter.LoadCommandTooShort(11, LC_SYMTAB);
+}
+
+TEST(MachOReaderReporter, SectionsMissing) {
+ Reader::Reporter reporter("filename");
+ reporter.SectionsMissing("segment name");
+}
+
+TEST(MachOReaderReporter, MisplacedSegmentData) {
+ Reader::Reporter reporter("filename");
+ reporter.MisplacedSegmentData("segment name");
+}
+
+TEST(MachOReaderReporter, MisplacedSectionData) {
+ Reader::Reporter reporter("filename");
+ reporter.MisplacedSectionData("section name", "segment name");
+}
+
+TEST(MachOReaderReporter, MisplacedSymbolTable) {
+ Reader::Reporter reporter("filename");
+ reporter.MisplacedSymbolTable();
+}
+
+TEST(MachOReaderReporter, UnsupportedCPUType) {
+ Reader::Reporter reporter("filename");
+ reporter.UnsupportedCPUType(CPU_TYPE_HPPA);
+}
+
+struct FatReaderFixture {
+ FatReaderFixture()
+ : fat(kBigEndian),
+ reporter("reporter filename"),
+ reader(&reporter), object_files() {
+ EXPECT_CALL(reporter, BadHeader()).Times(0);
+ EXPECT_CALL(reporter, TooShort()).Times(0);
+
+ // here, start, and Mark are file offsets in 'fat'.
+ fat.start() = 0;
+ }
+ // Append a 'fat_arch' entry to 'fat', with the given field values.
+ void AppendFatArch(cpu_type_t type, cpu_subtype_t subtype,
+ Label offset, Label size, uint32_t align) {
+ fat
+ .B32(type)
+ .B32(subtype)
+ .B32(offset)
+ .B32(size)
+ .B32(align);
+ }
+ // Append |n| dummy 'fat_arch' entries to 'fat'. The cpu type and
+ // subtype have unrealistic values.
+ void AppendDummyArchEntries(int n) {
+ for (int i = 0; i < n; i++)
+ AppendFatArch(0xb68ad617, 0x715a0840, 0, 0, 1);
+ }
+ void ReadFat(bool expect_parse_success = true) {
+ ASSERT_TRUE(fat.GetContents(&contents));
+ fat_bytes = reinterpret_cast<const uint8_t *>(contents.data());
+ if (expect_parse_success) {
+ EXPECT_TRUE(reader.Read(fat_bytes, contents.size()));
+ size_t fat_files_count;
+ const SuperFatArch* fat_files = reader.object_files(&fat_files_count);
+ object_files.resize(fat_files_count);
+ for (size_t i = 0; i < fat_files_count; ++i) {
+ EXPECT_TRUE(fat_files[i].ConvertToFatArch(&object_files[i]));
+ }
+ }
+ else
+ EXPECT_FALSE(reader.Read(fat_bytes, contents.size()));
+ }
+ test_assembler::Section fat;
+ MockFatReaderReporter reporter;
+ FatReader reader;
+ string contents;
+ const uint8_t *fat_bytes;
+ vector<struct fat_arch> object_files;
+};
+
+class FatReaderTest: public FatReaderFixture, public Test { };
+
+TEST_F(FatReaderTest, BadMagic) {
+ EXPECT_CALL(reporter, BadHeader()).Times(1);
+ fat
+ .B32(0xcafed00d) // magic number (incorrect)
+ .B32(10); // number of architectures
+ AppendDummyArchEntries(10);
+ ReadFat(false);
+}
+
+TEST_F(FatReaderTest, HeaderTooShort) {
+ EXPECT_CALL(reporter, TooShort()).Times(1);
+ fat
+ .B32(0xcafebabe); // magic number
+ ReadFat(false);
+}
+
+TEST_F(FatReaderTest, ObjectListTooShort) {
+ EXPECT_CALL(reporter, TooShort()).Times(1);
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(10); // number of architectures
+ AppendDummyArchEntries(9); // nine dummy architecture entries...
+ fat // and a tenth, missing a byte at the end
+ .B32(0x3d46c8fc) // cpu type
+ .B32(0x8a7bfb01) // cpu subtype
+ .B32(0) // offset
+ .B32(0) // size
+ .Append(3, '*'); // one byte short of a four-byte alignment
+ ReadFat(false);
+}
+
+TEST_F(FatReaderTest, DataTooShort) {
+ EXPECT_CALL(reporter, MisplacedObjectFile()).Times(1);
+ Label arch_data;
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(1); // number of architectures
+ AppendFatArch(0xb4d4a366, 0x4ba4f525, arch_data, 40, 0);
+ fat
+ .Mark(&arch_data) // file data begins here
+ .Append(30, '*'); // only 30 bytes, not 40 as header claims
+ ReadFat(false);
+}
+
+TEST_F(FatReaderTest, NoObjectFiles) {
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(0); // number of architectures
+ ReadFat();
+ EXPECT_EQ(0U, object_files.size());
+}
+
+TEST_F(FatReaderTest, OneObjectFile) {
+ Label obj1_offset;
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(1); // number of architectures
+ // First object file list entry
+ AppendFatArch(0x5e3a6e91, 0x52ccd852, obj1_offset, 0x42, 0x355b15b2);
+ // First object file data
+ fat
+ .Mark(&obj1_offset)
+ .Append(0x42, '*'); // dummy contents
+ ReadFat();
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x5e3a6e91, object_files[0].cputype);
+ EXPECT_EQ(0x52ccd852, object_files[0].cpusubtype);
+ EXPECT_EQ(obj1_offset.Value(), object_files[0].offset);
+ EXPECT_EQ(0x42U, object_files[0].size);
+ EXPECT_EQ(0x355b15b2U, object_files[0].align);
+}
+
+TEST_F(FatReaderTest, ThreeObjectFiles) {
+ Label obj1, obj2, obj3;
+ fat
+ .B32(0xcafebabe) // magic number
+ .B32(3); // number of architectures
+ // Three object file list entries.
+ AppendFatArch(0x0cb92c30, 0x6a159a71, obj1, 0xfb4, 0x2615dbe8);
+ AppendFatArch(0x0f3f1cbb, 0x6c55e90f, obj2, 0xc31, 0x83af6ffd);
+ AppendFatArch(0x3717276d, 0x10ecdc84, obj3, 0x4b3, 0x035267d7);
+ fat
+ // First object file data
+ .Mark(&obj1)
+ .Append(0xfb4, '*') // dummy contents
+ // Second object file data
+ .Mark(&obj2)
+ .Append(0xc31, '%') // dummy contents
+ // Third object file data
+ .Mark(&obj3)
+ .Append(0x4b3, '^'); // dummy contents
+
+ ReadFat();
+
+ ASSERT_EQ(3U, object_files.size());
+
+ // First object file.
+ EXPECT_EQ(0x0cb92c30, object_files[0].cputype);
+ EXPECT_EQ(0x6a159a71, object_files[0].cpusubtype);
+ EXPECT_EQ(obj1.Value(), object_files[0].offset);
+ EXPECT_EQ(0xfb4U, object_files[0].size);
+ EXPECT_EQ(0x2615dbe8U, object_files[0].align);
+
+ // Second object file.
+ EXPECT_EQ(0x0f3f1cbb, object_files[1].cputype);
+ EXPECT_EQ(0x6c55e90f, object_files[1].cpusubtype);
+ EXPECT_EQ(obj2.Value(), object_files[1].offset);
+ EXPECT_EQ(0xc31U, object_files[1].size);
+ EXPECT_EQ(0x83af6ffdU, object_files[1].align);
+
+ // Third object file.
+ EXPECT_EQ(0x3717276d, object_files[2].cputype);
+ EXPECT_EQ(0x10ecdc84, object_files[2].cpusubtype);
+ EXPECT_EQ(obj3.Value(), object_files[2].offset);
+ EXPECT_EQ(0x4b3U, object_files[2].size);
+ EXPECT_EQ(0x035267d7U, object_files[2].align);
+}
+
+TEST_F(FatReaderTest, BigEndianMachO32) {
+ fat.set_endianness(kBigEndian);
+ fat
+ .D32(0xfeedface) // Mach-O file magic number
+ .D32(0x1a9d0518) // cpu type
+ .D32(0x1b779357) // cpu subtype
+ .D32(0x009df67e) // file type
+ .D32(0) // no load commands
+ .D32(0) // the load commands occupy no bytes
+ .D32(0x21987a99); // flags
+
+ ReadFat();
+
+ // FatReader should treat a Mach-O file as if it were a fat binary file
+ // containing one object file --- the whole thing.
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x1a9d0518, object_files[0].cputype);
+ EXPECT_EQ(0x1b779357, object_files[0].cpusubtype);
+ EXPECT_EQ(0U, object_files[0].offset);
+ EXPECT_EQ(contents.size(), object_files[0].size);
+}
+
+TEST_F(FatReaderTest, BigEndianMachO64) {
+ fat.set_endianness(kBigEndian);
+ fat
+ .D32(0xfeedfacf) // Mach-O 64-bit file magic number
+ .D32(0x5aff8487) // cpu type
+ .D32(0x4c6a57f7) // cpu subtype
+ .D32(0x4392d2c8) // file type
+ .D32(0) // no load commands
+ .D32(0) // the load commands occupy no bytes
+ .D32(0x1b033eea); // flags
+
+ ReadFat();
+
+ // FatReader should treat a Mach-O file as if it were a fat binary file
+ // containing one object file --- the whole thing.
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x5aff8487, object_files[0].cputype);
+ EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype);
+ EXPECT_EQ(0U, object_files[0].offset);
+ EXPECT_EQ(contents.size(), object_files[0].size);
+}
+
+TEST_F(FatReaderTest, LittleEndianMachO32) {
+ fat.set_endianness(kLittleEndian);
+ fat
+ .D32(0xfeedface) // Mach-O file magic number
+ .D32(0x1a9d0518) // cpu type
+ .D32(0x1b779357) // cpu subtype
+ .D32(0x009df67e) // file type
+ .D32(0) // no load commands
+ .D32(0) // the load commands occupy no bytes
+ .D32(0x21987a99); // flags
+
+ ReadFat();
+
+ // FatReader should treat a Mach-O file as if it were a fat binary file
+ // containing one object file --- the whole thing.
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x1a9d0518, object_files[0].cputype);
+ EXPECT_EQ(0x1b779357, object_files[0].cpusubtype);
+ EXPECT_EQ(0U, object_files[0].offset);
+ EXPECT_EQ(contents.size(), object_files[0].size);
+}
+
+TEST_F(FatReaderTest, LittleEndianMachO64) {
+ fat.set_endianness(kLittleEndian);
+ fat
+ .D32(0xfeedfacf) // Mach-O 64-bit file magic number
+ .D32(0x5aff8487) // cpu type
+ .D32(0x4c6a57f7) // cpu subtype
+ .D32(0x4392d2c8) // file type
+ .D32(0) // no load commands
+ .D32(0) // the load commands occupy no bytes
+ .D32(0x1b033eea); // flags
+
+ ReadFat();
+
+ // FatReader should treat a Mach-O file as if it were a fat binary file
+ // containing one object file --- the whole thing.
+ ASSERT_EQ(1U, object_files.size());
+ EXPECT_EQ(0x5aff8487, object_files[0].cputype);
+ EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype);
+ EXPECT_EQ(0U, object_files[0].offset);
+ EXPECT_EQ(contents.size(), object_files[0].size);
+}
+
+TEST_F(FatReaderTest, IncompleteMach) {
+ fat.set_endianness(kLittleEndian);
+ fat
+ .D32(0xfeedfacf) // Mach-O 64-bit file magic number
+ .D32(0x5aff8487); // cpu type
+ // Truncated!
+
+ EXPECT_CALL(reporter, TooShort()).WillOnce(Return());
+
+ ReadFat(false);
+}
+
+
+// General mach_o::Reader tests.
+
+// Dynamically scoped configuration data.
+class WithConfiguration {
+ public:
+ // Establish the given parameters as the default for SizedSections
+ // created within the dynamic scope of this instance.
+ WithConfiguration(Endianness endianness, size_t word_size)
+ : endianness_(endianness), word_size_(word_size), saved_(current_) {
+ current_ = this;
+ }
+ ~WithConfiguration() { current_ = saved_; }
+ static Endianness endianness() {
+ assert(current_);
+ return current_->endianness_;
+ }
+ static size_t word_size() {
+ assert(current_);
+ return current_->word_size_;
+ }
+
+ private:
+ // The innermost WithConfiguration in whose dynamic scope we are
+ // currently executing.
+ static WithConfiguration *current_;
+
+ // The innermost WithConfiguration whose dynamic scope encloses this
+ // WithConfiguration.
+ Endianness endianness_;
+ size_t word_size_;
+ WithConfiguration *saved_;
+};
+
+WithConfiguration *WithConfiguration::current_ = NULL;
+
+// A test_assembler::Section with a size that we can cite. The start(),
+// Here() and Mark() member functions of a SizedSection always represent
+// offsets within the overall file.
+class SizedSection: public test_assembler::Section {
+ public:
+ // Construct a section of the given endianness and word size.
+ explicit SizedSection(Endianness endianness, size_t word_size)
+ : test_assembler::Section(endianness), word_size_(word_size) {
+ assert(word_size_ == 32 || word_size_ == 64);
+ }
+ SizedSection()
+ : test_assembler::Section(WithConfiguration::endianness()),
+ word_size_(WithConfiguration::word_size()) {
+ assert(word_size_ == 32 || word_size_ == 64);
+ }
+
+ // Access/set this section's word size.
+ size_t word_size() const { return word_size_; }
+ void set_word_size(size_t word_size) {
+ assert(word_size_ == 32 || word_size_ == 64);
+ word_size_ = word_size;
+ }
+
+ // Return a label representing the size this section will have when it
+ // is Placed in some containing section.
+ Label final_size() const { return final_size_; }
+
+ // Append SECTION to the end of this section, and call its Finish member.
+ // Return a reference to this section.
+ SizedSection &Place(SizedSection *section) {
+ assert(section->endianness() == endianness());
+ section->Finish();
+ section->start() = Here();
+ test_assembler::Section::Append(*section);
+ return *this;
+ }
+
+ protected:
+ // Mark this section's contents as complete. For plain SizedSections, we
+ // set SECTION's start to its position in this section, and its final_size
+ // label to its current size. Derived classes can extend this as needed
+ // for their additional semantics.
+ virtual void Finish() {
+ final_size_ = Size();
+ }
+
+ // The word size for this data: either 32 or 64.
+ size_t word_size_;
+
+ private:
+ // This section's final size, set when we are placed in some other
+ // SizedSection.
+ Label final_size_;
+};
+
+// A SizedSection that is loaded into memory at a particular address.
+class LoadedSection: public SizedSection {
+ public:
+ explicit LoadedSection(Label address = Label()) : address_(address) { }
+
+ // Return a label representing this section's address.
+ Label address() const { return address_; }
+
+ // Placing a loaded section within a loaded section sets the relationship
+ // between their addresses.
+ LoadedSection &Place(LoadedSection *section) {
+ section->address() = address() + Size();
+ SizedSection::Place(section);
+ return *this;
+ }
+
+ protected:
+ // The address at which this section's contents will be loaded.
+ Label address_;
+};
+
+// A SizedSection representing a segment load command.
+class SegmentLoadCommand: public SizedSection {
+ public:
+ SegmentLoadCommand() : section_count_(0) { }
+
+ // Append a segment load command header with the given characteristics.
+ // The load command will refer to CONTENTS, which must be Placed in the
+ // file separately, at the desired position. Return a reference to this
+ // section.
+ SegmentLoadCommand &Header(const string &name, const LoadedSection &contents,
+ uint32_t maxprot, uint32_t initprot,
+ uint32_t flags) {
+ assert(contents.word_size() == word_size());
+ D32(word_size() == 32 ? LC_SEGMENT : LC_SEGMENT_64);
+ D32(final_size());
+ AppendCString(name, 16);
+ Append(endianness(), word_size() / 8, contents.address());
+ Append(endianness(), word_size() / 8, vmsize_);
+ Append(endianness(), word_size() / 8, contents.start());
+ Append(endianness(), word_size() / 8, contents.final_size());
+ D32(maxprot);
+ D32(initprot);
+ D32(final_section_count_);
+ D32(flags);
+
+ content_final_size_ = contents.final_size();
+
+ return *this;
+ }
+
+ // Return a label representing the size of this segment when loaded into
+ // memory. If this label is still undefined by the time we place this
+ // segment, it defaults to the final size of the segment's in-file
+ // contents. Return a reference to this load command.
+ Label &vmsize() { return vmsize_; }
+
+ // Add a section entry with the given characteristics to this segment
+ // load command. Return a reference to this. The section entry will refer
+ // to CONTENTS, which must be Placed in the segment's contents
+ // separately, at the desired position.
+ SegmentLoadCommand &AppendSectionEntry(const string &section_name,
+ const string &segment_name,
+ uint32_t alignment, uint32_t flags,
+ const LoadedSection &contents) {
+ AppendCString(section_name, 16);
+ AppendCString(segment_name, 16);
+ Append(endianness(), word_size() / 8, contents.address());
+ Append(endianness(), word_size() / 8, contents.final_size());
+ D32(contents.start());
+ D32(alignment);
+ D32(0); // relocations start
+ D32(0); // relocations size
+ D32(flags);
+ D32(0x93656b95); // reserved1
+ D32(0xc35a2473); // reserved2
+ if (word_size() == 64)
+ D32(0x70284b95); // reserved3
+
+ section_count_++;
+
+ return *this;
+ }
+
+ protected:
+ void Finish() {
+ final_section_count_ = section_count_;
+ if (!vmsize_.IsKnownConstant())
+ vmsize_ = content_final_size_;
+ SizedSection::Finish();
+ }
+
+ private:
+ // The number of sections that have been added to this segment so far.
+ size_t section_count_;
+
+ // A label representing the final number of sections this segment will hold.
+ Label final_section_count_;
+
+ // The size of the contents for this segment present in the file.
+ Label content_final_size_;
+
+ // A label representing the size of this segment when loaded; this can be
+ // larger than the size of its file contents, the difference being
+ // zero-filled. If not set explicitly by calling set_vmsize, this is set
+ // equal to the size of the contents.
+ Label vmsize_;
+};
+
+// A SizedSection holding a list of Mach-O load commands.
+class LoadCommands: public SizedSection {
+ public:
+ LoadCommands() : command_count_(0) { }
+
+ // Return a label representing the final load command count.
+ Label final_command_count() const { return final_command_count_; }
+
+ // Increment the command count; return a reference to this section.
+ LoadCommands &CountCommand() {
+ command_count_++;
+ return *this;
+ }
+
+ // Place COMMAND, containing a load command, at the end of this section.
+ // Return a reference to this section.
+ LoadCommands &Place(SizedSection *section) {
+ SizedSection::Place(section);
+ CountCommand();
+ return *this;
+ }
+
+ protected:
+ // Mark this load command list as complete.
+ void Finish() {
+ SizedSection::Finish();
+ final_command_count_ = command_count_;
+ }
+
+ private:
+ // The number of load commands we have added to this file so far.
+ size_t command_count_;
+
+ // A label representing the final command count.
+ Label final_command_count_;
+};
+
+// A SizedSection holding the contents of a Mach-O file. Within a
+// MachOFile, the start, Here, and Mark members refer to file offsets.
+class MachOFile: public SizedSection {
+ public:
+ MachOFile() {
+ start() = 0;
+ }
+
+ // Create a Mach-O file header using the given characteristics and load
+ // command list. This Places COMMANDS immediately after the header.
+ // Return a reference to this section.
+ MachOFile &Header(LoadCommands *commands,
+ cpu_type_t cpu_type = CPU_TYPE_X86,
+ cpu_subtype_t cpu_subtype = CPU_SUBTYPE_I386_ALL,
+ FileType file_type = MH_EXECUTE,
+ uint32_t file_flags = (MH_TWOLEVEL |
+ MH_DYLDLINK |
+ MH_NOUNDEFS)) {
+ D32(word_size() == 32 ? 0xfeedface : 0xfeedfacf); // magic number
+ D32(cpu_type); // cpu type
+ D32(cpu_subtype); // cpu subtype
+ D32(file_type); // file type
+ D32(commands->final_command_count()); // number of load commands
+ D32(commands->final_size()); // their size in bytes
+ D32(file_flags); // flags
+ if (word_size() == 64)
+ D32(0x55638b90); // reserved
+ Place(commands);
+ return *this;
+ }
+};
+
+
+struct ReaderFixture {
+ ReaderFixture()
+ : reporter("reporter filename"),
+ reader(&reporter) {
+ EXPECT_CALL(reporter, BadHeader()).Times(0);
+ EXPECT_CALL(reporter, CPUTypeMismatch(_, _, _, _)).Times(0);
+ EXPECT_CALL(reporter, HeaderTruncated()).Times(0);
+ EXPECT_CALL(reporter, LoadCommandRegionTruncated()).Times(0);
+ EXPECT_CALL(reporter, LoadCommandsOverrun(_, _, _)).Times(0);
+ EXPECT_CALL(reporter, LoadCommandTooShort(_, _)).Times(0);
+ EXPECT_CALL(reporter, SectionsMissing(_)).Times(0);
+ EXPECT_CALL(reporter, MisplacedSegmentData(_)).Times(0);
+ EXPECT_CALL(reporter, MisplacedSectionData(_, _)).Times(0);
+ EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(0);
+ EXPECT_CALL(reporter, UnsupportedCPUType(_)).Times(0);
+
+ EXPECT_CALL(load_command_handler, UnknownCommand(_, _)).Times(0);
+ EXPECT_CALL(load_command_handler, SegmentCommand(_)).Times(0);
+ }
+
+ void ReadFile(MachOFile *file,
+ bool expect_parse_success,
+ cpu_type_t expected_cpu_type,
+ cpu_subtype_t expected_cpu_subtype) {
+ ASSERT_TRUE(file->GetContents(&file_contents));
+ file_bytes = reinterpret_cast<const uint8_t *>(file_contents.data());
+ if (expect_parse_success) {
+ EXPECT_TRUE(reader.Read(file_bytes,
+ file_contents.size(),
+ expected_cpu_type,
+ expected_cpu_subtype));
+ } else {
+ EXPECT_FALSE(reader.Read(file_bytes,
+ file_contents.size(),
+ expected_cpu_type,
+ expected_cpu_subtype));
+ }
+ }
+
+ string file_contents;
+ const uint8_t *file_bytes;
+ MockReaderReporter reporter;
+ Reader reader;
+ MockLoadCommandHandler load_command_handler;
+ MockSectionHandler section_handler;
+};
+
+class ReaderTest: public ReaderFixture, public Test { };
+
+TEST_F(ReaderTest, BadMagic) {
+ WithConfiguration config(kLittleEndian, 32);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0x67bdebe1) // Not a proper magic number.
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ EXPECT_CALL(reporter, BadHeader()).WillOnce(Return());
+ ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType);
+}
+
+TEST_F(ReaderTest, MismatchedMagic) {
+ WithConfiguration config(kLittleEndian, 32);
+ const cpu_type_t kCPUType = CPU_TYPE_I386;
+ const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL;
+ MachOFile file;
+ file
+ .D32(MH_CIGAM) // Right magic, but winds up wrong
+ // due to bitswapping
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ EXPECT_CALL(reporter, BadHeader()).WillOnce(Return());
+ ReadFile(&file, false, kCPUType, kCPUSubType);
+}
+
+TEST_F(ReaderTest, ShortMagic) {
+ WithConfiguration config(kBigEndian, 32);
+ MachOFile file;
+ file
+ .D16(0xfeed); // magic number
+ // truncated!
+ EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return());
+ ReadFile(&file, false, CPU_TYPE_ANY, 0);
+}
+
+TEST_F(ReaderTest, ShortHeader) {
+ WithConfiguration config(kBigEndian, 32);
+ const cpu_type_t kCPUType = CPU_TYPE_ANY;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedface) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0); // they occupy no bytes
+ EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return());
+ ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType);
+}
+
+TEST_F(ReaderTest, MismatchedCPU) {
+ WithConfiguration config(kBigEndian, 32);
+ const cpu_type_t kCPUType = CPU_TYPE_I386;
+ const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL;
+ MachOFile file;
+ file
+ .D32(MH_MAGIC) // Right magic for PPC (once bitswapped)
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ EXPECT_CALL(reporter,
+ CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL,
+ CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL))
+ .WillOnce(Return());
+ ReadFile(&file, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL);
+}
+
+TEST_F(ReaderTest, LittleEndian32Bit) {
+ WithConfiguration config(kLittleEndian, 32);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedface) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
+ EXPECT_FALSE(reader.bits_64());
+ EXPECT_FALSE(reader.big_endian());
+ EXPECT_EQ(kCPUType, reader.cpu_type());
+ EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
+ EXPECT_EQ(FileType(0x149fc717), reader.file_type());
+ EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
+}
+
+TEST_F(ReaderTest, LittleEndian64Bit) {
+ WithConfiguration config(kLittleEndian, 64);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedfacf) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
+ EXPECT_TRUE(reader.bits_64());
+ EXPECT_FALSE(reader.big_endian());
+ EXPECT_EQ(kCPUType, reader.cpu_type());
+ EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
+ EXPECT_EQ(FileType(0x149fc717), reader.file_type());
+ EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
+}
+
+TEST_F(ReaderTest, BigEndian32Bit) {
+ WithConfiguration config(kBigEndian, 32);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedface) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
+ EXPECT_FALSE(reader.bits_64());
+ EXPECT_TRUE(reader.big_endian());
+ EXPECT_EQ(kCPUType, reader.cpu_type());
+ EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
+ EXPECT_EQ(FileType(0x149fc717), reader.file_type());
+ EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
+}
+
+TEST_F(ReaderTest, BigEndian64Bit) {
+ WithConfiguration config(kBigEndian, 64);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedfacf) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(0) // no load commands
+ .D32(0) // they occupy no bytes
+ .D32(0x80e71d64) // flags
+ .D32(0); // reserved
+ ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType);
+ EXPECT_TRUE(reader.bits_64());
+ EXPECT_TRUE(reader.big_endian());
+ EXPECT_EQ(kCPUType, reader.cpu_type());
+ EXPECT_EQ(kCPUSubType, reader.cpu_subtype());
+ EXPECT_EQ(FileType(0x149fc717), reader.file_type());
+ EXPECT_EQ(FileFlags(0x80e71d64), reader.flags());
+}
+
+
+// Load command tests.
+
+class LoadCommand: public ReaderFixture, public Test { };
+
+TEST_F(LoadCommand, RegionTruncated) {
+ WithConfiguration config(kBigEndian, 64);
+ const cpu_type_t kCPUType = 0x46b760df;
+ const cpu_subtype_t kCPUSubType = 0x76a0e7f7;
+ MachOFile file;
+ file
+ .D32(0xfeedfacf) // magic number
+ .D32(kCPUType) // cpu type
+ .D32(kCPUSubType) // cpu subtype
+ .D32(0x149fc717) // file type
+ .D32(1) // one load command
+ .D32(40) // occupying 40 bytes
+ .D32(0x80e71d64) // flags
+ .D32(0) // reserved
+ .Append(20, 0); // load command region, not as long as
+ // Mach-O header promised
+
+ EXPECT_CALL(reporter, LoadCommandRegionTruncated()).WillOnce(Return());
+
+ ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType);
+}
+
+TEST_F(LoadCommand, None) {
+ WithConfiguration config(kLittleEndian, 32);
+ LoadCommands load_commands;
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_X86, CPU_SUBTYPE_I386_ALL);
+
+ EXPECT_FALSE(reader.bits_64());
+ EXPECT_FALSE(reader.big_endian());
+ EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type());
+ EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype());
+ EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type());
+ EXPECT_EQ(FileFlags(MH_TWOLEVEL |
+ MH_DYLDLINK |
+ MH_NOUNDEFS),
+ FileFlags(reader.flags()));
+
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, Unknown) {
+ WithConfiguration config(kBigEndian, 32);
+ LoadCommands load_commands;
+ load_commands
+ .CountCommand()
+ .D32(0x33293d4a) // unknown load command
+ .D32(40) // total size in bytes
+ .Append(32, '*'); // dummy data
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_FALSE(reader.bits_64());
+ EXPECT_TRUE(reader.big_endian());
+ EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type());
+ EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype());
+ EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type());
+ EXPECT_EQ(FileFlags(MH_TWOLEVEL |
+ MH_DYLDLINK |
+ MH_NOUNDEFS),
+ reader.flags());
+
+ ByteBuffer expected;
+ expected.start = file_bytes + load_commands.start().Value();
+ expected.end = expected.start + load_commands.final_size().Value();
+ EXPECT_CALL(load_command_handler, UnknownCommand(0x33293d4a,
+ expected))
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, TypeIncomplete) {
+ WithConfiguration config(kLittleEndian, 32);
+ LoadCommands load_commands;
+ load_commands
+ .CountCommand()
+ .Append(3, 0); // load command type, incomplete
+
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, 0))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, LengthIncomplete) {
+ WithConfiguration config(kBigEndian, 64);
+ LoadCommands load_commands;
+ load_commands
+ .CountCommand()
+ .D32(LC_SEGMENT); // load command
+ // no length
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, ContentIncomplete) {
+ WithConfiguration config(kLittleEndian, 64);
+ LoadCommands load_commands;
+ load_commands
+ .CountCommand()
+ .D32(LC_SEGMENT) // load command
+ .D32(40) // total size in bytes
+ .Append(28, '*'); // not enough dummy data
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, SegmentBE32) {
+ WithConfiguration config(kBigEndian, 32);
+ LoadedSection segment;
+ segment.address() = 0x1891139c;
+ segment.Append(42, '*'); // segment contents
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("froon", segment, 0x94d6dd22, 0x8bdbc319, 0x990a16dd);
+ segment_command.vmsize() = 0xcb76584fU;
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_FALSE(actual_segment.bits_64);
+ EXPECT_EQ("froon", actual_segment.name);
+ EXPECT_EQ(0x1891139cU, actual_segment.vmaddr);
+ EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
+ EXPECT_EQ(0x94d6dd22U, actual_segment.maxprot);
+ EXPECT_EQ(0x8bdbc319U, actual_segment.initprot);
+ EXPECT_EQ(0x990a16ddU, actual_segment.flags);
+ EXPECT_EQ(0U, actual_segment.nsects);
+ EXPECT_EQ(0U, actual_segment.section_list.Size());
+ EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
+}
+
+TEST_F(LoadCommand, SegmentLE32) {
+ WithConfiguration config(kLittleEndian, 32);
+ LoadedSection segment;
+ segment.address() = 0x4b877866;
+ segment.Append(42, '*'); // segment contents
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("sixteenprecisely", segment,
+ 0x350759ed, 0x6cf5a62e, 0x990a16dd);
+ segment_command.vmsize() = 0xcb76584fU;
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_FALSE(actual_segment.bits_64);
+ EXPECT_EQ("sixteenprecisely", actual_segment.name);
+ EXPECT_EQ(0x4b877866U, actual_segment.vmaddr);
+ EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
+ EXPECT_EQ(0x350759edU, actual_segment.maxprot);
+ EXPECT_EQ(0x6cf5a62eU, actual_segment.initprot);
+ EXPECT_EQ(0x990a16ddU, actual_segment.flags);
+ EXPECT_EQ(0U, actual_segment.nsects);
+ EXPECT_EQ(0U, actual_segment.section_list.Size());
+ EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
+}
+
+TEST_F(LoadCommand, SegmentBE64) {
+ WithConfiguration config(kBigEndian, 64);
+ LoadedSection segment;
+ segment.address() = 0x79f484f77009e511ULL;
+ segment.Append(42, '*'); // segment contents
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("froon", segment, 0x42b45da5, 0x8bdbc319, 0xb2335220);
+ segment_command.vmsize() = 0x8d92397ce6248abaULL;
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_EQ(true, actual_segment.bits_64);
+ EXPECT_EQ("froon", actual_segment.name);
+ EXPECT_EQ(0x79f484f77009e511ULL, actual_segment.vmaddr);
+ EXPECT_EQ(0x8d92397ce6248abaULL, actual_segment.vmsize);
+ EXPECT_EQ(0x42b45da5U, actual_segment.maxprot);
+ EXPECT_EQ(0x8bdbc319U, actual_segment.initprot);
+ EXPECT_EQ(0xb2335220U, actual_segment.flags);
+ EXPECT_EQ(0U, actual_segment.nsects);
+ EXPECT_EQ(0U, actual_segment.section_list.Size());
+ EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
+}
+
+TEST_F(LoadCommand, SegmentLE64) {
+ WithConfiguration config(kLittleEndian, 64);
+ LoadedSection segment;
+ segment.address() = 0x50c0501dc5922d35ULL;
+ segment.Append(42, '*'); // segment contents
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("sixteenprecisely", segment,
+ 0x917c339d, 0xdbc446fa, 0xb650b563);
+ segment_command.vmsize() = 0x84ae73e7c75469bfULL;
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_EQ(true, actual_segment.bits_64);
+ EXPECT_EQ("sixteenprecisely", actual_segment.name);
+ EXPECT_EQ(0x50c0501dc5922d35ULL, actual_segment.vmaddr);
+ EXPECT_EQ(0x84ae73e7c75469bfULL, actual_segment.vmsize);
+ EXPECT_EQ(0x917c339dU, actual_segment.maxprot);
+ EXPECT_EQ(0xdbc446faU, actual_segment.initprot);
+ EXPECT_EQ(0xb650b563U, actual_segment.flags);
+ EXPECT_EQ(0U, actual_segment.nsects);
+ EXPECT_EQ(0U, actual_segment.section_list.Size());
+ EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size());
+}
+
+TEST_F(LoadCommand, SegmentCommandTruncated) {
+ WithConfiguration config(kBigEndian, 32);
+ LoadedSection segment_contents;
+ segment_contents.Append(20, '*'); // lah di dah
+ SizedSection command;
+ command
+ .D32(LC_SEGMENT) // command type
+ .D32(command.final_size()) // command size
+ .AppendCString("too-short", 16) // segment name
+ .D32(0x9c759211) // vmaddr
+ .D32(segment_contents.final_size()) // vmsize
+ .D32(segment_contents.start()) // file offset
+ .D32(segment_contents.final_size()) // file size
+ .D32(0x56f28446) // max protection
+ .D32(0xe7910dcb) // initial protection
+ .D32(0) // no sections
+ .Append(3, 0); // flags (one byte short!)
+ LoadCommands load_commands;
+ load_commands.Place(&command);
+ MachOFile file;
+ file
+ .Header(&load_commands)
+ .Place(&segment_contents);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, LoadCommandTooShort(0, LC_SEGMENT))
+ .WillOnce(Return());
+
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, SegmentBadContentOffset) {
+ WithConfiguration config(kLittleEndian, 32);
+ // Instead of letting a Place call set the segment's file offset and size,
+ // set them ourselves, to check that the parser catches invalid offsets
+ // instead of handing us bogus pointers.
+ LoadedSection segment;
+ segment.address() = 0x4db5489c;
+ segment.start() = 0x7e189e76; // beyond end of file
+ segment.final_size() = 0x98b9c3ab;
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("notmerelyfifteen", segment, 0xcbab25ee, 0x359a20db, 0x68a3933f);
+ LoadCommands load_commands;
+ load_commands.Place(&segment_command);
+ MachOFile file;
+ file.Header(&load_commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, MisplacedSegmentData("notmerelyfifteen"))
+ .WillOnce(Return());
+
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(LoadCommand, ThreeLoadCommands) {
+ WithConfiguration config(kBigEndian, 32);
+ LoadedSection seg1, seg2, seg3;
+ SegmentLoadCommand cmd1, cmd2, cmd3;
+
+ seg1.Append(128, '@');
+ seg1.address() = 0xa7f61ef6;
+ cmd1.Header("head", seg1, 0x88bf1cc7, 0x889a26a4, 0xe9b80d87);
+ // Include some dummy data at the end of the load command. Since we
+ // didn't claim to have any sections, the reader should ignore this. But
+ // making sure the commands have different lengths ensures that we're
+ // using the right command's length to advance the LoadCommandIterator.
+ cmd1.Append(128, '!');
+
+ seg2.Append(42, '*');
+ seg2.address() = 0xc70fc909;
+ cmd2.Header("thorax", seg2, 0xde7327f4, 0xfdaf771d, 0x65e74b30);
+ // More dummy data at the end of the load command.
+ cmd2.Append(32, '^');
+
+ seg3.Append(42, '%');
+ seg3.address() = 0x46b3ab05;
+ cmd3.Header("abdomen", seg3, 0x7098b70d, 0x8d8d7728, 0x5131419b);
+ // More dummy data at the end of the load command.
+ cmd3.Append(64, '&');
+
+ LoadCommands load_commands;
+ load_commands.Place(&cmd1).Place(&cmd2).Place(&cmd3);
+
+ MachOFile file;
+ file.Header(&load_commands).Place(&seg1).Place(&seg2).Place(&seg3);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ {
+ InSequence s;
+ EXPECT_CALL(load_command_handler,
+ SegmentCommand(Field(&Segment::name, "head")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(load_command_handler,
+ SegmentCommand(Field(&Segment::name, "thorax")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(load_command_handler,
+ SegmentCommand(Field(&Segment::name, "abdomen")))
+ .WillOnce(Return(true));
+ }
+
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+static inline Matcher<const Section &> MatchSection(
+ Matcher<bool> bits_64,
+ Matcher<const string &> section_name,
+ Matcher<const string &> segment_name,
+ Matcher<uint64_t> address,
+ Matcher<uint32_t> alignment,
+ Matcher<uint32_t> flags,
+ Matcher<const ByteBuffer &> contents) {
+ return AllOf(AllOf(Field(&Section::bits_64, bits_64),
+ Field(&Section::section_name, section_name),
+ Field(&Section::segment_name, segment_name),
+ Field(&Section::address, address)),
+ AllOf(Field(&Section::align, alignment),
+ Field(&Section::flags, flags),
+ Field(&Section::contents, contents)));
+}
+
+static inline Matcher<const Section &> MatchSection(
+ Matcher<bool> bits_64,
+ Matcher<const string &> section_name,
+ Matcher<const string &> segment_name,
+ Matcher<uint64_t> address) {
+ return AllOf(Field(&Section::bits_64, bits_64),
+ Field(&Section::section_name, section_name),
+ Field(&Section::segment_name, segment_name),
+ Field(&Section::address, address));
+}
+
+TEST_F(LoadCommand, OneSegmentTwoSections) {
+ WithConfiguration config(kBigEndian, 64);
+
+ // Create some sections with some data.
+ LoadedSection section1, section2;
+ section1.Append("buddha's hand");
+ section2.Append("kumquat");
+
+ // Create a segment to hold them.
+ LoadedSection segment;
+ segment.address() = 0xe1d0eeec;
+ segment.Place(&section2).Place(&section1);
+
+ SegmentLoadCommand segment_command;
+ segment_command
+ .Header("head", segment, 0x92c9568c, 0xa89f2627, 0x4dc7a1e2)
+ .AppendSectionEntry("mandarin", "kishu", 12, 0x8cd4604bU, section1)
+ .AppendSectionEntry("bergamot", "cara cara", 12, 0x98746efaU, section2);
+
+ LoadCommands commands;
+ commands.Place(&segment_command);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_CALL(load_command_handler, SegmentCommand(_))
+ .WillOnce(DoAll(SaveArg<0>(&actual_segment),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ {
+ InSequence s;
+ ByteBuffer contents1;
+ contents1.start = file_bytes + section1.start().Value();
+ contents1.end = contents1.start + section1.final_size().Value();
+ EXPECT_EQ("buddha's hand",
+ string(reinterpret_cast<const char *>(contents1.start),
+ contents1.Size()));
+ EXPECT_CALL(section_handler,
+ HandleSection(MatchSection(true, "mandarin", "kishu",
+ section1.address().Value(), 12,
+ 0x8cd4604bU, contents1)))
+ .WillOnce(Return(true));
+
+ ByteBuffer contents2;
+ contents2.start = file_bytes + section2.start().Value();
+ contents2.end = contents2.start + section2.final_size().Value();
+ EXPECT_EQ("kumquat",
+ string(reinterpret_cast<const char *>(contents2.start),
+ contents2.Size()));
+ EXPECT_CALL(section_handler,
+ HandleSection(MatchSection(true, "bergamot", "cara cara",
+ section2.address().Value(), 12,
+ 0x98746efaU, contents2)))
+ .WillOnce(Return(true));
+ }
+
+ EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+TEST_F(LoadCommand, MisplacedSectionBefore) {
+ WithConfiguration config(kLittleEndian, 64);
+
+ // The segment.
+ LoadedSection segment;
+ segment.address() = 0x696d83cc;
+ segment.Append(10, '0');
+
+ // The contents of the following sections don't matter, because
+ // we're not really going to Place them in segment; we're just going
+ // to set all their labels by hand to get the (impossible)
+ // configurations we want.
+
+ // A section whose starting offset is before that of its section.
+ LoadedSection before;
+ before.Append(10, '1');
+ before.start() = segment.start() - 1;
+ before.address() = segment.address() - 1;
+ before.final_size() = before.Size();
+
+ SegmentLoadCommand command;
+ command
+ .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057)
+ .AppendSectionEntry("before", "segment", 0, 0x686c6921, before);
+
+ LoadCommands commands;
+ commands.Place(&command);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_TRUE(reader.FindSegment("segment", &actual_segment));
+
+ EXPECT_CALL(reporter, MisplacedSectionData("before", "segment"))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+TEST_F(LoadCommand, MisplacedSectionAfter) {
+ WithConfiguration config(kLittleEndian, 64);
+
+ // The segment.
+ LoadedSection segment;
+ segment.address() = 0x696d83cc;
+ segment.Append(10, '0');
+
+ // The contents of the following sections don't matter, because
+ // we're not really going to Place them in segment; we're just going
+ // to set all their labels by hand to get the (impossible)
+ // configurations we want.
+
+ // A section whose starting offset is after the end of its section.
+ LoadedSection after;
+ after.Append(10, '2');
+ after.start() = segment.start() + 11;
+ after.address() = segment.address() + 11;
+ after.final_size() = after.Size();
+
+ SegmentLoadCommand command;
+ command
+ .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057)
+ .AppendSectionEntry("after", "segment", 0, 0x2ee50124, after);
+
+ LoadCommands commands;
+ commands.Place(&command);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_TRUE(reader.FindSegment("segment", &actual_segment));
+
+ EXPECT_CALL(reporter, MisplacedSectionData("after", "segment"))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+TEST_F(LoadCommand, MisplacedSectionTooBig) {
+ WithConfiguration config(kLittleEndian, 64);
+
+ // The segment.
+ LoadedSection segment;
+ segment.address() = 0x696d83cc;
+ segment.Append(10, '0');
+
+ // The contents of the following sections don't matter, because
+ // we're not really going to Place them in segment; we're just going
+ // to set all their labels by hand to get the (impossible)
+ // configurations we want.
+
+ // A section that extends beyond the end of its section.
+ LoadedSection too_big;
+ too_big.Append(10, '3');
+ too_big.start() = segment.start() + 1;
+ too_big.address() = segment.address() + 1;
+ too_big.final_size() = too_big.Size();
+
+ SegmentLoadCommand command;
+ command
+ .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057)
+ .AppendSectionEntry("too big", "segment", 0, 0x8b53ae5c, too_big);
+
+ LoadCommands commands;
+ commands.Place(&command);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_TRUE(reader.FindSegment("segment", &actual_segment));
+
+ EXPECT_CALL(reporter, MisplacedSectionData("too big", "segment"))
+ .WillOnce(Return());
+ EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+
+// The segments in a .dSYM bundle's Mach-O file have their file offset
+// and size set to zero, but the sections don't. The reader shouldn't
+// report an error in this case.
+TEST_F(LoadCommand, ZappedSegment) {
+ WithConfiguration config(kBigEndian, 32);
+
+ // The segment.
+ LoadedSection segment;
+ segment.address() = 0x696d83cc;
+ segment.start() = 0;
+ segment.final_size() = 0;
+
+ // The section.
+ LoadedSection section;
+ section.address() = segment.address();
+ section.start() = 0;
+ section.final_size() = 1000; // extends beyond its segment
+
+ SegmentLoadCommand command;
+ command
+ .Header("zapped", segment, 0x0861a5cb, 0x68ccff67, 0x0b66255c)
+ .AppendSectionEntry("twitching", "zapped", 0, 0x93b3bd42, section);
+
+ LoadCommands commands;
+ commands.Place(&command);
+
+ MachOFile file;
+ file.Header(&commands);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+ EXPECT_TRUE(reader.FindSegment("zapped", &actual_segment));
+
+ ByteBuffer zapped_extent(NULL, 0);
+ EXPECT_CALL(section_handler,
+ HandleSection(MatchSection(false, "twitching", "zapped",
+ 0x696d83cc, 0, 0x93b3bd42,
+ zapped_extent)))
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, &section_handler));
+}
+
+TEST_F(LoadCommand, MapSegmentSections) {
+ WithConfiguration config(kLittleEndian, 32);
+
+ // Create some sections with some data.
+ LoadedSection section1, section2, section3, section4;
+ section1.Append("buddha's hand");
+ section2.start() = 0; // Section 2 is an S_ZEROFILL section.
+ section2.final_size() = 0;
+ section3.Append("shasta gold");
+ section4.Append("satsuma");
+
+ // Create two segments to hold them.
+ LoadedSection segment1, segment2;
+ segment1.address() = 0x13e6c8a9;
+ segment1.Place(&section3).Place(&section1);
+ segment2.set_word_size(64);
+ segment2.address() = 0x04d462e2;
+ segment2.Place(&section4);
+ section2.address() = segment2.address() + segment2.Size();
+
+ SegmentLoadCommand segment_command1, segment_command2;
+ segment_command1
+ .Header("head", segment1, 0x67d955a6, 0x7a61c13e, 0xe3e50c64)
+ .AppendSectionEntry("mandarin", "head", 12, 0x5bb565d7, section1)
+ .AppendSectionEntry("bergamot", "head", 12, 0x8620de73, section3);
+ segment_command2.set_word_size(64);
+ segment_command2
+ .Header("thorax", segment2, 0x7aab2419, 0xe908007f, 0x17961d33)
+ .AppendSectionEntry("sixteenprecisely", "thorax",
+ 12, S_ZEROFILL, section2)
+ .AppendSectionEntry("cara cara", "thorax", 12, 0xb6c5dd8a, section4);
+
+ LoadCommands commands;
+ commands.Place(&segment_command1).Place(&segment_command2);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment1).Place(&segment2);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment segment;
+ SectionMap section_map;
+
+ EXPECT_FALSE(reader.FindSegment("smoot", &segment));
+
+ ASSERT_TRUE(reader.FindSegment("thorax", &segment));
+ ASSERT_TRUE(reader.MapSegmentSections(segment, &section_map));
+
+ EXPECT_FALSE(section_map.find("sixteenpreciselyandthensome")
+ != section_map.end());
+ EXPECT_FALSE(section_map.find("mandarin") != section_map.end());
+ ASSERT_TRUE(section_map.find("cara cara") != section_map.end());
+ EXPECT_THAT(section_map["cara cara"],
+ MatchSection(true, "cara cara", "thorax", 0x04d462e2));
+ ASSERT_TRUE(section_map.find("sixteenprecisely")
+ != section_map.end());
+ ByteBuffer sixteenprecisely_contents(NULL, 0);
+ EXPECT_THAT(section_map["sixteenprecisely"],
+ MatchSection(true, "sixteenprecisely", "thorax",
+ 0x04d462e2 + 7, 12, S_ZEROFILL,
+ sixteenprecisely_contents));
+
+ ASSERT_TRUE(reader.FindSegment("head", &segment));
+ ASSERT_TRUE(reader.MapSegmentSections(segment, &section_map));
+
+ ASSERT_TRUE(section_map.find("mandarin") != section_map.end());
+ EXPECT_THAT(section_map["mandarin"],
+ MatchSection(false, "mandarin", "head", 0x13e6c8a9 + 11));
+ ASSERT_TRUE(section_map.find("bergamot") != section_map.end());
+ EXPECT_THAT(section_map["bergamot"],
+ MatchSection(false, "bergamot", "head", 0x13e6c8a9));
+}
+
+TEST_F(LoadCommand, FindSegment) {
+ WithConfiguration config(kBigEndian, 32);
+
+ LoadedSection segment1, segment2, segment3;
+ segment1.address() = 0xb8ae5752;
+ segment1.Append("Some contents!");
+ segment2.address() = 0xd6b0ce83;
+ segment2.Append("Different stuff.");
+ segment3.address() = 0x7374fd2a;
+ segment3.Append("Further materials.");
+
+ SegmentLoadCommand cmd1, cmd2, cmd3;
+ cmd1.Header("first", segment1, 0xfadb6932, 0x175bf529, 0x0de790ad);
+ cmd2.Header("second", segment2, 0xeef716e0, 0xe103a9d7, 0x7d38a8ef);
+ cmd3.Header("third", segment3, 0xe172b39e, 0x86012f07, 0x080ac94d);
+
+ LoadCommands commands;
+ commands.Place(&cmd1).Place(&cmd2).Place(&cmd3);
+
+ MachOFile file;
+ file.Header(&commands).Place(&segment1).Place(&segment2).Place(&segment3);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ Segment actual_segment;
+
+ EXPECT_FALSE(reader.FindSegment("murphy", &actual_segment));
+
+ EXPECT_TRUE(reader.FindSegment("second", &actual_segment));
+ EXPECT_EQ(0xd6b0ce83, actual_segment.vmaddr);
+}
+
+
+// Symtab tests.
+
+// A StringAssembler is a class for generating .stabstr sections to present
+// as input to the STABS parser.
+class StringAssembler: public SizedSection {
+ public:
+ // Add the string S to this StringAssembler, and return the string's
+ // offset within this compilation unit's strings.
+ size_t Add(const string &s) {
+ size_t offset = Size();
+ AppendCString(s);
+ return offset;
+ }
+};
+
+// A SymbolAssembler is a class for generating .stab sections to present as
+// test input for the STABS parser.
+class SymbolAssembler: public SizedSection {
+ public:
+ // Create a SymbolAssembler that uses StringAssembler for its strings.
+ explicit SymbolAssembler(StringAssembler *string_assembler)
+ : string_assembler_(string_assembler),
+ entry_count_(0) { }
+
+ // Append a STAB entry to the end of this section with the given
+ // characteristics. NAME is the offset of this entry's name string within
+ // its compilation unit's portion of the .stabstr section; this can be a
+ // value generated by a StringAssembler. Return a reference to this
+ // SymbolAssembler.
+ SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor,
+ Label value, Label name) {
+ D32(name);
+ D8(type);
+ D8(other);
+ D16(descriptor);
+ Append(endianness(), word_size_ / 8, value);
+ entry_count_++;
+ return *this;
+ }
+
+ // As above, but automatically add NAME to our StringAssembler.
+ SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor,
+ Label value, const string &name) {
+ return Symbol(type, other, descriptor, value, string_assembler_->Add(name));
+ }
+
+ private:
+ // The strings for our STABS entries.
+ StringAssembler *string_assembler_;
+
+ // The number of entries in this compilation unit so far.
+ size_t entry_count_;
+};
+
+class Symtab: public ReaderFixture, public Test { };
+
+TEST_F(Symtab, Symtab32) {
+ WithConfiguration config(kLittleEndian, 32);
+
+ StringAssembler strings;
+ SymbolAssembler symbols(&strings);
+ symbols
+ .Symbol(0x52, 0x7c, 0x3470, 0x9bb02e7c, "hrududu")
+ .Symbol(0x50, 0x90, 0x7520, 0x1122525d, "Frith");
+
+ SizedSection symtab_command;
+ symtab_command
+ .D32(LC_SYMTAB) // command
+ .D32(symtab_command.final_size()) // size
+ .D32(symbols.start()) // file offset of symbols
+ .D32(2) // symbol count
+ .D32(strings.start()) // file offset of strings
+ .D32(strings.final_size()); // strings size
+
+ LoadCommands load_commands;
+ load_commands.Place(&symtab_command);
+
+ MachOFile file;
+ file.Header(&load_commands).Place(&symbols).Place(&strings);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ ByteBuffer symbols_found, strings_found;
+ EXPECT_CALL(load_command_handler, SymtabCommand(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&symbols_found),
+ SaveArg<1>(&strings_found),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_EQ(24U, symbols_found.Size());
+ EXPECT_EQ(14U, strings_found.Size());
+}
+
+TEST_F(Symtab, Symtab64) {
+ WithConfiguration config(kBigEndian, 64);
+
+ StringAssembler strings;
+ SymbolAssembler symbols(&strings);
+ symbols
+ .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo")
+ .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar");
+
+ SizedSection symtab_command;
+ symtab_command
+ .D32(LC_SYMTAB) // command
+ .D32(symtab_command.final_size()) // size
+ .D32(symbols.start()) // file offset of symbols
+ .D32(2) // symbol count
+ .D32(strings.start()) // file offset of strings
+ .D32(strings.final_size()); // strings size
+
+ LoadCommands load_commands;
+ load_commands.Place(&symtab_command);
+
+ MachOFile file;
+ file.Header(&load_commands).Place(&symbols).Place(&strings);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ ByteBuffer symbols_found, strings_found;
+ EXPECT_CALL(load_command_handler, SymtabCommand(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&symbols_found),
+ SaveArg<1>(&strings_found),
+ Return(true)));
+ EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
+
+ EXPECT_EQ(32U, symbols_found.Size());
+ EXPECT_EQ(8U, strings_found.Size());
+}
+
+TEST_F(Symtab, SymtabMisplacedSymbols) {
+ WithConfiguration config(kBigEndian, 32);
+
+ StringAssembler strings;
+ SymbolAssembler symbols(&strings);
+ symbols
+ .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo")
+ .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar");
+
+ SizedSection symtab_command;
+ symtab_command
+ .D32(LC_SYMTAB) // command
+ .D32(symtab_command.final_size()) // size
+ .D32(symbols.start()) // file offset of symbols
+ .D32(3) // symbol count (too many)
+ .D32(strings.start()) // file offset of strings
+ .D32(strings.final_size()); // strings size
+
+ LoadCommands load_commands;
+ load_commands.Place(&symtab_command);
+
+ MachOFile file;
+ // Put symbols at end, so the excessive length will be noticed.
+ file.Header(&load_commands).Place(&strings).Place(&symbols);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1);
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
+TEST_F(Symtab, SymtabMisplacedStrings) {
+ WithConfiguration config(kLittleEndian, 32);
+
+ StringAssembler strings;
+ SymbolAssembler symbols(&strings);
+ symbols
+ .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo")
+ .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar");
+
+ SizedSection symtab_command;
+ symtab_command
+ .D32(LC_SYMTAB) // command
+ .D32(symtab_command.final_size()) // size
+ .D32(symbols.start()) // file offset of symbols
+ .D32(2) // symbol count
+ .D32(strings.start()) // file offset of strings
+ .D32(strings.final_size() + 1); // strings size (too long)
+
+ LoadCommands load_commands;
+ load_commands.Place(&symtab_command);
+
+ MachOFile file;
+ // Put strings at end, so the excessive length will be noticed.
+ file.Header(&load_commands).Place(&symbols).Place(&strings);
+
+ ReadFile(&file, true, CPU_TYPE_ANY, 0);
+
+ EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1);
+ EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler));
+}
+
diff --git a/3rdParty/Breakpad/src/common/mac/macho_utilities.cc b/3rdParty/Breakpad/src/common/mac/macho_utilities.cc
index 89f9e77..f56fe76 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_utilities.cc
+++ b/3rdParty/Breakpad/src/common/mac/macho_utilities.cc
@@ -34,16 +34,44 @@
#include "common/mac/byteswap.h"
#include "common/mac/macho_utilities.h"
-void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc,
- enum NXByteOrder target_byte_order)
-{
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+
+void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc) {
uc->cmd = ByteSwap(uc->cmd);
uc->cmdsize = ByteSwap(uc->cmdsize);
}
-void breakpad_swap_segment_command_64(struct segment_command_64 *sg,
- enum NXByteOrder target_byte_order)
-{
+void breakpad_swap_load_command(struct load_command *lc) {
+ lc->cmd = ByteSwap(lc->cmd);
+ lc->cmdsize = ByteSwap(lc->cmdsize);
+}
+
+void breakpad_swap_dylib_command(struct dylib_command *dc) {
+ dc->cmd = ByteSwap(dc->cmd);
+ dc->cmdsize = ByteSwap(dc->cmdsize);
+
+ dc->dylib.name.offset = ByteSwap(dc->dylib.name.offset);
+ dc->dylib.timestamp = ByteSwap(dc->dylib.timestamp);
+ dc->dylib.current_version = ByteSwap(dc->dylib.current_version);
+ dc->dylib.compatibility_version = ByteSwap(dc->dylib.compatibility_version);
+}
+
+void breakpad_swap_segment_command(struct segment_command *sc) {
+ sc->cmd = ByteSwap(sc->cmd);
+ sc->cmdsize = ByteSwap(sc->cmdsize);
+
+ sc->vmaddr = ByteSwap(sc->vmaddr);
+ sc->vmsize = ByteSwap(sc->vmsize);
+ sc->fileoff = ByteSwap(sc->fileoff);
+ sc->filesize = ByteSwap(sc->filesize);
+ sc->maxprot = ByteSwap(sc->maxprot);
+ sc->initprot = ByteSwap(sc->initprot);
+ sc->nsects = ByteSwap(sc->nsects);
+ sc->flags = ByteSwap(sc->flags);
+}
+
+void breakpad_swap_segment_command_64(struct segment_command_64 *sg) {
sg->cmd = ByteSwap(sg->cmd);
sg->cmdsize = ByteSwap(sg->cmdsize);
@@ -58,9 +86,32 @@ void breakpad_swap_segment_command_64(struct segment_command_64 *sg,
sg->flags = ByteSwap(sg->flags);
}
-void breakpad_swap_mach_header_64(struct mach_header_64 *mh,
- enum NXByteOrder target_byte_order)
-{
+void breakpad_swap_fat_header(struct fat_header *fh) {
+ fh->magic = ByteSwap(fh->magic);
+ fh->nfat_arch = ByteSwap(fh->nfat_arch);
+}
+
+void breakpad_swap_fat_arch(struct fat_arch *fa, uint32_t narchs) {
+ for (uint32_t i = 0; i < narchs; ++i) {
+ fa[i].cputype = ByteSwap(fa[i].cputype);
+ fa[i].cpusubtype = ByteSwap(fa[i].cpusubtype);
+ fa[i].offset = ByteSwap(fa[i].offset);
+ fa[i].size = ByteSwap(fa[i].size);
+ fa[i].align = ByteSwap(fa[i].align);
+ }
+}
+
+void breakpad_swap_mach_header(struct mach_header *mh) {
+ mh->magic = ByteSwap(mh->magic);
+ mh->cputype = ByteSwap(mh->cputype);
+ mh->cpusubtype = ByteSwap(mh->cpusubtype);
+ mh->filetype = ByteSwap(mh->filetype);
+ mh->ncmds = ByteSwap(mh->ncmds);
+ mh->sizeofcmds = ByteSwap(mh->sizeofcmds);
+ mh->flags = ByteSwap(mh->flags);
+}
+
+void breakpad_swap_mach_header_64(struct mach_header_64 *mh) {
mh->magic = ByteSwap(mh->magic);
mh->cputype = ByteSwap(mh->cputype);
mh->cpusubtype = ByteSwap(mh->cpusubtype);
@@ -71,10 +122,24 @@ void breakpad_swap_mach_header_64(struct mach_header_64 *mh,
mh->reserved = ByteSwap(mh->reserved);
}
+void breakpad_swap_section(struct section *s,
+ uint32_t nsects) {
+ for (uint32_t i = 0; i < nsects; i++) {
+ s[i].addr = ByteSwap(s[i].addr);
+ s[i].size = ByteSwap(s[i].size);
+
+ s[i].offset = ByteSwap(s[i].offset);
+ s[i].align = ByteSwap(s[i].align);
+ s[i].reloff = ByteSwap(s[i].reloff);
+ s[i].nreloc = ByteSwap(s[i].nreloc);
+ s[i].flags = ByteSwap(s[i].flags);
+ s[i].reserved1 = ByteSwap(s[i].reserved1);
+ s[i].reserved2 = ByteSwap(s[i].reserved2);
+ }
+}
+
void breakpad_swap_section_64(struct section_64 *s,
- uint32_t nsects,
- enum NXByteOrder target_byte_order)
-{
+ uint32_t nsects) {
for (uint32_t i = 0; i < nsects; i++) {
s[i].addr = ByteSwap(s[i].addr);
s[i].size = ByteSwap(s[i].size);
diff --git a/3rdParty/Breakpad/src/common/mac/macho_utilities.h b/3rdParty/Breakpad/src/common/mac/macho_utilities.h
index a07945f..00563a7 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_utilities.h
+++ b/3rdParty/Breakpad/src/common/mac/macho_utilities.h
@@ -54,14 +54,6 @@
# define LC_UUID 0x1b /* the uuid */
#endif
-#if TARGET_CPU_X86
-# define BREAKPAD_MACHINE_THREAD_STATE i386_THREAD_STATE
-#elif TARGET_CPU_X86_64
-# define BREAKPAD_MACHINE_THREAD_STATE x86_THREAD_STATE64
-#else
-# define BREAKPAD_MACHINE_THREAD_STATE MACHINE_THREAD_STATE
-#endif
-
// The uuid_command struct/swap routines were added during the 10.4 series.
// Their presence isn't guaranteed.
struct breakpad_uuid_command {
@@ -70,23 +62,34 @@ struct breakpad_uuid_command {
uint8_t uuid[16]; /* the 128-bit uuid */
};
-void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc,
- enum NXByteOrder target_byte_order);
+void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc);
+
+void breakpad_swap_load_command(struct load_command *lc);
+
+void breakpad_swap_dylib_command(struct dylib_command *dc);
// Older SDKs defines thread_state_data_t as an int[] instead
// of the natural_t[] it should be.
typedef natural_t breakpad_thread_state_data_t[THREAD_STATE_MAX];
+void breakpad_swap_segment_command(struct segment_command *sc);
+
// The 64-bit swap routines were added during the 10.4 series, their
// presence isn't guaranteed.
-void breakpad_swap_segment_command_64(struct segment_command_64 *sg,
- enum NXByteOrder target_byte_order);
+void breakpad_swap_segment_command_64(struct segment_command_64 *sg);
+
+void breakpad_swap_fat_header(struct fat_header *fh);
+
+void breakpad_swap_fat_arch(struct fat_arch *fa, uint32_t narchs);
+
+void breakpad_swap_mach_header(struct mach_header *mh);
+
+void breakpad_swap_mach_header_64(struct mach_header_64 *mh);
-void breakpad_swap_mach_header_64(struct mach_header_64 *mh,
- enum NXByteOrder target_byte_order);
+void breakpad_swap_section(struct section *s,
+ uint32_t nsects);
void breakpad_swap_section_64(struct section_64 *s,
- uint32_t nsects,
- enum NXByteOrder target_byte_order);
+ uint32_t nsects);
#endif
diff --git a/3rdParty/Breakpad/src/common/mac/macho_walker.cc b/3rdParty/Breakpad/src/common/mac/macho_walker.cc
index 92da7b1..1acd866 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_walker.cc
+++ b/3rdParty/Breakpad/src/common/mac/macho_walker.cc
@@ -33,15 +33,13 @@
//
// Author: Dan Waylonis
-extern "C" { // necessary for Leopard
- #include <assert.h>
- #include <fcntl.h>
- #include <mach-o/arch.h>
- #include <mach-o/loader.h>
- #include <mach-o/swap.h>
- #include <string.h>
- #include <unistd.h>
-}
+#include <assert.h>
+#include <fcntl.h>
+#include <mach-o/arch.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <string.h>
+#include <unistd.h>
#include "common/mac/byteswap.h"
#include "common/mac/macho_walker.h"
@@ -51,7 +49,7 @@ namespace MacFileUtilities {
MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
void *context)
- : file_(0),
+ : file_(-1),
memory_(NULL),
memory_size_(0),
callback_(callback),
@@ -64,7 +62,7 @@ MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
MachoWalker::MachoWalker(void *memory, size_t size,
LoadCommandCallback callback, void *context)
- : file_(0),
+ : file_(-1),
memory_(memory),
memory_size_(size),
callback_(callback),
@@ -79,21 +77,18 @@ MachoWalker::~MachoWalker() {
close(file_);
}
-int MachoWalker::ValidateCPUType(int cpu_type) {
- // If the user didn't specify, use the local architecture.
+bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
+ cpu_type_t valid_cpu_type = cpu_type;
+ cpu_subtype_t valid_cpu_subtype = cpu_subtype;
+ // if |cpu_type| is 0, use the native cpu type.
if (cpu_type == 0) {
const NXArchInfo *arch = NXGetLocalArchInfo();
assert(arch);
- cpu_type = arch->cputype;
+ valid_cpu_type = arch->cputype;
+ valid_cpu_subtype = CPU_SUBTYPE_MULTIPLE;
}
-
- return cpu_type;
-}
-
-bool MachoWalker::WalkHeader(int cpu_type) {
- int valid_cpu_type = ValidateCPUType(cpu_type);
off_t offset;
- if (FindHeader(valid_cpu_type, offset)) {
+ if (FindHeader(valid_cpu_type, valid_cpu_subtype, offset)) {
if (cpu_type & CPU_ARCH_ABI64)
return WalkHeader64AtOffset(offset);
@@ -111,7 +106,7 @@ bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) {
if (offset + size > memory_size_) {
if (static_cast<size_t>(offset) >= memory_size_)
return false;
- size = memory_size_ - offset;
+ size = memory_size_ - static_cast<size_t>(offset);
result = false;
}
memcpy(buffer, static_cast<char *>(memory_) + offset, size);
@@ -131,8 +126,9 @@ bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) {
return false;
}
-bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
- int valid_cpu_type = ValidateCPUType(cpu_type);
+bool MachoWalker::FindHeader(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ off_t &offset) {
// Read the magic bytes that's common amongst all mach-o files
uint32_t magic;
if (!ReadBytes(&magic, sizeof(magic), 0))
@@ -153,15 +149,18 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
if (!is_fat) {
// If we don't have a fat header, check if the cpu type matches the single
// header
- cpu_type_t header_cpu_type;
- if (!ReadBytes(&header_cpu_type, sizeof(header_cpu_type), offset))
+ struct mach_header header;
+ if (!ReadBytes(&header, sizeof(header), 0))
return false;
if (magic == MH_CIGAM || magic == MH_CIGAM_64)
- header_cpu_type = ByteSwap(header_cpu_type);
+ breakpad_swap_mach_header(&header);
- if (valid_cpu_type != header_cpu_type)
+ if (cpu_type != header.cputype ||
+ (cpu_subtype != CPU_SUBTYPE_MULTIPLE &&
+ cpu_subtype != header.cpusubtype)) {
return false;
+ }
offset = 0;
return true;
@@ -173,7 +172,7 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
return false;
if (NXHostByteOrder() != NX_BigEndian)
- swap_fat_header(&fat, NXHostByteOrder());
+ breakpad_swap_fat_header(&fat);
offset += sizeof(fat);
@@ -184,9 +183,11 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
return false;
if (NXHostByteOrder() != NX_BigEndian)
- swap_fat_arch(&arch, 1, NXHostByteOrder());
+ breakpad_swap_fat_arch(&arch, 1);
- if (arch.cputype == valid_cpu_type) {
+ if (arch.cputype == cpu_type &&
+ (cpu_subtype == CPU_SUBTYPE_MULTIPLE ||
+ arch.cpusubtype == cpu_subtype)) {
offset = arch.offset;
return true;
}
@@ -205,7 +206,7 @@ bool MachoWalker::WalkHeaderAtOffset(off_t offset) {
bool swap = (header.magic == MH_CIGAM);
if (swap)
- swap_mach_header(&header, NXHostByteOrder());
+ breakpad_swap_mach_header(&header);
// Copy the data into the mach_header_64 structure. Since the 32-bit and
// 64-bit only differ in the last field (reserved), this is safe to do.
@@ -231,7 +232,7 @@ bool MachoWalker::WalkHeader64AtOffset(off_t offset) {
bool swap = (header.magic == MH_CIGAM_64);
if (swap)
- breakpad_swap_mach_header_64(&header, NXHostByteOrder());
+ breakpad_swap_mach_header_64(&header);
current_header_ = &header;
current_header_size_ = sizeof(header);
@@ -252,7 +253,7 @@ bool MachoWalker::WalkHeaderCore(off_t offset, uint32_t number_of_commands,
return false;
if (swap)
- swap_load_command(&cmd, NXHostByteOrder());
+ breakpad_swap_load_command(&cmd);
// Call the user callback
if (callback_ && !callback_(this, &cmd, offset, swap, callback_context_))
diff --git a/3rdParty/Breakpad/src/common/mac/macho_walker.h b/3rdParty/Breakpad/src/common/mac/macho_walker.h
index cee3eb8..dd53581 100644
--- a/3rdParty/Breakpad/src/common/mac/macho_walker.h
+++ b/3rdParty/Breakpad/src/common/mac/macho_walker.h
@@ -34,6 +34,7 @@
#ifndef COMMON_MAC_MACHO_WALKER_H__
#define COMMON_MAC_MACHO_WALKER_H__
+#include <mach/machine.h>
#include <mach-o/loader.h>
#include <sys/types.h>
@@ -56,16 +57,14 @@ class MachoWalker {
void *context);
~MachoWalker();
- // Begin walking the header for |cpu_type|. If |cpu_type| is 0, then the
- // native cpu type is used. Otherwise, accepted values are listed in
- // /usr/include/mach/machine.h (e.g., CPU_TYPE_X86 or CPU_TYPE_POWERPC).
- // Returns false if opening the file failed or if the |cpu_type| is not
- // present in the file.
- bool WalkHeader(int cpu_type);
-
- // Locate (if any) the header offset for |cpu_type| and return in |offset|.
- // Return true if found, false otherwise.
- bool FindHeader(int cpu_type, off_t &offset);
+ // Begin walking the header for |cpu_type| and |cpu_subtype|. If |cpu_type|
+ // is 0, then the native cpu type is used. Otherwise, accepted values are
+ // listed in /usr/include/mach/machine.h (e.g., CPU_TYPE_X86 or
+ // CPU_TYPE_POWERPC). If |cpu_subtype| is CPU_SUBTYPE_MULTIPLE, the match is
+ // only done on |cpu_type|.
+ // Returns false if opening the file failed or if the |cpu_type|/|cpu_subtype|
+ // is not present in the file.
+ bool WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
// Read |size| bytes from the opened file at |offset| into |buffer|
bool ReadBytes(void *buffer, size_t size, off_t offset);
@@ -74,8 +73,11 @@ class MachoWalker {
bool CurrentHeader(struct mach_header_64 *header, off_t *offset);
private:
- // Validate the |cpu_type|
- int ValidateCPUType(int cpu_type);
+ // Locate (if any) the header offset for |cpu_type| and return in |offset|.
+ // Return true if found, false otherwise.
+ bool FindHeader(cpu_type_t cpu_type,
+ cpu_subtype_t cpu_subtype,
+ off_t &offset);
// Process an individual header starting at |offset| from the start of the
// file. Return true if successful, false otherwise.
diff --git a/3rdParty/Breakpad/src/common/mac/string_utilities.cc b/3rdParty/Breakpad/src/common/mac/string_utilities.cc
index e1f63a9..07c0f42 100644
--- a/3rdParty/Breakpad/src/common/mac/string_utilities.cc
+++ b/3rdParty/Breakpad/src/common/mac/string_utilities.cc
@@ -27,7 +27,7 @@
// (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 "processor/scoped_ptr.h"
+#include "common/scoped_ptr.h"
#include "common/mac/string_utilities.h"
namespace MacStringUtils {
diff --git a/3rdParty/Breakpad/src/common/mac/super_fat_arch.h b/3rdParty/Breakpad/src/common/mac/super_fat_arch.h
new file mode 100644
index 0000000..501c865
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/mac/super_fat_arch.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Erik Chen <erikchen@chromium.org>
+
+// super_fat_arch.h: A class to handle 64-bit object files. Has conversions to
+// and from struct fat_arch.
+
+#ifndef BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_
+#define BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_
+
+#include <limits>
+#include <mach-o/fat.h>
+#include <stdint.h>
+
+// Similar to struct fat_arch, except size-related parameters support
+// 64-bits.
+class SuperFatArch {
+ public:
+ uint32_t cputype;
+ uint32_t cpusubtype;
+ uint64_t offset;
+ uint64_t size;
+ uint64_t align;
+
+ SuperFatArch() :
+ cputype(0),
+ cpusubtype(0),
+ offset(0),
+ size(0),
+ align(0) {
+ }
+
+ explicit SuperFatArch(const struct fat_arch &arch) :
+ cputype(arch.cputype),
+ cpusubtype(arch.cpusubtype),
+ offset(arch.offset),
+ size(arch.size),
+ align(arch.align) {
+ }
+
+ // Returns false if the conversion cannot be made.
+ // If the conversion succeeds, the result is placed in |output_arch|.
+ bool ConvertToFatArch(struct fat_arch* output_arch) const {
+ if (offset > std::numeric_limits<uint32_t>::max())
+ return false;
+ if (size > std::numeric_limits<uint32_t>::max())
+ return false;
+ if (align > std::numeric_limits<uint32_t>::max())
+ return false;
+ struct fat_arch arch;
+ arch.cputype = cputype;
+ arch.cpusubtype = cpusubtype;
+ arch.offset = offset;
+ arch.size = size;
+ arch.align = align;
+ *output_arch = arch;
+ return true;
+ }
+};
+
+#endif // BREAKPAD_COMMON_MAC_SUPER_FAT_ARCH_H_
diff --git a/3rdParty/Breakpad/src/common/md5.cc b/3rdParty/Breakpad/src/common/md5.cc
index bccf61c..4f1ac8c 100644
--- a/3rdParty/Breakpad/src/common/md5.cc
+++ b/3rdParty/Breakpad/src/common/md5.cc
@@ -58,7 +58,7 @@ void MD5Init(struct MD5Context *ctx)
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
-void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, size_t len)
{
u32 t;
@@ -138,8 +138,8 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
- ((u32 *) ctx->in)[14] = ctx->bits[0];
- ((u32 *) ctx->in)[15] = ctx->bits[1];
+ memcpy(&ctx->in[14], &ctx->bits[0], sizeof(u32));
+ memcpy(&ctx->in[15], &ctx->bits[1], sizeof(u32));
MD5Transform(ctx->buf, (u32 *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
@@ -166,7 +166,7 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
*/
static void MD5Transform(u32 buf[4], u32 const in[16])
{
- register u32 a, b, c, d;
+ u32 a, b, c, d;
a = buf[0];
b = buf[1];
diff --git a/3rdParty/Breakpad/src/common/md5.h b/3rdParty/Breakpad/src/common/md5.h
index e96521e..2ab0ab9 100644
--- a/3rdParty/Breakpad/src/common/md5.h
+++ b/3rdParty/Breakpad/src/common/md5.h
@@ -18,7 +18,7 @@ struct MD5Context {
void MD5Init(struct MD5Context *ctx);
-void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len);
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, size_t len);
void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
diff --git a/3rdParty/Breakpad/src/common/memory.h b/3rdParty/Breakpad/src/common/memory.h
index e90bd52..9158b50 100644
--- a/3rdParty/Breakpad/src/common/memory.h
+++ b/3rdParty/Breakpad/src/common/memory.h
@@ -35,9 +35,15 @@
#include <unistd.h>
#include <sys/mman.h>
+#include <memory>
+#include <vector>
+
+#if defined(MEMORY_SANITIZER)
+#include <sanitizer/msan_interface.h>
+#endif
+
#ifdef __APPLE__
#define sys_mmap mmap
-#define sys_mmap2 mmap
#define sys_munmap munmap
#define MAP_ANONYMOUS MAP_ANON
#else
@@ -57,14 +63,15 @@ class PageAllocator {
: page_size_(getpagesize()),
last_(NULL),
current_page_(NULL),
- page_offset_(0) {
+ page_offset_(0),
+ pages_allocated_(0) {
}
~PageAllocator() {
FreeAll();
}
- void *Alloc(unsigned bytes) {
+ void *Alloc(size_t bytes) {
if (!bytes)
return NULL;
@@ -79,35 +86,54 @@ class PageAllocator {
return ret;
}
- const unsigned pages =
+ const size_t pages =
(bytes + sizeof(PageHeader) + page_size_ - 1) / page_size_;
uint8_t *const ret = GetNPages(pages);
if (!ret)
return NULL;
- page_offset_ = (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) % page_size_;
+ page_offset_ =
+ (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) %
+ page_size_;
current_page_ = page_offset_ ? ret + page_size_ * (pages - 1) : NULL;
return ret + sizeof(PageHeader);
}
+ // Checks whether the page allocator owns the passed-in pointer.
+ // This method exists for testing pursposes only.
+ bool OwnsPointer(const void* p) {
+ for (PageHeader* header = last_; header; header = header->next) {
+ const char* current = reinterpret_cast<char*>(header);
+ if ((p >= current) && (p < current + header->num_pages * page_size_))
+ return true;
+ }
+
+ return false;
+ }
+
+ unsigned long pages_allocated() { return pages_allocated_; }
+
private:
- uint8_t *GetNPages(unsigned num_pages) {
-#ifdef __x86_64
+ uint8_t *GetNPages(size_t num_pages) {
void *a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-#else
- void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-#endif
if (a == MAP_FAILED)
return NULL;
+#if defined(MEMORY_SANITIZER)
+ // We need to indicate to MSan that memory allocated through sys_mmap is
+ // initialized, since linux_syscall_support.h doesn't have MSan hooks.
+ __msan_unpoison(a, page_size_ * num_pages);
+#endif
+
struct PageHeader *header = reinterpret_cast<PageHeader*>(a);
header->next = last_;
header->num_pages = num_pages;
last_ = header;
+ pages_allocated_ += num_pages;
+
return reinterpret_cast<uint8_t*>(a);
}
@@ -122,97 +148,102 @@ class PageAllocator {
struct PageHeader {
PageHeader *next; // pointer to the start of the next set of pages.
- unsigned num_pages; // the number of pages in this set.
+ size_t num_pages; // the number of pages in this set.
};
- const unsigned page_size_;
+ const size_t page_size_;
PageHeader *last_;
uint8_t *current_page_;
- unsigned page_offset_;
+ size_t page_offset_;
+ unsigned long pages_allocated_;
};
-// A wasteful vector is like a normal std::vector, except that it's very much
-// simplier and it allocates memory from a PageAllocator. It's wasteful
-// because, when resizing, it always allocates a whole new array since the
-// PageAllocator doesn't support realloc.
-template<class T>
-class wasteful_vector {
- public:
- wasteful_vector(PageAllocator *allocator, unsigned size_hint = 16)
- : allocator_(allocator),
- a_((T*) allocator->Alloc(sizeof(T) * size_hint)),
- allocated_(size_hint),
- used_(0) {
- }
-
- T& back() {
- return a_[used_ - 1];
- }
-
- const T& back() const {
- return a_[used_ - 1];
- }
-
- bool empty() const {
- return used_ == 0;
- }
-
- void push_back(const T& new_element) {
- if (used_ == allocated_)
- Realloc(allocated_ * 2);
- a_[used_++] = new_element;
- }
-
- size_t size() const {
- return used_;
- }
-
- void resize(unsigned sz, T c = T()) {
- // No need to test "sz >= 0", as "sz" is unsigned.
- if (sz <= used_) {
- used_ = sz;
- } else {
- unsigned a = allocated_;
- if (sz > a) {
- while (sz > a) {
- a *= 2;
- }
- Realloc(a);
- }
- while (sz > used_) {
- a_[used_++] = c;
- }
+// Wrapper to use with STL containers
+template <typename T>
+struct PageStdAllocator : public std::allocator<T> {
+ typedef typename std::allocator<T>::pointer pointer;
+ typedef typename std::allocator<T>::size_type size_type;
+
+ explicit PageStdAllocator(PageAllocator& allocator) : allocator_(allocator),
+ stackdata_(NULL),
+ stackdata_size_(0)
+ {}
+
+ template <class Other> PageStdAllocator(const PageStdAllocator<Other>& other)
+ : allocator_(other.allocator_),
+ stackdata_(nullptr),
+ stackdata_size_(0)
+ {}
+
+ explicit PageStdAllocator(PageAllocator& allocator,
+ pointer stackdata,
+ size_type stackdata_size) : allocator_(allocator),
+ stackdata_(stackdata),
+ stackdata_size_(stackdata_size)
+ {}
+
+ inline pointer allocate(size_type n, const void* = 0) {
+ const size_type size = sizeof(T) * n;
+ if (size <= stackdata_size_) {
+ return stackdata_;
}
+ return static_cast<pointer>(allocator_.Alloc(size));
}
- T& operator[](size_t index) {
- return a_[index];
+ inline void deallocate(pointer, size_type) {
+ // The PageAllocator doesn't free.
}
- const T& operator[](size_t index) const {
- return a_[index];
- }
+ template <typename U> struct rebind {
+ typedef PageStdAllocator<U> other;
+ };
private:
- void Realloc(unsigned new_size) {
- T *new_array =
- reinterpret_cast<T*>(allocator_->Alloc(sizeof(T) * new_size));
- memcpy(new_array, a_, used_ * sizeof(T));
- a_ = new_array;
- allocated_ = new_size;
+ // Silly workaround for the gcc from Android's ndk (gcc 4.6), which will
+ // otherwise complain that `other.allocator_` is private in the constructor
+ // code.
+ template<typename Other> friend struct PageStdAllocator;
+
+ PageAllocator& allocator_;
+ pointer stackdata_;
+ size_type stackdata_size_;
+};
+
+// A wasteful vector is a std::vector, except that it allocates memory from a
+// PageAllocator. It's wasteful because, when resizing, it always allocates a
+// whole new array since the PageAllocator doesn't support realloc.
+template<class T>
+class wasteful_vector : public std::vector<T, PageStdAllocator<T> > {
+ public:
+ wasteful_vector(PageAllocator* allocator, unsigned size_hint = 16)
+ : std::vector<T, PageStdAllocator<T> >(PageStdAllocator<T>(*allocator)) {
+ std::vector<T, PageStdAllocator<T> >::reserve(size_hint);
}
+ protected:
+ wasteful_vector(PageStdAllocator<T> allocator)
+ : std::vector<T, PageStdAllocator<T> >(allocator) {}
+};
- PageAllocator *const allocator_;
- T *a_; // pointer to an array of |allocated_| elements.
- unsigned allocated_; // size of |a_|, in elements.
- unsigned used_; // number of used slots in |a_|.
+// auto_wasteful_vector allocates space on the stack for N entries to avoid
+// using the PageAllocator for small data, while still allowing for larger data.
+template<class T, unsigned int N>
+class auto_wasteful_vector : public wasteful_vector<T> {
+ T stackdata_[N];
+ public:
+ auto_wasteful_vector(PageAllocator* allocator)
+ : wasteful_vector<T>(
+ PageStdAllocator<T>(*allocator,
+ &stackdata_[0],
+ sizeof(stackdata_))) {
+ std::vector<T, PageStdAllocator<T> >::reserve(N);
+ }
};
} // namespace google_breakpad
inline void* operator new(size_t nbytes,
google_breakpad::PageAllocator& allocator) {
- return allocator.Alloc(nbytes);
+ return allocator.Alloc(nbytes);
}
#endif // GOOGLE_BREAKPAD_COMMON_MEMORY_H_
diff --git a/3rdParty/Breakpad/src/common/memory_range.h b/3rdParty/Breakpad/src/common/memory_range.h
new file mode 100644
index 0000000..41dd2da
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/memory_range.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2011, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// memory_range.h: Define the google_breakpad::MemoryRange class, which
+// is a lightweight wrapper with a pointer and a length to encapsulate
+// a contiguous range of memory.
+
+#ifndef COMMON_MEMORY_RANGE_H_
+#define COMMON_MEMORY_RANGE_H_
+
+#include <stddef.h>
+
+#include "google_breakpad/common/breakpad_types.h"
+
+namespace google_breakpad {
+
+// A lightweight wrapper with a pointer and a length to encapsulate a
+// contiguous range of memory. It provides helper methods for checked
+// access of a subrange of the memory. Its implemementation does not
+// allocate memory or call into libc functions, and is thus safer to use
+// in a crashed environment.
+class MemoryRange {
+ public:
+ MemoryRange() : data_(NULL), length_(0) {}
+
+ MemoryRange(const void* data, size_t length) {
+ Set(data, length);
+ }
+
+ // Returns true if this memory range contains no data.
+ bool IsEmpty() const {
+ // Set() guarantees that |length_| is zero if |data_| is NULL.
+ return length_ == 0;
+ }
+
+ // Resets to an empty range.
+ void Reset() {
+ data_ = NULL;
+ length_ = 0;
+ }
+
+ // Sets this memory range to point to |data| and its length to |length|.
+ void Set(const void* data, size_t length) {
+ data_ = reinterpret_cast<const uint8_t*>(data);
+ // Always set |length_| to zero if |data_| is NULL.
+ length_ = data ? length : 0;
+ }
+
+ // Returns true if this range covers a subrange of |sub_length| bytes
+ // at |sub_offset| bytes of this memory range, or false otherwise.
+ bool Covers(size_t sub_offset, size_t sub_length) const {
+ // The following checks verify that:
+ // 1. sub_offset is within [ 0 .. length_ - 1 ]
+ // 2. sub_offset + sub_length is within
+ // [ sub_offset .. length_ ]
+ return sub_offset < length_ &&
+ sub_offset + sub_length >= sub_offset &&
+ sub_offset + sub_length <= length_;
+ }
+
+ // Returns a raw data pointer to a subrange of |sub_length| bytes at
+ // |sub_offset| bytes of this memory range, or NULL if the subrange
+ // is out of bounds.
+ const void* GetData(size_t sub_offset, size_t sub_length) const {
+ return Covers(sub_offset, sub_length) ? (data_ + sub_offset) : NULL;
+ }
+
+ // Same as the two-argument version of GetData() but uses sizeof(DataType)
+ // as the subrange length and returns an |DataType| pointer for convenience.
+ template <typename DataType>
+ const DataType* GetData(size_t sub_offset) const {
+ return reinterpret_cast<const DataType*>(
+ GetData(sub_offset, sizeof(DataType)));
+ }
+
+ // Returns a raw pointer to the |element_index|-th element of an array
+ // of elements of length |element_size| starting at |sub_offset| bytes
+ // of this memory range, or NULL if the element is out of bounds.
+ const void* GetArrayElement(size_t element_offset,
+ size_t element_size,
+ unsigned element_index) const {
+ size_t sub_offset = element_offset + element_index * element_size;
+ return GetData(sub_offset, element_size);
+ }
+
+ // Same as the three-argument version of GetArrayElement() but deduces
+ // the element size using sizeof(ElementType) and returns an |ElementType|
+ // pointer for convenience.
+ template <typename ElementType>
+ const ElementType* GetArrayElement(size_t element_offset,
+ unsigned element_index) const {
+ return reinterpret_cast<const ElementType*>(
+ GetArrayElement(element_offset, sizeof(ElementType), element_index));
+ }
+
+ // Returns a subrange of |sub_length| bytes at |sub_offset| bytes of
+ // this memory range, or an empty range if the subrange is out of bounds.
+ MemoryRange Subrange(size_t sub_offset, size_t sub_length) const {
+ return Covers(sub_offset, sub_length) ?
+ MemoryRange(data_ + sub_offset, sub_length) : MemoryRange();
+ }
+
+ // Returns a pointer to the beginning of this memory range.
+ const uint8_t* data() const { return data_; }
+
+ // Returns the length, in bytes, of this memory range.
+ size_t length() const { return length_; }
+
+ private:
+ // Pointer to the beginning of this memory range.
+ const uint8_t* data_;
+
+ // Length, in bytes, of this memory range.
+ size_t length_;
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_MEMORY_RANGE_H_
diff --git a/3rdParty/Breakpad/src/common/memory_range_unittest.cc b/3rdParty/Breakpad/src/common/memory_range_unittest.cc
new file mode 100644
index 0000000..f6cf8c8
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/memory_range_unittest.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2011, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// memory_range_unittest.cc: Unit tests for google_breakpad::MemoryRange.
+
+#include "breakpad_googletest_includes.h"
+#include "common/memory_range.h"
+
+using google_breakpad::MemoryRange;
+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(MemoryRangeTest, DefaultConstructor) {
+ MemoryRange range;
+ EXPECT_EQ(NULL, range.data());
+ EXPECT_EQ(0U, range.length());
+}
+
+TEST(MemoryRangeTest, ConstructorWithDataAndLength) {
+ MemoryRange range(kBuffer, kBufferSize);
+ EXPECT_EQ(kBufferPointer, range.data());
+ EXPECT_EQ(kBufferSize, range.length());
+}
+
+TEST(MemoryRangeTest, Reset) {
+ MemoryRange 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(MemoryRangeTest, Set) {
+ MemoryRange 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(MemoryRangeTest, SubrangeOfEmptyMemoryRange) {
+ MemoryRange range;
+ MemoryRange subrange = range.Subrange(0, 10);
+ EXPECT_EQ(NULL, subrange.data());
+ EXPECT_EQ(0U, subrange.length());
+}
+
+TEST(MemoryRangeTest, SubrangeAndGetData) {
+ MemoryRange 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);
+
+ MemoryRange 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(MemoryRangeTest, GetDataWithTemplateType) {
+ MemoryRange 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(MemoryRangeTest, GetArrayElement) {
+ MemoryRange 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(MemoryRangeTest, GetArrayElmentWithTemplateType) {
+ MemoryRange 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);
+}
diff --git a/3rdParty/Breakpad/src/common/memory_unittest.cc b/3rdParty/Breakpad/src/common/memory_unittest.cc
new file mode 100644
index 0000000..8d2494c
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/memory_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "breakpad_googletest_includes.h"
+#include "common/memory.h"
+
+using namespace google_breakpad;
+
+namespace {
+typedef testing::Test PageAllocatorTest;
+}
+
+TEST(PageAllocatorTest, Setup) {
+ PageAllocator allocator;
+ EXPECT_EQ(0U, allocator.pages_allocated());
+}
+
+TEST(PageAllocatorTest, SmallObjects) {
+ PageAllocator allocator;
+
+ EXPECT_EQ(0U, allocator.pages_allocated());
+ for (unsigned i = 1; i < 1024; ++i) {
+ uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(i));
+ ASSERT_FALSE(p == NULL);
+ memset(p, 0, i);
+ }
+}
+
+TEST(PageAllocatorTest, LargeObject) {
+ PageAllocator allocator;
+
+ EXPECT_EQ(0U, allocator.pages_allocated());
+ uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(10000));
+ ASSERT_FALSE(p == NULL);
+ EXPECT_EQ(3U, allocator.pages_allocated());
+ for (unsigned i = 1; i < 10; ++i) {
+ uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(i));
+ ASSERT_FALSE(p == NULL);
+ memset(p, 0, i);
+ }
+}
+
+namespace {
+typedef testing::Test WastefulVectorTest;
+}
+
+TEST(WastefulVectorTest, Setup) {
+ PageAllocator allocator_;
+ wasteful_vector<int> v(&allocator_);
+ ASSERT_TRUE(v.empty());
+ ASSERT_EQ(v.size(), 0u);
+}
+
+TEST(WastefulVectorTest, Simple) {
+ PageAllocator allocator_;
+ EXPECT_EQ(0U, allocator_.pages_allocated());
+ wasteful_vector<unsigned> v(&allocator_);
+
+ for (unsigned i = 0; i < 256; ++i) {
+ v.push_back(i);
+ ASSERT_EQ(i, v.back());
+ ASSERT_EQ(&v.back(), &v[i]);
+ }
+ ASSERT_FALSE(v.empty());
+ ASSERT_EQ(v.size(), 256u);
+ EXPECT_EQ(1U, allocator_.pages_allocated());
+ for (unsigned i = 0; i < 256; ++i)
+ ASSERT_EQ(v[i], i);
+}
+
+TEST(WastefulVectorTest, UsesPageAllocator) {
+ PageAllocator allocator_;
+ wasteful_vector<unsigned> v(&allocator_);
+ EXPECT_EQ(1U, allocator_.pages_allocated());
+
+ v.push_back(1);
+ ASSERT_TRUE(allocator_.OwnsPointer(&v[0]));
+}
+
+TEST(WastefulVectorTest, AutoWastefulVector) {
+ PageAllocator allocator_;
+ EXPECT_EQ(0U, allocator_.pages_allocated());
+
+ auto_wasteful_vector<unsigned, 4> v(&allocator_);
+ EXPECT_EQ(0U, allocator_.pages_allocated());
+
+ v.push_back(1);
+ EXPECT_EQ(0U, allocator_.pages_allocated());
+ EXPECT_FALSE(allocator_.OwnsPointer(&v[0]));
+
+ v.resize(4);
+ EXPECT_EQ(0U, allocator_.pages_allocated());
+ EXPECT_FALSE(allocator_.OwnsPointer(&v[0]));
+
+ v.resize(10);
+ EXPECT_EQ(1U, allocator_.pages_allocated());
+ EXPECT_TRUE(allocator_.OwnsPointer(&v[0]));
+}
diff --git a/3rdParty/Breakpad/src/common/minidump_type_helper.h b/3rdParty/Breakpad/src/common/minidump_type_helper.h
new file mode 100644
index 0000000..5a7d5a6
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/minidump_type_helper.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2014, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_TYPE_HELPER_H_
+#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_TYPE_HELPER_H_
+
+#include <stdint.h>
+
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+template <size_t>
+struct MDTypeHelper;
+
+template <>
+struct MDTypeHelper<sizeof(uint32_t)> {
+ typedef MDRawDebug32 MDRawDebug;
+ typedef MDRawLinkMap32 MDRawLinkMap;
+};
+
+template <>
+struct MDTypeHelper<sizeof(uint64_t)> {
+ typedef MDRawDebug64 MDRawDebug;
+ typedef MDRawLinkMap64 MDRawLinkMap;
+};
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_COMMON_MINIDUMP_TYPE_HELPER_H_
diff --git a/3rdParty/Breakpad/src/common/module.cc b/3rdParty/Breakpad/src/common/module.cc
index 4e257d1..b5fcb62 100644
--- a/3rdParty/Breakpad/src/common/module.cc
+++ b/3rdParty/Breakpad/src/common/module.cc
@@ -49,11 +49,13 @@ using std::hex;
Module::Module(const string &name, const string &os,
- const string &architecture, const string &id) :
+ const string &architecture, const string &id,
+ const string &code_id /* = "" */) :
name_(name),
os_(os),
architecture_(architecture),
id_(id),
+ code_id_(code_id),
load_address_(0) { }
Module::~Module() {
@@ -79,8 +81,34 @@ void Module::AddFunction(Function *function) {
// FUNC lines must not hold an empty name, so catch the problem early if
// callers try to add one.
assert(!function->name.empty());
+
+ // FUNCs are better than PUBLICs as they come with sizes, so remove an extern
+ // with the same address if present.
+ Extern ext(function->address);
+ ExternSet::iterator it_ext = externs_.find(&ext);
+ if (it_ext == externs_.end() &&
+ architecture_ == "arm" &&
+ (function->address & 0x1) == 0) {
+ // ARM THUMB functions have bit 0 set. ARM64 does not have THUMB.
+ Extern arm_thumb_ext(function->address | 0x1);
+ it_ext = externs_.find(&arm_thumb_ext);
+ }
+ if (it_ext != externs_.end()) {
+ delete *it_ext;
+ externs_.erase(it_ext);
+ }
+#if _DEBUG
+ {
+ // There should be no other PUBLIC symbols that overlap with the function.
+ Extern debug_ext(function->address);
+ ExternSet::iterator it_debug = externs_.lower_bound(&ext);
+ assert(it_debug == externs_.end() ||
+ (*it_debug)->address >= function->address + function->size);
+ }
+#endif
+
std::pair<FunctionSet::iterator,bool> ret = functions_.insert(function);
- if (!ret.second) {
+ if (!ret.second && (*ret.first != function)) {
// Free the duplicate that was not inserted because this Module
// now owns it.
delete function;
@@ -130,8 +158,7 @@ Module::File *Module::FindFile(const string &name) {
FileByNameMap::iterator destiny = files_.lower_bound(&name);
if (destiny == files_.end()
|| *destiny->first != name) { // Repeated string comparison, boo hoo.
- File *file = new File;
- file->name = name;
+ File *file = new File(name);
file->source_id = -1;
destiny = files_.insert(destiny,
FileByNameMap::value_type(&file->name, file));
@@ -155,7 +182,7 @@ void Module::GetFiles(vector<File *> *vec) {
vec->push_back(it->second);
}
-void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) {
+void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) const {
*vec = stack_frame_entries_;
}
@@ -204,62 +231,66 @@ bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) {
return stream.good();
}
-bool Module::Write(std::ostream &stream, bool cfi) {
+bool Module::Write(std::ostream &stream, SymbolData symbol_data) {
stream << "MODULE " << os_ << " " << architecture_ << " "
<< id_ << " " << name_ << endl;
if (!stream.good())
return ReportError();
- AssignSourceIds();
+ if (!code_id_.empty()) {
+ stream << "INFO CODE_ID " << code_id_ << endl;
+ }
- // Write out files.
- for (FileByNameMap::iterator file_it = files_.begin();
- file_it != files_.end(); ++file_it) {
- File *file = file_it->second;
- if (file->source_id >= 0) {
- stream << "FILE " << file->source_id << " " << file->name << endl;
- if (!stream.good())
- return ReportError();
+ if (symbol_data != ONLY_CFI) {
+ AssignSourceIds();
+
+ // Write out files.
+ for (FileByNameMap::iterator file_it = files_.begin();
+ file_it != files_.end(); ++file_it) {
+ File *file = file_it->second;
+ if (file->source_id >= 0) {
+ stream << "FILE " << file->source_id << " " << file->name << endl;
+ if (!stream.good())
+ return ReportError();
+ }
}
- }
- // Write out functions and their lines.
- for (FunctionSet::const_iterator func_it = functions_.begin();
- func_it != functions_.end(); ++func_it) {
- Function *func = *func_it;
- stream << "FUNC " << hex
- << (func->address - load_address_) << " "
- << func->size << " "
- << func->parameter_size << " "
- << func->name << dec << endl;
-
- if (!stream.good())
- return ReportError();
- for (vector<Line>::iterator line_it = func->lines.begin();
- line_it != func->lines.end(); ++line_it) {
- stream << hex
- << (line_it->address - load_address_) << " "
- << line_it->size << " "
- << dec
- << line_it->number << " "
- << line_it->file->source_id << endl;
+ // Write out functions and their lines.
+ for (FunctionSet::const_iterator func_it = functions_.begin();
+ func_it != functions_.end(); ++func_it) {
+ Function *func = *func_it;
+ stream << "FUNC " << hex
+ << (func->address - load_address_) << " "
+ << func->size << " "
+ << func->parameter_size << " "
+ << func->name << dec << endl;
if (!stream.good())
return ReportError();
+
+ for (vector<Line>::iterator line_it = func->lines.begin();
+ line_it != func->lines.end(); ++line_it) {
+ stream << hex
+ << (line_it->address - load_address_) << " "
+ << line_it->size << " "
+ << dec
+ << line_it->number << " "
+ << line_it->file->source_id << endl;
+ if (!stream.good())
+ return ReportError();
+ }
}
- }
- // Write out 'PUBLIC' records.
- for (ExternSet::const_iterator extern_it = externs_.begin();
- extern_it != externs_.end(); ++extern_it) {
- Extern *ext = *extern_it;
- stream << "PUBLIC " << hex
- << (ext->address - load_address_) << " 0 "
- << ext->name << dec << endl;
- if (!stream.good())
- return ReportError();
+ // Write out 'PUBLIC' records.
+ for (ExternSet::const_iterator extern_it = externs_.begin();
+ extern_it != externs_.end(); ++extern_it) {
+ Extern *ext = *extern_it;
+ stream << "PUBLIC " << hex
+ << (ext->address - load_address_) << " 0 "
+ << ext->name << dec << endl;
+ }
}
- if (cfi) {
+ if (symbol_data != NO_CFI) {
// Write out 'STACK CFI INIT' and 'STACK CFI' records.
vector<StackFrameEntry *>::const_iterator frame_it;
for (frame_it = stack_frame_entries_.begin();
diff --git a/3rdParty/Breakpad/src/common/module.h b/3rdParty/Breakpad/src/common/module.h
index cc89bba..6c2bb27 100644
--- a/3rdParty/Breakpad/src/common/module.h
+++ b/3rdParty/Breakpad/src/common/module.h
@@ -44,6 +44,7 @@
#include <string>
#include <vector>
+#include "common/symbol_data.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/breakpad_types.h"
@@ -60,7 +61,7 @@ using std::map;
class Module {
public:
// The type of addresses and sizes in a symbol table.
- typedef u_int64_t Address;
+ typedef uint64_t Address;
struct File;
struct Function;
struct Line;
@@ -73,8 +74,10 @@ class Module {
// A source file.
struct File {
+ explicit File(const string &name_input) : name(name_input), source_id(0) {}
+
// The name of the source file.
- string name;
+ const string name;
// The file's source id. The Write member function clears this
// field and assigns source ids a fresh, so any value placed here
@@ -84,6 +87,9 @@ class Module {
// A function.
struct Function {
+ Function(const string &name_input, const Address &address_input) :
+ name(name_input), address(address_input), size(0), parameter_size(0) {}
+
// For sorting by address. (Not style-guide compliant, but it's
// stupid not to put this in the struct.)
static bool CompareByAddress(const Function *x, const Function *y) {
@@ -91,10 +97,11 @@ class Module {
}
// The function's name.
- string name;
+ const string name;
// The start address and length of the function's code.
- Address address, size;
+ const Address address;
+ Address size;
// The function's parameter size.
Address parameter_size;
@@ -119,7 +126,8 @@ class Module {
// An exported symbol.
struct Extern {
- Address address;
+ explicit Extern(const Address &address_input) : address(address_input) {}
+ const Address address;
string name;
};
@@ -171,7 +179,7 @@ class Module {
// Create a new module with the given name, operating system,
// architecture, and ID string.
Module(const string &name, const string &os, const string &architecture,
- const string &id);
+ const string &id, const string &code_id = "");
~Module();
// Set the module's load address to LOAD_ADDRESS; addresses given
@@ -246,7 +254,7 @@ class Module {
// effectively a copy of the stack frame entry list, this is mostly
// useful for testing; other uses should probably get
// a more appropriate interface.)
- void GetStackFrameEntries(vector<StackFrameEntry *> *vec);
+ void GetStackFrameEntries(vector<StackFrameEntry *> *vec) const;
// Find those files in this module that are actually referred to by
// functions' line number data, and assign them source id numbers.
@@ -259,13 +267,21 @@ class Module {
// breakpad symbol format. Return true if all goes well, or false if
// an error occurs. This method writes out:
// - a header based on the values given to the constructor,
+ // If symbol_data is not ONLY_CFI then:
// - the source files added via FindFile,
// - the functions added via AddFunctions, each with its lines,
// - all public records,
- // - and if CFI is true, all CFI records.
+ // If symbol_data is not NO_CFI then:
+ // - all CFI records.
// Addresses in the output are all relative to the load address
// established by SetLoadAddress.
- bool Write(std::ostream &stream, bool cfi);
+ bool Write(std::ostream &stream, SymbolData symbol_data);
+
+ string name() const { return name_; }
+ string os() const { return os_; }
+ string architecture() const { return architecture_; }
+ string identifier() const { return id_; }
+ string code_identifier() const { return code_id_; }
private:
// Report an error that has occurred writing the symbol file, using
@@ -278,7 +294,7 @@ class Module {
static bool WriteRuleMap(const RuleMap &rule_map, std::ostream &stream);
// Module header entries.
- string name_, os_, architecture_, id_;
+ string name_, os_, architecture_, id_, code_id_;
// The module's nominal load address. Addresses for functions and
// lines are absolute, assuming the module is loaded at this
@@ -288,7 +304,7 @@ class Module {
// Relation for maps whose keys are strings shared with some other
// structure.
struct CompareStringPtrs {
- bool operator()(const string *x, const string *y) { return *x < *y; }
+ bool operator()(const string *x, const string *y) const { return *x < *y; }
};
// A map from filenames to File structures. The map's keys are
diff --git a/3rdParty/Breakpad/src/common/module_unittest.cc b/3rdParty/Breakpad/src/common/module_unittest.cc
new file mode 100644
index 0000000..78406e3
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/module_unittest.cc
@@ -0,0 +1,557 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// module_unittest.cc: Unit tests for google_breakpad::Module.
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "common/module.h"
+#include "common/using_std_string.h"
+
+using google_breakpad::Module;
+using std::stringstream;
+using std::vector;
+using testing::ContainerEq;
+
+static Module::Function *generate_duplicate_function(const string &name) {
+ const Module::Address DUP_ADDRESS = 0xd35402aac7a7ad5cLL;
+ const Module::Address DUP_SIZE = 0x200b26e605f99071LL;
+ const Module::Address DUP_PARAMETER_SIZE = 0xf14ac4fed48c4a99LL;
+
+ Module::Function *function = new Module::Function(name, DUP_ADDRESS);
+ function->size = DUP_SIZE;
+ function->parameter_size = DUP_PARAMETER_SIZE;
+ return function;
+}
+
+#define MODULE_NAME "name with spaces"
+#define MODULE_OS "os-name"
+#define MODULE_ARCH "architecture"
+#define MODULE_ID "id-string"
+#define MODULE_CODE_ID "code-id-string"
+
+TEST(Write, Header) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n",
+ contents.c_str());
+}
+
+TEST(Write, HeaderCodeId) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID, MODULE_CODE_ID);
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "INFO CODE_ID code-id-string\n",
+ contents.c_str());
+}
+
+TEST(Write, OneLineFunc) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ Module::File *file = m.FindFile("file_name.cc");
+ Module::Function *function = new Module::Function(
+ "function_name", 0xe165bf8023b9d9abLL);
+ function->size = 0x1e4bb0eb1cbf5b09LL;
+ function->parameter_size = 0x772beee89114358aLL;
+ Module::Line line = { 0xe165bf8023b9d9abLL, 0x1e4bb0eb1cbf5b09LL,
+ file, 67519080 };
+ function->lines.push_back(line);
+ m.AddFunction(function);
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "FILE 0 file_name.cc\n"
+ "FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a"
+ " function_name\n"
+ "e165bf8023b9d9ab 1e4bb0eb1cbf5b09 67519080 0\n",
+ contents.c_str());
+}
+
+TEST(Write, RelativeLoadAddress) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Some source files. We will expect to see them in lexicographic order.
+ Module::File *file1 = m.FindFile("filename-b.cc");
+ Module::File *file2 = m.FindFile("filename-a.cc");
+
+ // A function.
+ Module::Function *function = new Module::Function(
+ "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)", 0xbec774ea5dd935f3LL);
+ function->size = 0x2922088f98d3f6fcLL;
+ function->parameter_size = 0xe5e9aa008bd5f0d0LL;
+
+ // Some source lines. The module should not sort these.
+ Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL,
+ file1, 41676901 };
+ Module::Line line2 = { 0xdaf35bc123885c04LL, 0xcf621b8d324d0ebLL,
+ file2, 67519080 };
+ function->lines.push_back(line2);
+ function->lines.push_back(line1);
+
+ m.AddFunction(function);
+
+ // Some stack information.
+ Module::StackFrameEntry *entry = new Module::StackFrameEntry();
+ entry->address = 0x30f9e5c83323973dULL;
+ entry->size = 0x49fc9ca7c7c13dc2ULL;
+ entry->initial_rules[".cfa"] = "he was a handsome man";
+ entry->initial_rules["and"] = "what i want to know is";
+ entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
+ "do you like your blueeyed boy";
+ entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death";
+ m.AddStackFrameEntry(entry);
+
+ // Set the load address. Doing this after adding all the data to
+ // the module must work fine.
+ m.SetLoadAddress(0x2ab698b0b6407073LL);
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "FILE 0 filename-a.cc\n"
+ "FILE 1 filename-b.cc\n"
+ "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
+ " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
+ "b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n"
+ "9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n"
+ "STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2"
+ " .cfa: he was a handsome man"
+ " and: what i want to know is\n"
+ "STACK CFI 6434d177ce326cb"
+ " Mister: Death"
+ " how: do you like your blueeyed boy\n",
+ contents.c_str());
+}
+
+TEST(Write, OmitUnusedFiles) {
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Create some source files.
+ Module::File *file1 = m.FindFile("filename1");
+ m.FindFile("filename2"); // not used by any line
+ Module::File *file3 = m.FindFile("filename3");
+
+ // Create a function.
+ Module::Function *function = new Module::Function(
+ "function_name", 0x9b926d464f0b9384LL);
+ function->size = 0x4f524a4ba795e6a6LL;
+ function->parameter_size = 0xbbe8133a6641c9b7LL;
+
+ // Source files that refer to some files, but not others.
+ Module::Line line1 = { 0x595fa44ebacc1086LL, 0x1e1e0191b066c5b3LL,
+ file1, 137850127 };
+ Module::Line line2 = { 0x401ce8c8a12d25e3LL, 0x895751c41b8d2ce2LL,
+ file3, 28113549 };
+ function->lines.push_back(line1);
+ function->lines.push_back(line2);
+ m.AddFunction(function);
+
+ m.AssignSourceIds();
+
+ vector<Module::File *> vec;
+ m.GetFiles(&vec);
+ EXPECT_EQ((size_t) 3, vec.size());
+ EXPECT_STREQ("filename1", vec[0]->name.c_str());
+ EXPECT_NE(-1, vec[0]->source_id);
+ // Expect filename2 not to be used.
+ EXPECT_STREQ("filename2", vec[1]->name.c_str());
+ EXPECT_EQ(-1, vec[1]->source_id);
+ EXPECT_STREQ("filename3", vec[2]->name.c_str());
+ EXPECT_NE(-1, vec[2]->source_id);
+
+ stringstream s;
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "FILE 0 filename1\n"
+ "FILE 1 filename3\n"
+ "FUNC 9b926d464f0b9384 4f524a4ba795e6a6 bbe8133a6641c9b7"
+ " function_name\n"
+ "595fa44ebacc1086 1e1e0191b066c5b3 137850127 0\n"
+ "401ce8c8a12d25e3 895751c41b8d2ce2 28113549 1\n",
+ contents.c_str());
+}
+
+TEST(Write, NoCFI) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Some source files. We will expect to see them in lexicographic order.
+ Module::File *file1 = m.FindFile("filename.cc");
+
+ // A function.
+ Module::Function *function = new Module::Function(
+ "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)", 0xbec774ea5dd935f3LL);
+ function->size = 0x2922088f98d3f6fcLL;
+ function->parameter_size = 0xe5e9aa008bd5f0d0LL;
+
+ // Some source lines. The module should not sort these.
+ Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL,
+ file1, 41676901 };
+ function->lines.push_back(line1);
+
+ m.AddFunction(function);
+
+ // Some stack information.
+ Module::StackFrameEntry *entry = new Module::StackFrameEntry();
+ entry->address = 0x30f9e5c83323973dULL;
+ entry->size = 0x49fc9ca7c7c13dc2ULL;
+ entry->initial_rules[".cfa"] = "he was a handsome man";
+ entry->initial_rules["and"] = "what i want to know is";
+ entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
+ "do you like your blueeyed boy";
+ entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death";
+ m.AddStackFrameEntry(entry);
+
+ // Set the load address. Doing this after adding all the data to
+ // the module must work fine.
+ m.SetLoadAddress(0x2ab698b0b6407073LL);
+
+ m.Write(s, NO_CFI);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "FILE 0 filename.cc\n"
+ "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
+ " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
+ "9410dc39a798c580 1c2be6d6c5af2611 41676901 0\n",
+ contents.c_str());
+}
+
+TEST(Construct, AddFunctions) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two functions.
+ Module::Function *function1 = new Module::Function(
+ "_without_form", 0xd35024aa7ca7da5cLL);
+ function1->size = 0x200b26e605f99071LL;
+ function1->parameter_size = 0xf14ac4fed48c4a99LL;
+
+ Module::Function *function2 = new Module::Function(
+ "_and_void", 0x2987743d0b35b13fLL);
+ function2->size = 0xb369db048deb3010LL;
+ function2->parameter_size = 0x938e556cb5a79988LL;
+
+ // Put them in a vector.
+ vector<Module::Function *> vec;
+ vec.push_back(function1);
+ vec.push_back(function2);
+
+ m.AddFunctions(vec.begin(), vec.end());
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988"
+ " _and_void\n"
+ "FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99"
+ " _without_form\n",
+ contents.c_str());
+
+ // Check that m.GetFunctions returns the functions we expect.
+ vec.clear();
+ m.GetFunctions(&vec, vec.end());
+ EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function1));
+ EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function2));
+ EXPECT_EQ((size_t) 2, vec.size());
+}
+
+TEST(Construct, AddFrames) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // First STACK CFI entry, with no initial rules or deltas.
+ Module::StackFrameEntry *entry1 = new Module::StackFrameEntry();
+ entry1->address = 0xddb5f41285aa7757ULL;
+ entry1->size = 0x1486493370dc5073ULL;
+ m.AddStackFrameEntry(entry1);
+
+ // Second STACK CFI entry, with initial rules but no deltas.
+ Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
+ entry2->address = 0x8064f3af5e067e38ULL;
+ entry2->size = 0x0de2a5ee55509407ULL;
+ entry2->initial_rules[".cfa"] = "I think that I shall never see";
+ entry2->initial_rules["stromboli"] = "a poem lovely as a tree";
+ entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest";
+ m.AddStackFrameEntry(entry2);
+
+ // Third STACK CFI entry, with initial rules and deltas.
+ Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
+ entry3->address = 0x5e8d0db0a7075c6cULL;
+ entry3->size = 0x1c7edb12a7aea229ULL;
+ entry3->initial_rules[".cfa"] = "Whose woods are these";
+ entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
+ "the village though";
+ entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
+ "he will not see me stopping here";
+ entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
+ "his house is in";
+ entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
+ "I think I know";
+ m.AddStackFrameEntry(entry3);
+
+ // Check that Write writes STACK CFI records properly.
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
+ "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
+ " .cfa: I think that I shall never see"
+ " cannoli: a tree whose hungry mouth is prest"
+ " stromboli: a poem lovely as a tree\n"
+ "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229"
+ " .cfa: Whose woods are these\n"
+ "STACK CFI 36682fad3763ffff"
+ " .cfa: I think I know"
+ " stromboli: his house is in\n"
+ "STACK CFI 47ceb0f63c269d7f"
+ " calzone: the village though"
+ " cannoli: he will not see me stopping here\n",
+ contents.c_str());
+
+ // Check that GetStackFrameEntries works.
+ vector<Module::StackFrameEntry *> entries;
+ m.GetStackFrameEntries(&entries);
+ ASSERT_EQ(3U, entries.size());
+ // Check first entry.
+ EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address);
+ EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size);
+ ASSERT_EQ(0U, entries[0]->initial_rules.size());
+ ASSERT_EQ(0U, entries[0]->rule_changes.size());
+ // Check second entry.
+ EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address);
+ EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size);
+ ASSERT_EQ(3U, entries[1]->initial_rules.size());
+ Module::RuleMap entry2_initial;
+ entry2_initial[".cfa"] = "I think that I shall never see";
+ entry2_initial["stromboli"] = "a poem lovely as a tree";
+ entry2_initial["cannoli"] = "a tree whose hungry mouth is prest";
+ EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial));
+ ASSERT_EQ(0U, entries[1]->rule_changes.size());
+ // Check third entry.
+ EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address);
+ EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size);
+ Module::RuleMap entry3_initial;
+ entry3_initial[".cfa"] = "Whose woods are these";
+ EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial));
+ Module::RuleChangeMap entry3_changes;
+ entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
+ entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
+ entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
+ entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
+ "he will not see me stopping here";
+ EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes));
+}
+
+TEST(Construct, UniqueFiles) {
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+ Module::File *file1 = m.FindFile("foo");
+ Module::File *file2 = m.FindFile(string("bar"));
+ Module::File *file3 = m.FindFile(string("foo"));
+ Module::File *file4 = m.FindFile("bar");
+ EXPECT_NE(file1, file2);
+ EXPECT_EQ(file1, file3);
+ EXPECT_EQ(file2, file4);
+ EXPECT_EQ(file1, m.FindExistingFile("foo"));
+ EXPECT_TRUE(m.FindExistingFile("baz") == NULL);
+}
+
+TEST(Construct, DuplicateFunctions) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two functions.
+ Module::Function *function1 = generate_duplicate_function("_without_form");
+ Module::Function *function2 = generate_duplicate_function("_without_form");
+
+ m.AddFunction(function1);
+ m.AddFunction(function2);
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
+ " _without_form\n",
+ contents.c_str());
+}
+
+TEST(Construct, FunctionsWithSameAddress) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two functions.
+ Module::Function *function1 = generate_duplicate_function("_without_form");
+ Module::Function *function2 = generate_duplicate_function("_and_void");
+
+ m.AddFunction(function1);
+ m.AddFunction(function2);
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
+ " _and_void\n"
+ "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
+ " _without_form\n",
+ contents.c_str());
+}
+
+// Externs should be written out as PUBLIC records, sorted by
+// address.
+TEST(Construct, Externs) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two externs.
+ Module::Extern *extern1 = new Module::Extern(0xffff);
+ extern1->name = "_abc";
+ Module::Extern *extern2 = new Module::Extern(0xaaaa);
+ extern2->name = "_xyz";
+
+ m.AddExtern(extern1);
+ m.AddExtern(extern2);
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+
+ EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
+ MODULE_ID " " MODULE_NAME "\n"
+ "PUBLIC aaaa 0 _xyz\n"
+ "PUBLIC ffff 0 _abc\n",
+ contents.c_str());
+}
+
+// Externs with the same address should only keep the first entry
+// added.
+TEST(Construct, DuplicateExterns) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two externs.
+ Module::Extern *extern1 = new Module::Extern(0xffff);
+ extern1->name = "_xyz";
+ Module::Extern *extern2 = new Module::Extern(0xffff);
+ extern2->name = "_abc";
+
+ m.AddExtern(extern1);
+ m.AddExtern(extern2);
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+
+ EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
+ MODULE_ID " " MODULE_NAME "\n"
+ "PUBLIC ffff 0 _xyz\n",
+ contents.c_str());
+}
+
+// If there exists an extern and a function at the same address, only write
+// out the FUNC entry.
+TEST(Construct, FunctionsAndExternsWithSameAddress) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two externs.
+ Module::Extern* extern1 = new Module::Extern(0xabc0);
+ extern1->name = "abc";
+ Module::Extern* extern2 = new Module::Extern(0xfff0);
+ extern2->name = "xyz";
+
+ m.AddExtern(extern1);
+ m.AddExtern(extern2);
+
+ Module::Function* function = new Module::Function("_xyz", 0xfff0);
+ function->size = 0x10;
+ function->parameter_size = 0;
+ m.AddFunction(function);
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+
+ EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
+ MODULE_ID " " MODULE_NAME "\n"
+ "FUNC fff0 10 0 _xyz\n"
+ "PUBLIC abc0 0 abc\n",
+ contents.c_str());
+}
+
+// If there exists an extern and a function at the same address, only write
+// out the FUNC entry. For ARM THUMB, the extern that comes from the ELF
+// symbol section has bit 0 set.
+TEST(Construct, FunctionsAndThumbExternsWithSameAddress) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, "arm", MODULE_ID);
+
+ // Two THUMB externs.
+ Module::Extern* thumb_extern1 = new Module::Extern(0xabc1);
+ thumb_extern1->name = "thumb_abc";
+ Module::Extern* thumb_extern2 = new Module::Extern(0xfff1);
+ thumb_extern2->name = "thumb_xyz";
+
+ Module::Extern* arm_extern1 = new Module::Extern(0xcc00);
+ arm_extern1->name = "arm_func";
+
+ m.AddExtern(thumb_extern1);
+ m.AddExtern(thumb_extern2);
+ m.AddExtern(arm_extern1);
+
+ // The corresponding function from the DWARF debug data have the actual
+ // address.
+ Module::Function* function = new Module::Function("_thumb_xyz", 0xfff0);
+ function->size = 0x10;
+ function->parameter_size = 0;
+ m.AddFunction(function);
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+
+ EXPECT_STREQ("MODULE " MODULE_OS " arm "
+ MODULE_ID " " MODULE_NAME "\n"
+ "FUNC fff0 10 0 _thumb_xyz\n"
+ "PUBLIC abc1 0 thumb_abc\n"
+ "PUBLIC cc00 0 arm_func\n",
+ contents.c_str());
+}
diff --git a/3rdParty/Breakpad/src/common/scoped_ptr.h b/3rdParty/Breakpad/src/common/scoped_ptr.h
new file mode 100644
index 0000000..d137c18
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/scoped_ptr.h
@@ -0,0 +1,404 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Scopers help you manage ownership of a pointer, helping you easily manage the
+// a pointer within a scope, and automatically destroying the pointer at the
+// end of a scope. There are two main classes you will use, which correspond
+// to the operators new/delete and new[]/delete[].
+//
+// Example usage (scoped_ptr):
+// {
+// scoped_ptr<Foo> foo(new Foo("wee"));
+// } // foo goes out of scope, releasing the pointer with it.
+//
+// {
+// scoped_ptr<Foo> foo; // No pointer managed.
+// foo.reset(new Foo("wee")); // Now a pointer is managed.
+// foo.reset(new Foo("wee2")); // Foo("wee") was destroyed.
+// foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed.
+// foo->Method(); // Foo::Method() called.
+// foo.get()->Method(); // Foo::Method() called.
+// SomeFunc(foo.release()); // SomeFunc takes ownership, foo no longer
+// // manages a pointer.
+// foo.reset(new Foo("wee4")); // foo manages a pointer again.
+// foo.reset(); // Foo("wee4") destroyed, foo no longer
+// // manages a pointer.
+// } // foo wasn't managing a pointer, so nothing was destroyed.
+//
+// Example usage (scoped_array):
+// {
+// scoped_array<Foo> foo(new Foo[100]);
+// foo.get()->Method(); // Foo::Method on the 0th element.
+// foo[10].Method(); // Foo::Method on the 10th element.
+// }
+
+#ifndef COMMON_SCOPED_PTR_H_
+#define COMMON_SCOPED_PTR_H_
+
+// This is an implementation designed to match the anticipated future TR2
+// implementation of the scoped_ptr class, and its closely-related brethren,
+// scoped_array, scoped_ptr_malloc.
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+namespace google_breakpad {
+
+// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
+// automatically deletes the pointer it holds (if any).
+// That is, scoped_ptr<T> owns the T object that it points to.
+// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
+// Also like T*, scoped_ptr<T> is thread-compatible, and once you
+// dereference it, you get the threadsafety guarantees of T.
+//
+// The size of a scoped_ptr is small:
+// sizeof(scoped_ptr<C>) == sizeof(C*)
+template <class C>
+class scoped_ptr {
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to initializing with NULL.
+ // There is no way to create an uninitialized scoped_ptr.
+ // The input parameter must be allocated with new.
+ explicit scoped_ptr(C* p = NULL) : ptr_(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_ptr() {
+ enum { type_must_be_complete = sizeof(C) };
+ delete ptr_;
+ }
+
+ // 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 != ptr_) {
+ enum { type_must_be_complete = sizeof(C) };
+ delete ptr_;
+ ptr_ = p;
+ }
+ }
+
+ // Accessors to get the owned object.
+ // operator* and operator-> will assert() if there is no current object.
+ C& operator*() const {
+ assert(ptr_ != NULL);
+ return *ptr_;
+ }
+ C* operator->() const {
+ assert(ptr_ != NULL);
+ return ptr_;
+ }
+ C* get() const { return ptr_; }
+
+ // Comparison operators.
+ // These return whether two scoped_ptr refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(C* p) const { return ptr_ == p; }
+ bool operator!=(C* p) const { return ptr_ != p; }
+
+ // Swap two scoped pointers.
+ void swap(scoped_ptr& p2) {
+ C* tmp = ptr_;
+ ptr_ = p2.ptr_;
+ p2.ptr_ = tmp;
+ }
+
+ // Release a pointer.
+ // 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 = ptr_;
+ ptr_ = NULL;
+ return retVal;
+ }
+
+ private:
+ C* ptr_;
+
+ // Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't
+ // make sense, and if C2 == C, it still doesn't make sense because you should
+ // never have the same object owned by two different scoped_ptrs.
+ template <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
+ template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;
+
+ // Disallow evil constructors
+ scoped_ptr(const scoped_ptr&);
+ void operator=(const scoped_ptr&);
+};
+
+// Free functions
+template <class C>
+void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2) {
+ p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_ptr<C>& p2) {
+ return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_ptr<C>& p2) {
+ return p1 != p2.get();
+}
+
+// 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[](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 two scoped_array refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(C* p) const { return array_ == p; }
+ bool operator!=(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&);
+};
+
+// Free functions
+template <class C>
+void swap(scoped_array<C>& p1, scoped_array<C>& p2) {
+ p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_array<C>& p2) {
+ return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_array<C>& p2) {
+ return p1 != p2.get();
+}
+
+// This class wraps the c library function free() in a class that can be
+// passed as a template argument to scoped_ptr_malloc below.
+class ScopedPtrMallocFree {
+ public:
+ inline void operator()(void* x) const {
+ free(x);
+ }
+};
+
+// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
+// second template argument, the functor used to free the object.
+
+template<class C, class FreeProc = ScopedPtrMallocFree>
+class scoped_ptr_malloc {
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to initializing with NULL.
+ // There is no way to create an uninitialized scoped_ptr.
+ // The input parameter must be allocated with an allocator that matches the
+ // Free functor. For the default Free functor, this is malloc, calloc, or
+ // realloc.
+ explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {}
+
+ // Destructor. If there is a C object, call the Free functor.
+ ~scoped_ptr_malloc() {
+ reset();
+ }
+
+ // Reset. Calls the Free functor on 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 (ptr_ != p) {
+ FreeProc free_proc;
+ free_proc(ptr_);
+ ptr_ = p;
+ }
+ }
+
+ // Get the current object.
+ // operator* and operator-> will cause an assert() failure if there is
+ // no current object.
+ C& operator*() const {
+ assert(ptr_ != NULL);
+ return *ptr_;
+ }
+
+ C* operator->() const {
+ assert(ptr_ != NULL);
+ return ptr_;
+ }
+
+ C* get() const {
+ return ptr_;
+ }
+
+ // Comparison operators.
+ // These return whether a scoped_ptr_malloc and a plain pointer refer
+ // to the same object, not just to two different but equal objects.
+ // For compatibility with the boost-derived implementation, these
+ // take non-const arguments.
+ bool operator==(C* p) const {
+ return ptr_ == p;
+ }
+
+ bool operator!=(C* p) const {
+ return ptr_ != p;
+ }
+
+ // Swap two scoped pointers.
+ void swap(scoped_ptr_malloc & b) {
+ C* tmp = b.ptr_;
+ b.ptr_ = ptr_;
+ ptr_ = tmp;
+ }
+
+ // Release a pointer.
+ // 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* tmp = ptr_;
+ ptr_ = NULL;
+ return tmp;
+ }
+
+ private:
+ C* ptr_;
+
+ // no reason to use these: each scoped_ptr_malloc should have its own object
+ template <class C2, class GP>
+ bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
+ template <class C2, class GP>
+ bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;
+
+ // Disallow evil constructors
+ scoped_ptr_malloc(const scoped_ptr_malloc&);
+ void operator=(const scoped_ptr_malloc&);
+};
+
+template<class C, class FP> inline
+void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
+ a.swap(b);
+}
+
+template<class C, class FP> inline
+bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) {
+ return p == b.get();
+}
+
+template<class C, class FP> inline
+bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
+ return p != b.get();
+}
+
+} // namespace google_breakpad
+
+#endif // COMMON_SCOPED_PTR_H_
diff --git a/3rdParty/Breakpad/src/common/simple_string_dictionary.cc b/3rdParty/Breakpad/src/common/simple_string_dictionary.cc
new file mode 100644
index 0000000..e0a74ce
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/simple_string_dictionary.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "common/simple_string_dictionary.h"
+
+namespace google_breakpad {
+
+namespace {
+
+// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member
+// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this
+// property to ensure that Entry remains POD.
+union Compile_Assert {
+ NonAllocatingMap<1, 1, 1>::Entry Compile_Assert__entry_must_be_pod;
+};
+
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/simple_string_dictionary.h b/3rdParty/Breakpad/src/common/simple_string_dictionary.h
new file mode 100644
index 0000000..d2ab17f
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/simple_string_dictionary.h
@@ -0,0 +1,260 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef COMMON_SIMPLE_STRING_DICTIONARY_H_
+#define COMMON_SIMPLE_STRING_DICTIONARY_H_
+
+#include <assert.h>
+#include <string.h>
+
+#include "common/basictypes.h"
+
+namespace google_breakpad {
+
+// Opaque type for the serialized representation of a NonAllocatingMap. One is
+// created in NonAllocatingMap::Serialize and can be deserialized using one of
+// the constructors.
+struct SerializedNonAllocatingMap;
+
+// NonAllocatingMap is an implementation of a map/dictionary collection that
+// uses a fixed amount of storage, so that it does not perform any dynamic
+// allocations for its operations.
+//
+// The actual map storage (the Entry) is guaranteed to be POD, so that it can
+// be transmitted over various IPC mechanisms.
+//
+// The template parameters control the amount of storage used for the key,
+// value, and map. The KeySize and ValueSize are measured in bytes, not glyphs,
+// and includes space for a \0 byte. This gives space for KeySize-1 and
+// ValueSize-1 characters in an entry. NumEntries is the total number of
+// entries that will fit in the map.
+template <size_t KeySize, size_t ValueSize, size_t NumEntries>
+class NonAllocatingMap {
+ public:
+ // Constant and publicly accessible versions of the template parameters.
+ static const size_t key_size = KeySize;
+ static const size_t value_size = ValueSize;
+ static const size_t num_entries = NumEntries;
+
+ // An Entry object is a single entry in the map. If the key is a 0-length
+ // NUL-terminated string, the entry is empty.
+ struct Entry {
+ char key[KeySize];
+ char value[ValueSize];
+
+ bool is_active() const {
+ return key[0] != '\0';
+ }
+ };
+
+ // An Iterator can be used to iterate over all the active entries in a
+ // NonAllocatingMap.
+ class Iterator {
+ public:
+ explicit Iterator(const NonAllocatingMap& map)
+ : map_(map),
+ current_(0) {
+ }
+
+ // Returns the next entry in the map, or NULL if at the end of the
+ // collection.
+ const Entry* Next() {
+ while (current_ < map_.num_entries) {
+ const Entry* entry = &map_.entries_[current_++];
+ if (entry->is_active()) {
+ return entry;
+ }
+ }
+ return NULL;
+ }
+
+ private:
+ const NonAllocatingMap& map_;
+ size_t current_;
+
+ DISALLOW_COPY_AND_ASSIGN(Iterator);
+ };
+
+ NonAllocatingMap() : entries_() {
+ }
+
+ NonAllocatingMap(const NonAllocatingMap& other) {
+ *this = other;
+ }
+
+ NonAllocatingMap& operator=(const NonAllocatingMap& other) {
+ assert(other.key_size == key_size);
+ assert(other.value_size == value_size);
+ assert(other.num_entries == num_entries);
+ if (other.key_size == key_size && other.value_size == value_size &&
+ other.num_entries == num_entries) {
+ memcpy(entries_, other.entries_, sizeof(entries_));
+ }
+ return *this;
+ }
+
+ // Constructs a map from its serialized form. |map| should be the out
+ // parameter from Serialize() and |size| should be its return value.
+ NonAllocatingMap(const SerializedNonAllocatingMap* map, size_t size) {
+ assert(size == sizeof(entries_));
+ if (size == sizeof(entries_)) {
+ memcpy(entries_, map, size);
+ }
+ }
+
+ // Returns the number of active key/value pairs. The upper limit for this
+ // is NumEntries.
+ size_t GetCount() const {
+ size_t count = 0;
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (entries_[i].is_active()) {
+ ++count;
+ }
+ }
+ return count;
+ }
+
+ // Given |key|, returns its corresponding |value|. |key| must not be NULL. If
+ // the key is not found, NULL is returned.
+ const char* GetValueForKey(const char* key) const {
+ assert(key);
+ if (!key)
+ return NULL;
+
+ const Entry* entry = GetConstEntryForKey(key);
+ if (!entry)
+ return NULL;
+
+ return entry->value;
+ }
+
+ // Stores |value| into |key|, replacing the existing value if |key| is
+ // already present. |key| must not be NULL. If |value| is NULL, the key is
+ // removed from the map. If there is no more space in the map, then the
+ // operation silently fails.
+ void SetKeyValue(const char* key, const char* value) {
+ if (!value) {
+ RemoveKey(key);
+ return;
+ }
+
+ assert(key);
+ if (!key)
+ return;
+
+ // Key must not be an empty string.
+ assert(key[0] != '\0');
+ if (key[0] == '\0')
+ return;
+
+ Entry* entry = GetEntryForKey(key);
+
+ // If it does not yet exist, attempt to insert it.
+ if (!entry) {
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (!entries_[i].is_active()) {
+ entry = &entries_[i];
+
+ strncpy(entry->key, key, key_size);
+ entry->key[key_size - 1] = '\0';
+
+ break;
+ }
+ }
+ }
+
+ // If the map is out of space, entry will be NULL.
+ if (!entry)
+ return;
+
+#ifndef NDEBUG
+ // Sanity check that the key only appears once.
+ int count = 0;
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (strncmp(entries_[i].key, key, key_size) == 0)
+ ++count;
+ }
+ assert(count == 1);
+#endif
+
+ strncpy(entry->value, value, value_size);
+ entry->value[value_size - 1] = '\0';
+ }
+
+ // Given |key|, removes any associated value. |key| must not be NULL. If
+ // the key is not found, this is a noop.
+ void RemoveKey(const char* key) {
+ assert(key);
+ if (!key)
+ return;
+
+ Entry* entry = GetEntryForKey(key);
+ if (entry) {
+ entry->key[0] = '\0';
+ entry->value[0] = '\0';
+ }
+
+#ifndef NDEBUG
+ assert(GetEntryForKey(key) == NULL);
+#endif
+ }
+
+ // Places a serialized version of the map into |map| and returns the size.
+ // Both of these should be passed to the deserializing constructor. Note that
+ // the serialized |map| is scoped to the lifetime of the non-serialized
+ // instance of this class. The |map| can be copied across IPC boundaries.
+ size_t Serialize(const SerializedNonAllocatingMap** map) const {
+ *map = reinterpret_cast<const SerializedNonAllocatingMap*>(entries_);
+ return sizeof(entries_);
+ }
+
+ private:
+ const Entry* GetConstEntryForKey(const char* key) const {
+ for (size_t i = 0; i < num_entries; ++i) {
+ if (strncmp(key, entries_[i].key, key_size) == 0) {
+ return &entries_[i];
+ }
+ }
+ return NULL;
+ }
+
+ Entry* GetEntryForKey(const char* key) {
+ return const_cast<Entry*>(GetConstEntryForKey(key));
+ }
+
+ Entry entries_[NumEntries];
+};
+
+// For historical reasons this specialized version is available with the same
+// size factors as a previous implementation.
+typedef NonAllocatingMap<256, 256, 64> SimpleStringDictionary;
+
+} // namespace google_breakpad
+
+#endif // COMMON_SIMPLE_STRING_DICTIONARY_H_
diff --git a/3rdParty/Breakpad/src/common/simple_string_dictionary_unittest.cc b/3rdParty/Breakpad/src/common/simple_string_dictionary_unittest.cc
new file mode 100644
index 0000000..34f4b9e
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/simple_string_dictionary_unittest.cc
@@ -0,0 +1,308 @@
+// Copyright (c) 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "breakpad_googletest_includes.h"
+#include "common/simple_string_dictionary.h"
+
+namespace google_breakpad {
+
+TEST(NonAllocatingMapTest, Entry) {
+ typedef NonAllocatingMap<5, 9, 15> TestMap;
+ TestMap map;
+
+ const TestMap::Entry* entry = TestMap::Iterator(map).Next();
+ EXPECT_FALSE(entry);
+
+ // Try setting a key/value and then verify.
+ map.SetKeyValue("key1", "value1");
+ entry = TestMap::Iterator(map).Next();
+ ASSERT_TRUE(entry);
+ EXPECT_STREQ(entry->key, "key1");
+ EXPECT_STREQ(entry->value, "value1");
+
+ // Try setting a new value.
+ map.SetKeyValue("key1", "value3");
+ EXPECT_STREQ(entry->value, "value3");
+
+ // Make sure the key didn't change.
+ EXPECT_STREQ(entry->key, "key1");
+
+ // Clear the entry and verify the key and value are empty strings.
+ map.RemoveKey("key1");
+ EXPECT_FALSE(entry->is_active());
+ EXPECT_EQ(strlen(entry->key), 0u);
+ EXPECT_EQ(strlen(entry->value), 0u);
+}
+
+TEST(NonAllocatingMapTest, SimpleStringDictionary) {
+ // Make a new dictionary
+ SimpleStringDictionary dict;
+
+ // Set three distinct values on three keys
+ dict.SetKeyValue("key1", "value1");
+ dict.SetKeyValue("key2", "value2");
+ dict.SetKeyValue("key3", "value3");
+
+ EXPECT_NE(dict.GetValueForKey("key1"), "value1");
+ EXPECT_NE(dict.GetValueForKey("key2"), "value2");
+ EXPECT_NE(dict.GetValueForKey("key3"), "value3");
+ EXPECT_EQ(dict.GetCount(), 3u);
+ // try an unknown key
+ EXPECT_FALSE(dict.GetValueForKey("key4"));
+
+ // Remove a key
+ dict.RemoveKey("key3");
+
+ // Now make sure it's not there anymore
+ EXPECT_FALSE(dict.GetValueForKey("key3"));
+
+ // Remove by setting value to NULL
+ dict.SetKeyValue("key2", NULL);
+
+ // Now make sure it's not there anymore
+ EXPECT_FALSE(dict.GetValueForKey("key2"));
+}
+
+TEST(NonAllocatingMapTest, CopyAndAssign) {
+ NonAllocatingMap<10, 10, 10> map;
+ map.SetKeyValue("one", "a");
+ map.SetKeyValue("two", "b");
+ map.SetKeyValue("three", "c");
+ map.RemoveKey("two");
+ EXPECT_EQ(2u, map.GetCount());
+
+ // Test copy.
+ NonAllocatingMap<10, 10, 10> map_copy(map);
+ EXPECT_EQ(2u, map_copy.GetCount());
+ EXPECT_STREQ("a", map_copy.GetValueForKey("one"));
+ EXPECT_STREQ("c", map_copy.GetValueForKey("three"));
+ map_copy.SetKeyValue("four", "d");
+ EXPECT_STREQ("d", map_copy.GetValueForKey("four"));
+ EXPECT_FALSE(map.GetValueForKey("four"));
+
+ // Test assign.
+ NonAllocatingMap<10, 10, 10> map_assign;
+ map_assign = map;
+ EXPECT_EQ(2u, map_assign.GetCount());
+ EXPECT_STREQ("a", map_assign.GetValueForKey("one"));
+ EXPECT_STREQ("c", map_assign.GetValueForKey("three"));
+ map_assign.SetKeyValue("four", "d");
+ EXPECT_STREQ("d", map_assign.GetValueForKey("four"));
+ EXPECT_FALSE(map.GetValueForKey("four"));
+
+ map.RemoveKey("one");
+ EXPECT_FALSE(map.GetValueForKey("one"));
+ EXPECT_STREQ("a", map_copy.GetValueForKey("one"));
+ EXPECT_STREQ("a", map_assign.GetValueForKey("one"));
+}
+
+// Add a bunch of values to the dictionary, remove some entries in the middle,
+// and then add more.
+TEST(NonAllocatingMapTest, Iterator) {
+ SimpleStringDictionary* dict = new SimpleStringDictionary();
+ ASSERT_TRUE(dict);
+
+ char key[SimpleStringDictionary::key_size];
+ char value[SimpleStringDictionary::value_size];
+
+ const int kDictionaryCapacity = SimpleStringDictionary::num_entries;
+ const int kPartitionIndex = kDictionaryCapacity - 5;
+
+ // We assume at least this size in the tests below
+ ASSERT_GE(kDictionaryCapacity, 64);
+
+ // We'll keep track of the number of key/value pairs we think should
+ // be in the dictionary
+ int expectedDictionarySize = 0;
+
+ // Set a bunch of key/value pairs like key0/value0, key1/value1, ...
+ for (int i = 0; i < kPartitionIndex; ++i) {
+ sprintf(key, "key%d", i);
+ sprintf(value, "value%d", i);
+ dict->SetKeyValue(key, value);
+ }
+ expectedDictionarySize = kPartitionIndex;
+
+ // set a couple of the keys twice (with the same value) - should be nop
+ dict->SetKeyValue("key2", "value2");
+ dict->SetKeyValue("key4", "value4");
+ dict->SetKeyValue("key15", "value15");
+
+ // Remove some random elements in the middle
+ dict->RemoveKey("key7");
+ dict->RemoveKey("key18");
+ dict->RemoveKey("key23");
+ dict->RemoveKey("key31");
+ expectedDictionarySize -= 4; // we just removed four key/value pairs
+
+ // Set some more key/value pairs like key59/value59, key60/value60, ...
+ for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) {
+ sprintf(key, "key%d", i);
+ sprintf(value, "value%d", i);
+ dict->SetKeyValue(key, value);
+ }
+ expectedDictionarySize += kDictionaryCapacity - kPartitionIndex;
+
+ // Now create an iterator on the dictionary
+ SimpleStringDictionary::Iterator iter(*dict);
+
+ // We then verify that it iterates through exactly the number of
+ // key/value pairs we expect, and that they match one-for-one with what we
+ // would expect. The ordering of the iteration does not matter...
+
+ // used to keep track of number of occurrences found for key/value pairs
+ int count[kDictionaryCapacity];
+ memset(count, 0, sizeof(count));
+
+ int totalCount = 0;
+
+ const SimpleStringDictionary::Entry* entry;
+ while ((entry = iter.Next())) {
+ totalCount++;
+
+ // Extract keyNumber from a string of the form key<keyNumber>
+ int keyNumber;
+ sscanf(entry->key, "key%d", &keyNumber);
+
+ // Extract valueNumber from a string of the form value<valueNumber>
+ int valueNumber;
+ sscanf(entry->value, "value%d", &valueNumber);
+
+ // The value number should equal the key number since that's how we set them
+ EXPECT_EQ(keyNumber, valueNumber);
+
+ // Key and value numbers should be in proper range:
+ // 0 <= keyNumber < kDictionaryCapacity
+ bool isKeyInGoodRange =
+ (keyNumber >= 0 && keyNumber < kDictionaryCapacity);
+ bool isValueInGoodRange =
+ (valueNumber >= 0 && valueNumber < kDictionaryCapacity);
+ EXPECT_TRUE(isKeyInGoodRange);
+ EXPECT_TRUE(isValueInGoodRange);
+
+ if (isKeyInGoodRange && isValueInGoodRange) {
+ ++count[keyNumber];
+ }
+ }
+
+ // Make sure each of the key/value pairs showed up exactly one time, except
+ // for the ones which we removed.
+ for (size_t i = 0; i < kDictionaryCapacity; ++i) {
+ // Skip over key7, key18, key23, and key31, since we removed them
+ if (!(i == 7 || i == 18 || i == 23 || i == 31)) {
+ EXPECT_EQ(count[i], 1);
+ }
+ }
+
+ // Make sure the number of iterations matches the expected dictionary size.
+ EXPECT_EQ(totalCount, expectedDictionarySize);
+}
+
+
+TEST(NonAllocatingMapTest, AddRemove) {
+ NonAllocatingMap<5, 7, 6> map;
+ map.SetKeyValue("rob", "ert");
+ map.SetKeyValue("mike", "pink");
+ map.SetKeyValue("mark", "allays");
+
+ EXPECT_EQ(3u, map.GetCount());
+ EXPECT_STREQ("ert", map.GetValueForKey("rob"));
+ EXPECT_STREQ("pink", map.GetValueForKey("mike"));
+ EXPECT_STREQ("allays", map.GetValueForKey("mark"));
+
+ map.RemoveKey("mike");
+
+ EXPECT_EQ(2u, map.GetCount());
+ EXPECT_FALSE(map.GetValueForKey("mike"));
+
+ map.SetKeyValue("mark", "mal");
+ EXPECT_EQ(2u, map.GetCount());
+ EXPECT_STREQ("mal", map.GetValueForKey("mark"));
+
+ map.RemoveKey("mark");
+ EXPECT_EQ(1u, map.GetCount());
+ EXPECT_FALSE(map.GetValueForKey("mark"));
+}
+
+TEST(NonAllocatingMapTest, Serialize) {
+ typedef NonAllocatingMap<4, 5, 7> TestMap;
+ TestMap map;
+ map.SetKeyValue("one", "abc");
+ map.SetKeyValue("two", "def");
+ map.SetKeyValue("tre", "hig");
+
+ EXPECT_STREQ("abc", map.GetValueForKey("one"));
+ EXPECT_STREQ("def", map.GetValueForKey("two"));
+ EXPECT_STREQ("hig", map.GetValueForKey("tre"));
+
+ const SerializedNonAllocatingMap* serialized;
+ size_t size = map.Serialize(&serialized);
+
+ SerializedNonAllocatingMap* serialized_copy =
+ reinterpret_cast<SerializedNonAllocatingMap*>(malloc(size));
+ ASSERT_TRUE(serialized_copy);
+ memcpy(serialized_copy, serialized, size);
+
+ TestMap deserialized(serialized_copy, size);
+ free(serialized_copy);
+
+ EXPECT_EQ(3u, deserialized.GetCount());
+ EXPECT_STREQ("abc", deserialized.GetValueForKey("one"));
+ EXPECT_STREQ("def", deserialized.GetValueForKey("two"));
+ EXPECT_STREQ("hig", deserialized.GetValueForKey("tre"));
+}
+
+// Running out of space shouldn't crash.
+TEST(NonAllocatingMapTest, OutOfSpace) {
+ NonAllocatingMap<3, 2, 2> map;
+ map.SetKeyValue("a", "1");
+ map.SetKeyValue("b", "2");
+ map.SetKeyValue("c", "3");
+ EXPECT_EQ(2u, map.GetCount());
+ EXPECT_FALSE(map.GetValueForKey("c"));
+}
+
+#ifndef NDEBUG
+
+TEST(NonAllocatingMapTest, NullKey) {
+ NonAllocatingMap<4, 6, 6> map;
+ ASSERT_DEATH(map.SetKeyValue(NULL, "hello"), "");
+
+ map.SetKeyValue("hi", "there");
+ ASSERT_DEATH(map.GetValueForKey(NULL), "");
+ EXPECT_STREQ("there", map.GetValueForKey("hi"));
+
+ ASSERT_DEATH(map.GetValueForKey(NULL), "");
+ map.RemoveKey("hi");
+ EXPECT_EQ(0u, map.GetCount());
+}
+
+#endif // !NDEBUG
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/stabs_reader.h b/3rdParty/Breakpad/src/common/stabs_reader.h
index d89afc0..98ee2dd 100644
--- a/3rdParty/Breakpad/src/common/stabs_reader.h
+++ b/3rdParty/Breakpad/src/common/stabs_reader.h
@@ -53,11 +53,10 @@
#include <config.h>
#endif
-#ifdef HAVE_A_OUT_H
-#include <a.out.h>
-#endif
#ifdef HAVE_MACH_O_NLIST_H
#include <mach-o/nlist.h>
+#elif defined(HAVE_A_OUT_H)
+#include <a.out.h>
#endif
#include <string>
diff --git a/3rdParty/Breakpad/src/common/stabs_reader_unittest.cc b/3rdParty/Breakpad/src/common/stabs_reader_unittest.cc
new file mode 100644
index 0000000..a84da1c
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/stabs_reader_unittest.cc
@@ -0,0 +1,611 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader.
+
+#include <assert.h>
+#include <errno.h>
+#include <stab.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "common/stabs_reader.h"
+#include "common/test_assembler.h"
+#include "common/using_std_string.h"
+
+using ::testing::Eq;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::_;
+using google_breakpad::StabsHandler;
+using google_breakpad::StabsReader;
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::Section;
+using google_breakpad::test_assembler::kBigEndian;
+using google_breakpad::test_assembler::kLittleEndian;
+using std::map;
+
+namespace {
+
+// A StringAssembler is a class for generating .stabstr sections to present
+// as input to the STABS parser.
+class StringAssembler: public Section {
+ public:
+ StringAssembler() : in_cu_(false) { StartCU(); }
+
+ // Add the string S to this StringAssembler, and return the string's
+ // offset within this compilation unit's strings. If S has been added
+ // already, this returns the offset of its first instance.
+ size_t Add(const string &s) {
+ map<string, size_t>::iterator it = added_.find(s);
+ if (it != added_.end())
+ return it->second;
+ size_t offset = Size() - cu_start_;
+ AppendCString(s);
+ added_[s] = offset;
+ return offset;
+ }
+
+ // Start a fresh compilation unit string collection.
+ void StartCU() {
+ // Ignore duplicate calls to StartCU. Our test data don't always call
+ // StartCU at all, meaning that our constructor has to take care of it,
+ // meaning that tests that *do* call StartCU call it twice at the
+ // beginning. This is not worth smoothing out.
+ if (in_cu_) return;
+
+ added_.clear();
+ cu_start_ = Size();
+
+ // Each compilation unit's strings start with an empty string.
+ AppendCString("");
+ added_[""] = 0;
+
+ in_cu_ = true;
+ }
+
+ // Finish off the current CU's strings.
+ size_t EndCU() {
+ assert(in_cu_);
+ in_cu_ = false;
+ return Size() - cu_start_;
+ }
+
+ private:
+ // The offset of the start of this compilation unit's strings.
+ size_t cu_start_;
+
+ // True if we're in a CU.
+ bool in_cu_;
+
+ // A map from the strings that have been added to this section to
+ // their starting indices within their compilation unit.
+ map<string, size_t> added_;
+};
+
+// A StabsAssembler is a class for generating .stab sections to present as
+// test input for the STABS parser.
+class StabsAssembler: public Section {
+ public:
+ // Create a StabsAssembler that uses StringAssembler for its strings.
+ StabsAssembler(StringAssembler *string_assembler)
+ : Section(string_assembler->endianness()),
+ string_assembler_(string_assembler),
+ value_size_(0),
+ entry_count_(0),
+ cu_header_(NULL) { }
+ ~StabsAssembler() { assert(!cu_header_); }
+
+ // Accessor and setter for value_size_.
+ size_t value_size() const { return value_size_; }
+ StabsAssembler &set_value_size(size_t value_size) {
+ value_size_ = value_size;
+ return *this;
+ }
+
+ // Append a STAB entry to the end of this section with the given
+ // characteristics. NAME is the offset of this entry's name string within
+ // its compilation unit's portion of the .stabstr section; this can be a
+ // value generated by a StringAssembler. Return a reference to this
+ // StabsAssembler.
+ StabsAssembler &Stab(uint8_t type, uint8_t other, Label descriptor,
+ Label value, Label name) {
+ D32(name);
+ D8(type);
+ D8(other);
+ D16(descriptor);
+ Append(endianness(), value_size_, value);
+ entry_count_++;
+ return *this;
+ }
+
+ // As above, but automatically add NAME to our StringAssembler.
+ StabsAssembler &Stab(uint8_t type, uint8_t other, Label descriptor,
+ Label value, const string &name) {
+ return Stab(type, other, descriptor, value, string_assembler_->Add(name));
+ }
+
+ // Start a compilation unit named NAME, with an N_UNDF symbol to start
+ // it, and its own portion of the string section. Return a reference to
+ // this StabsAssembler.
+ StabsAssembler &StartCU(const string &name) {
+ assert(!cu_header_);
+ cu_header_ = new CUHeader;
+ string_assembler_->StartCU();
+ entry_count_ = 0;
+ return Stab(N_UNDF, 0,
+ cu_header_->final_entry_count,
+ cu_header_->final_string_size,
+ string_assembler_->Add(name));
+ }
+
+ // Close off the current compilation unit. Return a reference to this
+ // StabsAssembler.
+ StabsAssembler &EndCU() {
+ assert(cu_header_);
+ cu_header_->final_entry_count = entry_count_;
+ cu_header_->final_string_size = string_assembler_->EndCU();
+ delete cu_header_;
+ cu_header_ = NULL;
+ return *this;
+ }
+
+ private:
+ // Data used in a compilation unit header STAB that we won't know until
+ // we've finished the compilation unit.
+ struct CUHeader {
+ // The final number of entries this compilation unit will hold.
+ Label final_entry_count;
+
+ // The final size of this compilation unit's strings.
+ Label final_string_size;
+ };
+
+ // The strings for our STABS entries.
+ StringAssembler *string_assembler_;
+
+ // The size of the 'value' field of stabs entries in this section.
+ size_t value_size_;
+
+ // The number of entries in this compilation unit so far.
+ size_t entry_count_;
+
+ // Header labels for this compilation unit, if we've started one but not
+ // finished it.
+ CUHeader *cu_header_;
+};
+
+class MockStabsReaderHandler: public StabsHandler {
+ public:
+ MOCK_METHOD3(StartCompilationUnit,
+ bool(const char *, uint64_t, const char *));
+ MOCK_METHOD1(EndCompilationUnit, bool(uint64_t));
+ MOCK_METHOD2(StartFunction, bool(const string &, uint64_t));
+ MOCK_METHOD1(EndFunction, bool(uint64_t));
+ MOCK_METHOD3(Line, bool(uint64_t, const char *, int));
+ MOCK_METHOD2(Extern, bool(const string &, uint64_t));
+ void Warning(const char *format, ...) { MockWarning(format); }
+ MOCK_METHOD1(MockWarning, void(const char *));
+};
+
+struct StabsFixture {
+ StabsFixture() : stabs(&strings), unitized(true) { }
+
+ // Create a StabsReader to parse the mock stabs data in stabs and
+ // strings, and pass the parsed information to mock_handler. Use the
+ // endianness and value size of stabs to parse the data. If all goes
+ // well, return the result of calling the reader's Process member
+ // function. Otherwise, return false.
+ bool ApplyHandlerToMockStabsData() {
+ string stabs_contents, stabstr_contents;
+ if (!stabs.GetContents(&stabs_contents) ||
+ !strings.GetContents(&stabstr_contents))
+ return false;
+
+ // Run the parser on the test input, passing whatever we find to HANDLER.
+ StabsReader reader(
+ reinterpret_cast<const uint8_t *>(stabs_contents.data()),
+ stabs_contents.size(),
+ reinterpret_cast<const uint8_t *>(stabstr_contents.data()),
+ stabstr_contents.size(),
+ stabs.endianness() == kBigEndian, stabs.value_size(), unitized,
+ &mock_handler);
+ return reader.Process();
+ }
+
+ StringAssembler strings;
+ StabsAssembler stabs;
+ bool unitized;
+ MockStabsReaderHandler mock_handler;
+};
+
+class Stabs: public StabsFixture, public Test { };
+
+TEST_F(Stabs, MockStabsInput) {
+ stabs.set_endianness(kLittleEndian);
+ stabs.set_value_size(4);
+ stabs
+ .Stab(N_SO, 149, 40232, 0x18a2a72bU, "builddir/")
+ .Stab(N_FUN, 83, 50010, 0x91a5353fU,
+ "not the SO with source file name we expected ")
+ .Stab(N_SO, 165, 24791, 0xfe69d23cU, "")
+ .Stab(N_SO, 184, 34178, 0xca4d883aU, "builddir1/")
+ .Stab(N_SO, 83, 40859, 0xd2fe5df3U, "file1.c")
+ .Stab(N_LSYM, 147, 39565, 0x60d4bb8aU, "not the FUN we're looking for")
+ .Stab(N_FUN, 120, 50271, 0xa049f4b1U, "fun1")
+ .Stab(N_BINCL, 150, 15694, 0xef65c659U,
+ "something to ignore in a FUN body")
+ .Stab(N_SLINE, 147, 4967, 0xd904b3f, "")
+ .Stab(N_SOL, 177, 56135, 0xbd97b1dcU, "header.h")
+ .Stab(N_SLINE, 130, 24610, 0x90f145b, "")
+ .Stab(N_FUN, 45, 32441, 0xbf27cf93U,
+ "fun2:some stabs type info here:to trim from the name")
+ .Stab(N_SLINE, 138, 39002, 0x8148b87, "")
+ .Stab(N_SOL, 60, 49318, 0x1d06e025U, "file1.c")
+ .Stab(N_SLINE, 29, 52163, 0x6eebbb7, "")
+ .Stab(N_SO, 167, 4647, 0xd04b7448U, "")
+ .Stab(N_LSYM, 58, 37837, 0xe6b14d37U, "")
+ .Stab(N_SO, 152, 7810, 0x11759f10U, "file3.c")
+ .Stab(N_SO, 218, 12447, 0x11cfe4b5U, "");
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("file1.c"), 0xd2fe5df3U,
+ StrEq("builddir1/")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, StartFunction(StrEq("fun1"), 0xa049f4b1U))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ Line(0xa049f4b1U + 0xd904b3f, StrEq("file1.c"), 4967))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ Line(0xa049f4b1U + 0x90f145b, StrEq("header.h"), 24610))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0xbf27cf93U))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, StartFunction(StrEq("fun2"), 0xbf27cf93U))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ Line(0xbf27cf93U + 0x8148b87, StrEq("header.h"), 39002))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ Line(0xbf27cf93U + 0x6eebbb7, StrEq("file1.c"), 52163))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0xd04b7448U))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0xd04b7448U))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file3.c"),
+ 0x11759f10U, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0x11cfe4b5U))
+ .WillOnce(Return(true));
+ }
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+TEST_F(Stabs, AbruptCU) {
+ stabs.set_endianness(kBigEndian);
+ stabs.set_value_size(4);
+ stabs.Stab(N_SO, 177, 23446, 0xbf10d5e4, "file2-1.c");
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("file2-1.c"), 0xbf10d5e4, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0))
+ .WillOnce(Return(true));
+ }
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+TEST_F(Stabs, AbruptFunction) {
+ stabs.set_endianness(kLittleEndian);
+ stabs.set_value_size(8);
+ stabs
+ .Stab(N_SO, 218, 26631, 0xb83ddf10U, "file3-1.c")
+ .Stab(N_FUN, 113, 24765, 0xbbd4a145U, "fun3_1");
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("file3-1.c"), 0xb83ddf10U, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, StartFunction(StrEq("fun3_1"), 0xbbd4a145U))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0))
+ .WillOnce(Return(true));
+ }
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+TEST_F(Stabs, NoCU) {
+ stabs.set_endianness(kBigEndian);
+ stabs.set_value_size(8);
+ stabs.Stab(N_SO, 161, 25673, 0x8f676e7bU, "build-directory/");
+
+ EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _))
+ .Times(0);
+ EXPECT_CALL(mock_handler, StartFunction(_, _))
+ .Times(0);
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+TEST_F(Stabs, NoCUEnd) {
+ stabs.set_endianness(kBigEndian);
+ stabs.set_value_size(8);
+ stabs
+ .Stab(N_SO, 116, 58280, 0x2f7493c9U, "file5-1.c")
+ .Stab(N_SO, 224, 23057, 0xf9f1d50fU, "file5-2.c");
+
+ {
+ InSequence s;
+
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("file5-1.c"), 0x2f7493c9U, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("file5-2.c"), 0xf9f1d50fU, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0))
+ .WillOnce(Return(true));
+ }
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+// On systems that store STABS in sections, string offsets are relative to
+// the beginning of that compilation unit's strings, marked with N_UNDF
+// symbols; see the comments for StabsReader::StabsReader.
+TEST_F(Stabs, Unitized) {
+ stabs.set_endianness(kBigEndian);
+ stabs.set_value_size(4);
+ stabs
+ .StartCU("antimony")
+ .Stab(N_SO, 49, 26043, 0x7e259f1aU, "antimony")
+ .Stab(N_FUN, 101, 63253, 0x7fbcccaeU, "arsenic")
+ .Stab(N_SO, 124, 37175, 0x80b0014cU, "")
+ .EndCU()
+ .StartCU("aluminum")
+ .Stab(N_SO, 72, 23084, 0x86756839U, "aluminum")
+ .Stab(N_FUN, 59, 3305, 0xa8e120b0U, "selenium")
+ .Stab(N_SO, 178, 56949, 0xbffff983U, "")
+ .EndCU();
+
+ {
+ InSequence s;
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("antimony"), 0x7e259f1aU, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x7fbcccaeU))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0x80b0014cU))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0x80b0014cU))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("aluminum"), 0x86756839U, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0xa8e120b0U))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0xbffff983U))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0xbffff983U))
+ .WillOnce(Return(true));
+ }
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+// On systems that store STABS entries in the real symbol table, the N_UNDF
+// entries have no special meaning, and shouldn't mess up the string
+// indices.
+TEST_F(Stabs, NonUnitized) {
+ stabs.set_endianness(kLittleEndian);
+ stabs.set_value_size(4);
+ unitized = false;
+ stabs
+ .Stab(N_UNDF, 21, 11551, 0x9bad2b2e, "")
+ .Stab(N_UNDF, 21, 11551, 0x9bad2b2e, "")
+ .Stab(N_SO, 71, 45139, 0x11a97352, "Tanzania")
+ .Stab(N_SO, 221, 41976, 0x21a97352, "");
+
+ {
+ InSequence s;
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("Tanzania"),
+ 0x11a97352, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0x21a97352))
+ .WillOnce(Return(true));
+ }
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+TEST_F(Stabs, FunctionEnd) {
+ stabs.set_endianness(kLittleEndian);
+ stabs.set_value_size(8);
+ stabs
+ .Stab(N_SO, 102, 62362, 0x52a830d644cd6942ULL, "compilation unit")
+ // This function is terminated by the start of the next function.
+ .Stab(N_FUN, 216, 38405, 0xbb5ab70ecdd23bfeULL, "function 1")
+ // This function is terminated by an explicit end-of-function stab,
+ // whose value is a size in bytes.
+ .Stab(N_FUN, 240, 10973, 0xc954de9b8fb3e5e2ULL, "function 2")
+ .Stab(N_FUN, 14, 36749, 0xc1ab, "")
+ // This function is terminated by the end of the compilation unit.
+ .Stab(N_FUN, 143, 64514, 0xdff98c9a35386e1fULL, "function 3")
+ .Stab(N_SO, 164, 60142, 0xfdacb856e78bbf57ULL, "");
+
+ {
+ InSequence s;
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("compilation unit"),
+ 0x52a830d644cd6942ULL, NULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ StartFunction(Eq("function 1"), 0xbb5ab70ecdd23bfeULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0xc954de9b8fb3e5e2ULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ StartFunction(Eq("function 2"), 0xc954de9b8fb3e5e2ULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0xc954de9b8fb3e5e2ULL + 0xc1ab))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ StartFunction(Eq("function 3"), 0xdff98c9a35386e1fULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0xfdacb856e78bbf57ULL))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0xfdacb856e78bbf57ULL))
+ .WillOnce(Return(true));
+ }
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+// On Mac OS X, SLINE records can appear before the FUN stab to which they
+// belong, and their values are absolute addresses, not offsets.
+TEST_F(Stabs, LeadingLine) {
+ stabs.set_endianness(kBigEndian);
+ stabs.set_value_size(4);
+ stabs
+ .Stab(N_SO, 179, 27357, 0x8adabc15, "build directory/")
+ .Stab(N_SO, 52, 53058, 0x4c7e3bf4, "compilation unit")
+ .Stab(N_SOL, 165, 12086, 0x6a797ca3, "source file name")
+ .Stab(N_SLINE, 229, 20015, 0x4cb3d7e0, "")
+ .Stab(N_SLINE, 89, 43802, 0x4cba8b88, "")
+ .Stab(N_FUN, 251, 51639, 0xce1b98fa, "rutabaga")
+ .Stab(N_FUN, 218, 16113, 0x5798, "")
+ .Stab(N_SO, 52, 53058, 0xd4af4415, "");
+
+ {
+ InSequence s;
+ EXPECT_CALL(mock_handler,
+ StartCompilationUnit(StrEq("compilation unit"),
+ 0x4c7e3bf4, StrEq("build directory/")))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ StartFunction(Eq("rutabaga"), 0xce1b98fa))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ Line(0x4cb3d7e0, StrEq("source file name"), 20015))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ Line(0x4cba8b88, StrEq("source file name"), 43802))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndFunction(0xce1b98fa + 0x5798))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler, EndCompilationUnit(0xd4af4415))
+ .WillOnce(Return(true));
+ }
+
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+
+#if defined(HAVE_MACH_O_NLIST_H)
+// These tests have no meaning on non-Mach-O-based systems, as
+// only Mach-O uses N_SECT to represent public symbols.
+TEST_F(Stabs, OnePublicSymbol) {
+ stabs.set_endianness(kLittleEndian);
+ stabs.set_value_size(4);
+
+ const uint32_t kExpectedAddress = 0x9000;
+ const string kExpectedFunctionName("public_function");
+ stabs
+ .Stab(N_SECT, 1, 0, kExpectedAddress, kExpectedFunctionName);
+
+ {
+ InSequence s;
+ EXPECT_CALL(mock_handler,
+ Extern(StrEq(kExpectedFunctionName),
+ kExpectedAddress))
+ .WillOnce(Return(true));
+ }
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+TEST_F(Stabs, TwoPublicSymbols) {
+ stabs.set_endianness(kLittleEndian);
+ stabs.set_value_size(4);
+
+ const uint32_t kExpectedAddress1 = 0xB0B0B0B0;
+ const string kExpectedFunctionName1("public_function");
+ const uint32_t kExpectedAddress2 = 0xF0F0F0F0;
+ const string kExpectedFunctionName2("something else");
+ stabs
+ .Stab(N_SECT, 1, 0, kExpectedAddress1, kExpectedFunctionName1)
+ .Stab(N_SECT, 1, 0, kExpectedAddress2, kExpectedFunctionName2);
+
+ {
+ InSequence s;
+ EXPECT_CALL(mock_handler,
+ Extern(StrEq(kExpectedFunctionName1),
+ kExpectedAddress1))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_handler,
+ Extern(StrEq(kExpectedFunctionName2),
+ kExpectedAddress2))
+ .WillOnce(Return(true));
+ }
+ ASSERT_TRUE(ApplyHandlerToMockStabsData());
+}
+
+#endif
+
+} // anonymous namespace
diff --git a/3rdParty/Breakpad/src/common/stabs_to_module.cc b/3rdParty/Breakpad/src/common/stabs_to_module.cc
index e59aebd..0a83cf2 100644
--- a/3rdParty/Breakpad/src/common/stabs_to_module.cc
+++ b/3rdParty/Breakpad/src/common/stabs_to_module.cc
@@ -90,9 +90,7 @@ bool StabsToModule::EndCompilationUnit(uint64_t address) {
bool StabsToModule::StartFunction(const string &name,
uint64_t address) {
assert(!current_function_);
- Module::Function *f = new Module::Function;
- f->name = Demangle(name);
- f->address = address;
+ Module::Function *f = new Module::Function(Demangle(name), address);
f->size = 0; // We compute this in StabsToModule::Finalize().
f->parameter_size = 0; // We don't provide this information.
current_function_ = f;
@@ -133,7 +131,7 @@ bool StabsToModule::Line(uint64_t address, const char *name, int number) {
}
bool StabsToModule::Extern(const string &name, uint64_t address) {
- Module::Extern *ext = new Module::Extern;
+ Module::Extern *ext = new Module::Extern(address);
// Older libstdc++ demangle implementations can crash on unexpected
// input, so be careful about what gets passed in.
if (name.compare(0, 3, "__Z") == 0) {
@@ -143,7 +141,6 @@ bool StabsToModule::Extern(const string &name, uint64_t address) {
} else {
ext->name = name;
}
- ext->address = address;
module_->AddExtern(ext);
return true;
}
diff --git a/3rdParty/Breakpad/src/common/stabs_to_module_unittest.cc b/3rdParty/Breakpad/src/common/stabs_to_module_unittest.cc
new file mode 100644
index 0000000..d445d1d
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/stabs_to_module_unittest.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dump_stabs_unittest.cc: Unit tests for StabsToModule.
+
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/stabs_to_module.h"
+
+using google_breakpad::Module;
+using google_breakpad::StabsToModule;
+using std::vector;
+
+TEST(StabsToModule, SimpleCU) {
+ Module m("name", "os", "arch", "id");
+ StabsToModule h(&m);
+
+ // Feed in a simple compilation unit that defines a function with
+ // one line.
+ EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0x9f4d1271e50db93bLL,
+ "build-directory"));
+ EXPECT_TRUE(h.StartFunction("function", 0xfde4abbed390c394LL));
+ EXPECT_TRUE(h.Line(0xfde4abbed390c394LL, "source-file-name", 174823314));
+ EXPECT_TRUE(h.EndFunction(0xfde4abbed390c3a4LL));
+ EXPECT_TRUE(h.EndCompilationUnit(0xfee4abbed390c3a4LL));
+ h.Finalize();
+
+ // Now check to see what has been added to the Module.
+ Module::File *file = m.FindExistingFile("source-file-name");
+ ASSERT_TRUE(file != NULL);
+
+ vector<Module::Function *> functions;
+ m.GetFunctions(&functions, functions.end());
+ ASSERT_EQ((size_t) 1, functions.size());
+ Module::Function *function = functions[0];
+ EXPECT_STREQ("function", function->name.c_str());
+ EXPECT_EQ(0xfde4abbed390c394LL, function->address);
+ EXPECT_EQ(0x10U, function->size);
+ EXPECT_EQ(0U, function->parameter_size);
+ ASSERT_EQ((size_t) 1, function->lines.size());
+ Module::Line *line = &function->lines[0];
+ EXPECT_EQ(0xfde4abbed390c394LL, line->address);
+ EXPECT_EQ(0x10U, line->size); // derived from EndFunction
+ EXPECT_TRUE(line->file == file);
+ EXPECT_EQ(174823314, line->number);
+}
+
+#ifdef __GNUC__
+// Function name mangling can vary by compiler, so only run mangled-name
+// tests on GCC for simplicity's sake.
+TEST(StabsToModule, Externs) {
+ Module m("name", "os", "arch", "id");
+ StabsToModule h(&m);
+
+ // Feed in a few Extern symbols.
+ EXPECT_TRUE(h.Extern("_foo", 0xffff));
+ EXPECT_TRUE(h.Extern("__Z21dyldGlobalLockAcquirev", 0xaaaa));
+ EXPECT_TRUE(h.Extern("_MorphTableGetNextMorphChain", 0x1111));
+ h.Finalize();
+
+ // Now check to see what has been added to the Module.
+ vector<Module::Extern *> externs;
+ m.GetExterns(&externs, externs.end());
+ ASSERT_EQ((size_t) 3, externs.size());
+ Module::Extern *extern1 = externs[0];
+ EXPECT_STREQ("MorphTableGetNextMorphChain", extern1->name.c_str());
+ EXPECT_EQ((Module::Address)0x1111, extern1->address);
+ Module::Extern *extern2 = externs[1];
+ EXPECT_STREQ("dyldGlobalLockAcquire()", extern2->name.c_str());
+ EXPECT_EQ((Module::Address)0xaaaa, extern2->address);
+ Module::Extern *extern3 = externs[2];
+ EXPECT_STREQ("foo", extern3->name.c_str());
+ EXPECT_EQ((Module::Address)0xffff, extern3->address);
+}
+#endif // __GNUC__
+
+TEST(StabsToModule, DuplicateFunctionNames) {
+ Module m("name", "os", "arch", "id");
+ StabsToModule h(&m);
+
+ // Compilation unit with one function, mangled name.
+ EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda36ecf7f46cLL,
+ "build-directory"));
+ EXPECT_TRUE(h.StartFunction("funcfoo",
+ 0xf2cfda36ecf7f46dLL));
+ EXPECT_TRUE(h.EndFunction(0));
+ EXPECT_TRUE(h.StartFunction("funcfoo",
+ 0xf2cfda36ecf7f46dLL));
+ EXPECT_TRUE(h.EndFunction(0));
+ EXPECT_TRUE(h.EndCompilationUnit(0));
+
+ h.Finalize();
+
+ // Now check to see what has been added to the Module.
+ Module::File *file = m.FindExistingFile("compilation-unit");
+ ASSERT_TRUE(file != NULL);
+
+ vector<Module::Function *> functions;
+ m.GetFunctions(&functions, functions.end());
+ ASSERT_EQ(1U, functions.size());
+
+ Module::Function *function = functions[0];
+ EXPECT_EQ(0xf2cfda36ecf7f46dLL, function->address);
+ EXPECT_LT(0U, function->size); // should have used dummy size
+ EXPECT_EQ(0U, function->parameter_size);
+ ASSERT_EQ(0U, function->lines.size());
+}
+
+TEST(InferSizes, LineSize) {
+ Module m("name", "os", "arch", "id");
+ StabsToModule h(&m);
+
+ // Feed in a simple compilation unit that defines a function with
+ // one line.
+ EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xb4513962eff94e92LL,
+ "build-directory"));
+ EXPECT_TRUE(h.StartFunction("function", 0xb4513962eff94e92LL));
+ EXPECT_TRUE(h.Line(0xb4513962eff94e92LL, "source-file-name-1", 77396614));
+ EXPECT_TRUE(h.Line(0xb4513963eff94e92LL, "source-file-name-2", 87660088));
+ EXPECT_TRUE(h.EndFunction(0)); // unknown function end address
+ EXPECT_TRUE(h.EndCompilationUnit(0)); // unknown CU end address
+ EXPECT_TRUE(h.StartCompilationUnit("compilation-unit-2", 0xb4523963eff94e92LL,
+ "build-directory-2")); // next boundary
+ EXPECT_TRUE(h.EndCompilationUnit(0));
+ h.Finalize();
+
+ // Now check to see what has been added to the Module.
+ Module::File *file1 = m.FindExistingFile("source-file-name-1");
+ ASSERT_TRUE(file1 != NULL);
+ Module::File *file2 = m.FindExistingFile("source-file-name-2");
+ ASSERT_TRUE(file2 != NULL);
+
+ vector<Module::Function *> functions;
+ m.GetFunctions(&functions, functions.end());
+ ASSERT_EQ((size_t) 1, functions.size());
+
+ Module::Function *function = functions[0];
+ EXPECT_STREQ("function", function->name.c_str());
+ EXPECT_EQ(0xb4513962eff94e92LL, function->address);
+ EXPECT_EQ(0x1000100000000ULL, function->size); // inferred from CU end
+ EXPECT_EQ(0U, function->parameter_size);
+ ASSERT_EQ((size_t) 2, function->lines.size());
+
+ Module::Line *line1 = &function->lines[0];
+ EXPECT_EQ(0xb4513962eff94e92LL, line1->address);
+ EXPECT_EQ(0x100000000ULL, line1->size); // derived from EndFunction
+ EXPECT_TRUE(line1->file == file1);
+ EXPECT_EQ(77396614, line1->number);
+
+ Module::Line *line2 = &function->lines[1];
+ EXPECT_EQ(0xb4513963eff94e92LL, line2->address);
+ EXPECT_EQ(0x1000000000000ULL, line2->size); // derived from EndFunction
+ EXPECT_TRUE(line2->file == file2);
+ EXPECT_EQ(87660088, line2->number);
+}
+
+#ifdef __GNUC__
+// Function name mangling can vary by compiler, so only run mangled-name
+// tests on GCC for simplicity's sake.
+TEST(FunctionNames, Mangled) {
+ Module m("name", "os", "arch", "id");
+ StabsToModule h(&m);
+
+ // Compilation unit with one function, mangled name.
+ EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda63cef7f46cLL,
+ "build-directory"));
+ EXPECT_TRUE(h.StartFunction("_ZNSt6vectorIySaIyEE9push_backERKy",
+ 0xf2cfda63cef7f46dLL));
+ EXPECT_TRUE(h.EndFunction(0));
+ EXPECT_TRUE(h.EndCompilationUnit(0));
+
+ h.Finalize();
+
+ // Now check to see what has been added to the Module.
+ Module::File *file = m.FindExistingFile("compilation-unit");
+ ASSERT_TRUE(file != NULL);
+
+ vector<Module::Function *> functions;
+ m.GetFunctions(&functions, functions.end());
+ ASSERT_EQ(1U, functions.size());
+
+ Module::Function *function = functions[0];
+ // This is GCC-specific, but we shouldn't be seeing STABS data anywhere
+ // but Linux.
+ EXPECT_STREQ("std::vector<unsigned long long, "
+ "std::allocator<unsigned long long> >::"
+ "push_back(unsigned long long const&)",
+ function->name.c_str());
+ EXPECT_EQ(0xf2cfda63cef7f46dLL, function->address);
+ EXPECT_LT(0U, function->size); // should have used dummy size
+ EXPECT_EQ(0U, function->parameter_size);
+ ASSERT_EQ(0U, function->lines.size());
+}
+#endif // __GNUC__
+
+// The GNU toolchain can omit functions that are not used; however,
+// when it does so, it doesn't clean up the debugging information that
+// refers to them. In STABS, this results in compilation units whose
+// SO addresses are zero.
+TEST(Omitted, Function) {
+ Module m("name", "os", "arch", "id");
+ StabsToModule h(&m);
+
+ // The StartCompilationUnit and EndCompilationUnit calls may both have an
+ // address of zero if the compilation unit has had sections removed.
+ EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0, "build-directory"));
+ EXPECT_TRUE(h.StartFunction("function", 0x2a133596));
+ EXPECT_TRUE(h.EndFunction(0));
+ EXPECT_TRUE(h.EndCompilationUnit(0));
+}
+
+// TODO --- if we actually cared about STABS. Even without these we've
+// got full coverage of non-failure source lines in dump_stabs.cc.
+
+// Line size from next line
+// Line size from function end
+// Line size from next function start
+// line size from cu end
+// line size from next cu start
+// fallback size is something plausible
+
+// function size from function end
+// function size from next function start
+// function size from cu end
+// function size from next cu start
+// fallback size is something plausible
+
+// omitting functions outside the compilation unit's address range
+// zero-line, one-line, many-line functions
diff --git a/3rdParty/Breakpad/src/common/stdio_wrapper.h b/3rdParty/Breakpad/src/common/stdio_wrapper.h
new file mode 100644
index 0000000..a3dd50a
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/stdio_wrapper.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H
+#define GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H
+
+#include <stdio.h>
+
+#if defined(_MSC_VER) && MSC_VER < 1900
+#include <basetsd.h>
+
+#define snprintf _snprintf
+typedef SSIZE_T ssize_t;
+#endif
+
+
+#endif // GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H
diff --git a/3rdParty/Breakpad/src/common/string_conversion.cc b/3rdParty/Breakpad/src/common/string_conversion.cc
index 27fb8cd..040d3e8 100644
--- a/3rdParty/Breakpad/src/common/string_conversion.cc
+++ b/3rdParty/Breakpad/src/common/string_conversion.cc
@@ -30,23 +30,23 @@
#include <string.h>
#include "common/convert_UTF.h"
+#include "common/scoped_ptr.h"
#include "common/string_conversion.h"
#include "common/using_std_string.h"
-#include "processor/scoped_ptr.h"
namespace google_breakpad {
using std::vector;
-void UTF8ToUTF16(const char *in, vector<u_int16_t> *out) {
+void UTF8ToUTF16(const char *in, vector<uint16_t> *out) {
size_t source_length = strlen(in);
const UTF8 *source_ptr = reinterpret_cast<const UTF8 *>(in);
const UTF8 *source_end_ptr = source_ptr + source_length;
// Erase the contents and zero fill to the expected size
out->clear();
out->insert(out->begin(), source_length, 0);
- u_int16_t *target_ptr = &(*out)[0];
- u_int16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(u_int16_t);
+ uint16_t *target_ptr = &(*out)[0];
+ uint16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(uint16_t);
ConversionResult result = ConvertUTF8toUTF16(&source_ptr, source_end_ptr,
&target_ptr, target_end_ptr,
strictConversion);
@@ -55,11 +55,11 @@ void UTF8ToUTF16(const char *in, vector<u_int16_t> *out) {
out->resize(result == conversionOK ? target_ptr - &(*out)[0] + 1: 0);
}
-int UTF8ToUTF16Char(const char *in, int in_length, u_int16_t out[2]) {
+int UTF8ToUTF16Char(const char *in, int in_length, uint16_t out[2]) {
const UTF8 *source_ptr = reinterpret_cast<const UTF8 *>(in);
- const UTF8 *source_end_ptr = source_ptr + sizeof(char);
- u_int16_t *target_ptr = out;
- u_int16_t *target_end_ptr = target_ptr + 2 * sizeof(u_int16_t);
+ const UTF8 *source_end_ptr = source_ptr + 1;
+ uint16_t *target_ptr = out;
+ uint16_t *target_end_ptr = target_ptr + 2;
out[0] = out[1] = 0;
// Process one character at a time
@@ -82,15 +82,15 @@ int UTF8ToUTF16Char(const char *in, int in_length, u_int16_t out[2]) {
return 0;
}
-void UTF32ToUTF16(const wchar_t *in, vector<u_int16_t> *out) {
+void UTF32ToUTF16(const wchar_t *in, vector<uint16_t> *out) {
size_t source_length = wcslen(in);
const UTF32 *source_ptr = reinterpret_cast<const UTF32 *>(in);
const UTF32 *source_end_ptr = source_ptr + source_length;
// Erase the contents and zero fill to the expected size
out->clear();
out->insert(out->begin(), source_length, 0);
- u_int16_t *target_ptr = &(*out)[0];
- u_int16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(u_int16_t);
+ uint16_t *target_ptr = &(*out)[0];
+ uint16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(uint16_t);
ConversionResult result = ConvertUTF32toUTF16(&source_ptr, source_end_ptr,
&target_ptr, target_end_ptr,
strictConversion);
@@ -99,11 +99,11 @@ void UTF32ToUTF16(const wchar_t *in, vector<u_int16_t> *out) {
out->resize(result == conversionOK ? target_ptr - &(*out)[0] + 1: 0);
}
-void UTF32ToUTF16Char(wchar_t in, u_int16_t out[2]) {
+void UTF32ToUTF16Char(wchar_t in, uint16_t out[2]) {
const UTF32 *source_ptr = reinterpret_cast<const UTF32 *>(&in);
const UTF32 *source_end_ptr = source_ptr + 1;
- u_int16_t *target_ptr = out;
- u_int16_t *target_end_ptr = target_ptr + 2 * sizeof(u_int16_t);
+ uint16_t *target_ptr = out;
+ uint16_t *target_end_ptr = target_ptr + 2;
out[0] = out[1] = 0;
ConversionResult result = ConvertUTF32toUTF16(&source_ptr, source_end_ptr,
&target_ptr, target_end_ptr,
@@ -114,20 +114,20 @@ void UTF32ToUTF16Char(wchar_t in, u_int16_t out[2]) {
}
}
-static inline u_int16_t Swap(u_int16_t value) {
- return (value >> 8) | (value << 8);
+static inline uint16_t Swap(uint16_t value) {
+ return (value >> 8) | static_cast<uint16_t>(value << 8);
}
-string UTF16ToUTF8(const vector<u_int16_t> &in, bool swap) {
+string UTF16ToUTF8(const vector<uint16_t> &in, bool swap) {
const UTF16 *source_ptr = &in[0];
- scoped_ptr<u_int16_t> source_buffer;
+ scoped_array<uint16_t> source_buffer;
// If we're to swap, we need to make a local copy and swap each byte pair
if (swap) {
int idx = 0;
- source_buffer.reset(new u_int16_t[in.size()]);
+ source_buffer.reset(new uint16_t[in.size()]);
UTF16 *source_buffer_ptr = source_buffer.get();
- for (vector<u_int16_t>::const_iterator it = in.begin();
+ for (vector<uint16_t>::const_iterator it = in.begin();
it != in.end(); ++it, ++idx)
source_buffer_ptr[idx] = Swap(*it);
@@ -140,7 +140,7 @@ string UTF16ToUTF8(const vector<u_int16_t> &in, bool swap) {
scoped_array<UTF8> target_buffer(new UTF8[target_capacity]);
UTF8 *target_ptr = target_buffer.get();
UTF8 *target_end_ptr = target_ptr + target_capacity;
- ConversionResult result = ConvertUTF16toUTF8(&source_ptr, source_end_ptr,
+ ConversionResult result = ConvertUTF16toUTF8(&source_ptr, source_end_ptr,
&target_ptr, target_end_ptr,
strictConversion);
diff --git a/3rdParty/Breakpad/src/common/string_conversion.h b/3rdParty/Breakpad/src/common/string_conversion.h
index eeed4d2..b9ba96a 100644
--- a/3rdParty/Breakpad/src/common/string_conversion.h
+++ b/3rdParty/Breakpad/src/common/string_conversion.h
@@ -44,24 +44,24 @@ using std::vector;
// Convert |in| to UTF-16 into |out|. Use platform byte ordering. If the
// conversion failed, |out| will be zero length.
-void UTF8ToUTF16(const char *in, vector<u_int16_t> *out);
+void UTF8ToUTF16(const char *in, vector<uint16_t> *out);
// Convert at least one character (up to a maximum of |in_length|) from |in|
// to UTF-16 into |out|. Return the number of characters consumed from |in|.
// Any unused characters in |out| will be initialized to 0. No memory will
// be allocated by this routine.
-int UTF8ToUTF16Char(const char *in, int in_length, u_int16_t out[2]);
+int UTF8ToUTF16Char(const char *in, int in_length, uint16_t out[2]);
// Convert |in| to UTF-16 into |out|. Use platform byte ordering. If the
// conversion failed, |out| will be zero length.
-void UTF32ToUTF16(const wchar_t *in, vector<u_int16_t> *out);
+void UTF32ToUTF16(const wchar_t *in, vector<uint16_t> *out);
// Convert |in| to UTF-16 into |out|. Any unused characters in |out| will be
// initialized to 0. No memory will be allocated by this routine.
-void UTF32ToUTF16Char(wchar_t in, u_int16_t out[2]);
+void UTF32ToUTF16Char(wchar_t in, uint16_t out[2]);
// Convert |in| to UTF-8. If |swap| is true, swap bytes before converting.
-string UTF16ToUTF8(const vector<u_int16_t> &in, bool swap);
+string UTF16ToUTF8(const vector<uint16_t> &in, bool swap);
} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/symbol_data.h b/3rdParty/Breakpad/src/common/symbol_data.h
new file mode 100644
index 0000000..2cf15a8
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/symbol_data.h
@@ -0,0 +1,42 @@
+// -*- mode: c++ -*-
+
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef COMMON_SYMBOL_DATA_H_
+#define COMMON_SYMBOL_DATA_H_
+
+// Control what data is used from the symbol file.
+enum SymbolData {
+ ALL_SYMBOL_DATA,
+ NO_CFI,
+ ONLY_CFI
+};
+
+#endif // COMMON_SYMBOL_DATA_H_
diff --git a/3rdParty/Breakpad/src/common/test_assembler.cc b/3rdParty/Breakpad/src/common/test_assembler.cc
new file mode 100644
index 0000000..1e783b4
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/test_assembler.cc
@@ -0,0 +1,359 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// test_assembler.cc: Implementation of google_breakpad::TestAssembler.
+// See test_assembler.h for details.
+
+#include "common/test_assembler.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <iterator>
+
+namespace google_breakpad {
+namespace test_assembler {
+
+using std::back_insert_iterator;
+
+Label::Label() : value_(new Binding()) { }
+Label::Label(uint64_t value) : value_(new Binding(value)) { }
+Label::Label(const Label &label) {
+ value_ = label.value_;
+ value_->Acquire();
+}
+Label::~Label() {
+ if (value_->Release()) delete value_;
+}
+
+Label &Label::operator=(uint64_t value) {
+ value_->Set(NULL, value);
+ return *this;
+}
+
+Label &Label::operator=(const Label &label) {
+ value_->Set(label.value_, 0);
+ return *this;
+}
+
+Label Label::operator+(uint64_t addend) const {
+ Label l;
+ l.value_->Set(this->value_, addend);
+ return l;
+}
+
+Label Label::operator-(uint64_t subtrahend) const {
+ Label l;
+ l.value_->Set(this->value_, -subtrahend);
+ return l;
+}
+
+// When NDEBUG is #defined, assert doesn't evaluate its argument. This
+// means you can't simply use assert to check the return value of a
+// function with necessary side effects.
+//
+// ALWAYS_EVALUATE_AND_ASSERT(x) evaluates x regardless of whether
+// NDEBUG is #defined; when NDEBUG is not #defined, it further asserts
+// that x is true.
+#ifdef NDEBUG
+#define ALWAYS_EVALUATE_AND_ASSERT(x) x
+#else
+#define ALWAYS_EVALUATE_AND_ASSERT(x) assert(x)
+#endif
+
+uint64_t Label::operator-(const Label &label) const {
+ uint64_t offset;
+ ALWAYS_EVALUATE_AND_ASSERT(IsKnownOffsetFrom(label, &offset));
+ return offset;
+}
+
+uint64_t Label::Value() const {
+ uint64_t v = 0;
+ ALWAYS_EVALUATE_AND_ASSERT(IsKnownConstant(&v));
+ return v;
+};
+
+bool Label::IsKnownConstant(uint64_t *value_p) const {
+ Binding *base;
+ uint64_t addend;
+ value_->Get(&base, &addend);
+ if (base != NULL) return false;
+ if (value_p) *value_p = addend;
+ return true;
+}
+
+bool Label::IsKnownOffsetFrom(const Label &label, uint64_t *offset_p) const
+{
+ Binding *label_base, *this_base;
+ uint64_t label_addend, this_addend;
+ label.value_->Get(&label_base, &label_addend);
+ value_->Get(&this_base, &this_addend);
+ // If this and label are related, Get will find their final
+ // common ancestor, regardless of how indirect the relation is. This
+ // comparison also handles the constant vs. constant case.
+ if (this_base != label_base) return false;
+ if (offset_p) *offset_p = this_addend - label_addend;
+ return true;
+}
+
+Label::Binding::Binding() : base_(this), addend_(), reference_count_(1) { }
+
+Label::Binding::Binding(uint64_t addend)
+ : base_(NULL), addend_(addend), reference_count_(1) { }
+
+Label::Binding::~Binding() {
+ assert(reference_count_ == 0);
+ if (base_ && base_ != this && base_->Release())
+ delete base_;
+}
+
+void Label::Binding::Set(Binding *binding, uint64_t addend) {
+ if (!base_ && !binding) {
+ // We're equating two constants. This could be okay.
+ assert(addend_ == addend);
+ } else if (!base_) {
+ // We are a known constant, but BINDING may not be, so turn the
+ // tables and try to set BINDING's value instead.
+ binding->Set(NULL, addend_ - addend);
+ } else {
+ if (binding) {
+ // Find binding's final value. Since the final value is always either
+ // completely unconstrained or a constant, never a reference to
+ // another variable (otherwise, it wouldn't be final), this
+ // guarantees we won't create cycles here, even for code like this:
+ // l = m, m = n, n = l;
+ uint64_t binding_addend;
+ binding->Get(&binding, &binding_addend);
+ addend += binding_addend;
+ }
+
+ // It seems likely that setting a binding to itself is a bug
+ // (although I can imagine this might turn out to be helpful to
+ // permit).
+ assert(binding != this);
+
+ if (base_ != this) {
+ // Set the other bindings on our chain as well. Note that this
+ // is sufficient even though binding relationships form trees:
+ // All binding operations traverse their chains to the end, and
+ // all bindings related to us share some tail of our chain, so
+ // they will see the changes we make here.
+ base_->Set(binding, addend - addend_);
+ // We're not going to use base_ any more.
+ if (base_->Release()) delete base_;
+ }
+
+ // Adopt BINDING as our base. Note that it should be correct to
+ // acquire here, after the release above, even though the usual
+ // reference-counting rules call for acquiring first, and then
+ // releasing: the self-reference assertion above should have
+ // complained if BINDING were 'this' or anywhere along our chain,
+ // so we didn't release BINDING.
+ if (binding) binding->Acquire();
+ base_ = binding;
+ addend_ = addend;
+ }
+}
+
+void Label::Binding::Get(Binding **base, uint64_t *addend) {
+ if (base_ && base_ != this) {
+ // Recurse to find the end of our reference chain (the root of our
+ // tree), and then rewrite every binding along the chain to refer
+ // to it directly, adjusting addends appropriately. (This is why
+ // this member function isn't this-const.)
+ Binding *final_base;
+ uint64_t final_addend;
+ base_->Get(&final_base, &final_addend);
+ if (final_base) final_base->Acquire();
+ if (base_->Release()) delete base_;
+ base_ = final_base;
+ addend_ += final_addend;
+ }
+ *base = base_;
+ *addend = addend_;
+}
+
+template<typename Inserter>
+static inline void InsertEndian(test_assembler::Endianness endianness,
+ size_t size, uint64_t number, Inserter dest) {
+ assert(size > 0);
+ if (endianness == kLittleEndian) {
+ for (size_t i = 0; i < size; i++) {
+ *dest++ = (char) (number & 0xff);
+ number >>= 8;
+ }
+ } else {
+ assert(endianness == kBigEndian);
+ // The loop condition is odd, but it's correct for size_t.
+ for (size_t i = size - 1; i < size; i--)
+ *dest++ = (char) ((number >> (i * 8)) & 0xff);
+ }
+}
+
+Section &Section::Append(Endianness endianness, size_t size, uint64_t number) {
+ InsertEndian(endianness, size, number,
+ back_insert_iterator<string>(contents_));
+ return *this;
+}
+
+Section &Section::Append(Endianness endianness, size_t size,
+ const Label &label) {
+ // If this label's value is known, there's no reason to waste an
+ // entry in references_ on it.
+ uint64_t value;
+ if (label.IsKnownConstant(&value))
+ return Append(endianness, size, value);
+
+ // This will get caught when the references are resolved, but it's
+ // nicer to find out earlier.
+ assert(endianness != kUnsetEndian);
+
+ references_.push_back(Reference(contents_.size(), endianness, size, label));
+ contents_.append(size, 0);
+ return *this;
+}
+
+#define ENDIANNESS_L kLittleEndian
+#define ENDIANNESS_B kBigEndian
+#define ENDIANNESS(e) ENDIANNESS_ ## e
+
+#define DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \
+ Section &Section::e ## bits(uint ## bits ## _t v) { \
+ InsertEndian(ENDIANNESS(e), bits / 8, v, \
+ back_insert_iterator<string>(contents_)); \
+ return *this; \
+ }
+
+#define DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) \
+ Section &Section::e ## bits(const Label &v) { \
+ return Append(ENDIANNESS(e), bits / 8, v); \
+ }
+
+// Define L16, B32, and friends.
+#define DEFINE_SHORT_APPEND_ENDIAN(e, bits) \
+ DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \
+ DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits)
+
+DEFINE_SHORT_APPEND_LABEL_ENDIAN(L, 8);
+DEFINE_SHORT_APPEND_LABEL_ENDIAN(B, 8);
+DEFINE_SHORT_APPEND_ENDIAN(L, 16);
+DEFINE_SHORT_APPEND_ENDIAN(L, 32);
+DEFINE_SHORT_APPEND_ENDIAN(L, 64);
+DEFINE_SHORT_APPEND_ENDIAN(B, 16);
+DEFINE_SHORT_APPEND_ENDIAN(B, 32);
+DEFINE_SHORT_APPEND_ENDIAN(B, 64);
+
+#define DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \
+ Section &Section::D ## bits(uint ## bits ## _t v) { \
+ InsertEndian(endianness_, bits / 8, v, \
+ back_insert_iterator<string>(contents_)); \
+ return *this; \
+ }
+#define DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) \
+ Section &Section::D ## bits(const Label &v) { \
+ return Append(endianness_, bits / 8, v); \
+ }
+#define DEFINE_SHORT_APPEND_DEFAULT(bits) \
+ DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \
+ DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits)
+
+DEFINE_SHORT_APPEND_LABEL_DEFAULT(8)
+DEFINE_SHORT_APPEND_DEFAULT(16);
+DEFINE_SHORT_APPEND_DEFAULT(32);
+DEFINE_SHORT_APPEND_DEFAULT(64);
+
+Section &Section::Append(const Section &section) {
+ size_t base = contents_.size();
+ contents_.append(section.contents_);
+ for (vector<Reference>::const_iterator it = section.references_.begin();
+ it != section.references_.end(); it++)
+ references_.push_back(Reference(base + it->offset, it->endianness,
+ it->size, it->label));
+ return *this;
+}
+
+Section &Section::LEB128(long long value) {
+ while (value < -0x40 || 0x3f < value) {
+ contents_ += (value & 0x7f) | 0x80;
+ if (value < 0)
+ value = (value >> 7) | ~(((unsigned long long) -1) >> 7);
+ else
+ value = (value >> 7);
+ }
+ contents_ += value & 0x7f;
+ return *this;
+}
+
+Section &Section::ULEB128(uint64_t value) {
+ while (value > 0x7f) {
+ contents_ += (value & 0x7f) | 0x80;
+ value = (value >> 7);
+ }
+ contents_ += value;
+ return *this;
+}
+
+Section &Section::Align(size_t alignment, uint8_t pad_byte) {
+ // ALIGNMENT must be a power of two.
+ assert(((alignment - 1) & alignment) == 0);
+ size_t new_size = (contents_.size() + alignment - 1) & ~(alignment - 1);
+ contents_.append(new_size - contents_.size(), pad_byte);
+ assert((contents_.size() & (alignment - 1)) == 0);
+ return *this;
+}
+
+void Section::Clear() {
+ contents_.clear();
+ references_.clear();
+}
+
+bool Section::GetContents(string *contents) {
+ // For each label reference, find the label's value, and patch it into
+ // the section's contents.
+ for (size_t i = 0; i < references_.size(); i++) {
+ Reference &r = references_[i];
+ uint64_t value;
+ if (!r.label.IsKnownConstant(&value)) {
+ fprintf(stderr, "Undefined label #%zu at offset 0x%zx\n", i, r.offset);
+ return false;
+ }
+ assert(r.offset < contents_.size());
+ assert(contents_.size() - r.offset >= r.size);
+ InsertEndian(r.endianness, r.size, value, contents_.begin() + r.offset);
+ }
+ contents->clear();
+ std::swap(contents_, *contents);
+ references_.clear();
+ return true;
+}
+
+} // namespace test_assembler
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/test_assembler.h b/3rdParty/Breakpad/src/common/test_assembler.h
new file mode 100644
index 0000000..373dbeb
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/test_assembler.h
@@ -0,0 +1,484 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// test-assembler.h: interface to class for building complex binary streams.
+
+// To test the Breakpad symbol dumper and processor thoroughly, for
+// all combinations of host system and minidump processor
+// architecture, we need to be able to easily generate complex test
+// data like debugging information and minidump files.
+//
+// For example, if we want our unit tests to provide full code
+// coverage for stack walking, it may be difficult to persuade the
+// compiler to generate every possible sort of stack walking
+// information that we want to support; there are probably DWARF CFI
+// opcodes that GCC never emits. Similarly, if we want to test our
+// error handling, we will need to generate damaged minidumps or
+// debugging information that (we hope) the client or compiler will
+// never produce on its own.
+//
+// google_breakpad::TestAssembler provides a predictable and
+// (relatively) simple way to generate complex formatted data streams
+// like minidumps and CFI. Furthermore, because TestAssembler is
+// portable, developers without access to (say) Visual Studio or a
+// SPARC assembler can still work on test data for those targets.
+
+#ifndef PROCESSOR_TEST_ASSEMBLER_H_
+#define PROCESSOR_TEST_ASSEMBLER_H_
+
+#include <list>
+#include <vector>
+#include <string>
+
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+
+namespace google_breakpad {
+
+using std::list;
+using std::vector;
+
+namespace test_assembler {
+
+// A Label represents a value not yet known that we need to store in a
+// section. As long as all the labels a section refers to are defined
+// by the time we retrieve its contents as bytes, we can use undefined
+// labels freely in that section's construction.
+//
+// A label can be in one of three states:
+// - undefined,
+// - defined as the sum of some other label and a constant, or
+// - a constant.
+//
+// A label's value never changes, but it can accumulate constraints.
+// Adding labels and integers is permitted, and yields a label.
+// Subtracting a constant from a label is permitted, and also yields a
+// label. Subtracting two labels that have some relationship to each
+// other is permitted, and yields a constant.
+//
+// For example:
+//
+// Label a; // a's value is undefined
+// Label b; // b's value is undefined
+// {
+// Label c = a + 4; // okay, even though a's value is unknown
+// b = c + 4; // also okay; b is now a+8
+// }
+// Label d = b - 2; // okay; d == a+6, even though c is gone
+// d.Value(); // error: d's value is not yet known
+// d - a; // is 6, even though their values are not known
+// a = 12; // now b == 20, and d == 18
+// d.Value(); // 18: no longer an error
+// b.Value(); // 20
+// d = 10; // error: d is already defined.
+//
+// Label objects' lifetimes are unconstrained: notice that, in the
+// above example, even though a and b are only related through c, and
+// c goes out of scope, the assignment to a sets b's value as well. In
+// particular, it's not necessary to ensure that a Label lives beyond
+// Sections that refer to it.
+class Label {
+ public:
+ Label(); // An undefined label.
+ Label(uint64_t value); // A label with a fixed value
+ Label(const Label &value); // A label equal to another.
+ ~Label();
+
+ // Return this label's value; it must be known.
+ //
+ // Providing this as a cast operator is nifty, but the conversions
+ // happen in unexpected places. In particular, ISO C++ says that
+ // Label + size_t becomes ambigious, because it can't decide whether
+ // to convert the Label to a uint64_t and then to a size_t, or use
+ // the overloaded operator that returns a new label, even though the
+ // former could fail if the label is not yet defined and the latter won't.
+ uint64_t Value() const;
+
+ Label &operator=(uint64_t value);
+ Label &operator=(const Label &value);
+ Label operator+(uint64_t addend) const;
+ Label operator-(uint64_t subtrahend) const;
+ uint64_t operator-(const Label &subtrahend) const;
+
+ // We could also provide == and != that work on undefined, but
+ // related, labels.
+
+ // Return true if this label's value is known. If VALUE_P is given,
+ // set *VALUE_P to the known value if returning true.
+ bool IsKnownConstant(uint64_t *value_p = NULL) const;
+
+ // Return true if the offset from LABEL to this label is known. If
+ // OFFSET_P is given, set *OFFSET_P to the offset when returning true.
+ //
+ // You can think of l.KnownOffsetFrom(m, &d) as being like 'd = l-m',
+ // except that it also returns a value indicating whether the
+ // subtraction is possible given what we currently know of l and m.
+ // It can be possible even if we don't know l and m's values. For
+ // example:
+ //
+ // Label l, m;
+ // m = l + 10;
+ // l.IsKnownConstant(); // false
+ // m.IsKnownConstant(); // false
+ // uint64_t d;
+ // l.IsKnownOffsetFrom(m, &d); // true, and sets d to -10.
+ // l-m // -10
+ // m-l // 10
+ // m.Value() // error: m's value is not known
+ bool IsKnownOffsetFrom(const Label &label, uint64_t *offset_p = NULL) const;
+
+ private:
+ // A label's value, or if that is not yet known, how the value is
+ // related to other labels' values. A binding may be:
+ // - a known constant,
+ // - constrained to be equal to some other binding plus a constant, or
+ // - unconstrained, and free to take on any value.
+ //
+ // Many labels may point to a single binding, and each binding may
+ // refer to another, so bindings and labels form trees whose leaves
+ // are labels, whose interior nodes (and roots) are bindings, and
+ // where links point from children to parents. Bindings are
+ // reference counted, allowing labels to be lightweight, copyable,
+ // assignable, placed in containers, and so on.
+ class Binding {
+ public:
+ Binding();
+ Binding(uint64_t addend);
+ ~Binding();
+
+ // Increment our reference count.
+ void Acquire() { reference_count_++; };
+ // Decrement our reference count, and return true if it is zero.
+ bool Release() { return --reference_count_ == 0; }
+
+ // Set this binding to be equal to BINDING + ADDEND. If BINDING is
+ // NULL, then set this binding to the known constant ADDEND.
+ // Update every binding on this binding's chain to point directly
+ // to BINDING, or to be a constant, with addends adjusted
+ // appropriately.
+ void Set(Binding *binding, uint64_t value);
+
+ // Return what we know about the value of this binding.
+ // - If this binding's value is a known constant, set BASE to
+ // NULL, and set ADDEND to its value.
+ // - If this binding is not a known constant but related to other
+ // bindings, set BASE to the binding at the end of the relation
+ // chain (which will always be unconstrained), and set ADDEND to the
+ // value to add to that binding's value to get this binding's
+ // value.
+ // - If this binding is unconstrained, set BASE to this, and leave
+ // ADDEND unchanged.
+ void Get(Binding **base, uint64_t *addend);
+
+ private:
+ // There are three cases:
+ //
+ // - A binding representing a known constant value has base_ NULL,
+ // and addend_ equal to the value.
+ //
+ // - A binding representing a completely unconstrained value has
+ // base_ pointing to this; addend_ is unused.
+ //
+ // - A binding whose value is related to some other binding's
+ // value has base_ pointing to that other binding, and addend_
+ // set to the amount to add to that binding's value to get this
+ // binding's value. We only represent relationships of the form
+ // x = y+c.
+ //
+ // Thus, the bind_ links form a chain terminating in either a
+ // known constant value or a completely unconstrained value. Most
+ // operations on bindings do path compression: they change every
+ // binding on the chain to point directly to the final value,
+ // adjusting addends as appropriate.
+ Binding *base_;
+ uint64_t addend_;
+
+ // The number of Labels and Bindings pointing to this binding.
+ // (When a binding points to itself, indicating a completely
+ // unconstrained binding, that doesn't count as a reference.)
+ int reference_count_;
+ };
+
+ // This label's value.
+ Binding *value_;
+};
+
+inline Label operator+(uint64_t a, const Label &l) { return l + a; }
+// Note that int-Label isn't defined, as negating a Label is not an
+// operation we support.
+
+// Conventions for representing larger numbers as sequences of bytes.
+enum Endianness {
+ kBigEndian, // Big-endian: the most significant byte comes first.
+ kLittleEndian, // Little-endian: the least significant byte comes first.
+ kUnsetEndian, // used internally
+};
+
+// A section is a sequence of bytes, constructed by appending bytes
+// to the end. Sections have a convenient and flexible set of member
+// functions for appending data in various formats: big-endian and
+// little-endian signed and unsigned values of different sizes;
+// LEB128 and ULEB128 values (see below), and raw blocks of bytes.
+//
+// If you need to append a value to a section that is not convenient
+// to compute immediately, you can create a label, append the
+// label's value to the section, and then set the label's value
+// later, when it's convenient to do so. Once a label's value is
+// known, the section class takes care of updating all previously
+// appended references to it.
+//
+// Once all the labels to which a section refers have had their
+// values determined, you can get a copy of the section's contents
+// as a string.
+//
+// Note that there is no specified "start of section" label. This is
+// because there are typically several different meanings for "the
+// start of a section": the offset of the section within an object
+// file, the address in memory at which the section's content appear,
+// and so on. It's up to the code that uses the Section class to
+// keep track of these explicitly, as they depend on the application.
+class Section {
+ public:
+ Section(Endianness endianness = kUnsetEndian)
+ : endianness_(endianness) { };
+
+ // A base class destructor should be either public and virtual,
+ // or protected and nonvirtual.
+ virtual ~Section() { };
+
+ // Set the default endianness of this section to ENDIANNESS. This
+ // sets the behavior of the D<N> appending functions. If the
+ // assembler's default endianness was set, this is the
+ void set_endianness(Endianness endianness) {
+ endianness_ = endianness;
+ }
+
+ // Return the default endianness of this section.
+ Endianness endianness() const { return endianness_; }
+
+ // Append the SIZE bytes at DATA or the contents of STRING to the
+ // end of this section. Return a reference to this section.
+ Section &Append(const uint8_t *data, size_t size) {
+ contents_.append(reinterpret_cast<const char *>(data), size);
+ return *this;
+ };
+ Section &Append(const string &data) {
+ contents_.append(data);
+ return *this;
+ };
+
+ // Append SIZE copies of BYTE to the end of this section. Return a
+ // reference to this section.
+ Section &Append(size_t size, uint8_t byte) {
+ contents_.append(size, (char) byte);
+ return *this;
+ }
+
+ // Append NUMBER to this section. ENDIANNESS is the endianness to
+ // use to write the number. SIZE is the length of the number in
+ // bytes. Return a reference to this section.
+ Section &Append(Endianness endianness, size_t size, uint64_t number);
+ Section &Append(Endianness endianness, size_t size, const Label &label);
+
+ // Append SECTION to the end of this section. The labels SECTION
+ // refers to need not be defined yet.
+ //
+ // Note that this has no effect on any Labels' values, or on
+ // SECTION. If placing SECTION within 'this' provides new
+ // constraints on existing labels' values, then it's up to the
+ // caller to fiddle with those labels as needed.
+ Section &Append(const Section &section);
+
+ // Append the contents of DATA as a series of bytes terminated by
+ // a NULL character.
+ Section &AppendCString(const string &data) {
+ Append(data);
+ contents_ += '\0';
+ return *this;
+ }
+
+ // Append at most SIZE bytes from DATA; if DATA is less than SIZE bytes
+ // long, pad with '\0' characters.
+ Section &AppendCString(const string &data, size_t size) {
+ contents_.append(data, 0, size);
+ if (data.size() < size)
+ Append(size - data.size(), 0);
+ return *this;
+ }
+
+ // Append VALUE or LABEL to this section, with the given bit width and
+ // endianness. Return a reference to this section.
+ //
+ // The names of these functions have the form <ENDIANNESS><BITWIDTH>:
+ // <ENDIANNESS> is either 'L' (little-endian, least significant byte first),
+ // 'B' (big-endian, most significant byte first), or
+ // 'D' (default, the section's default endianness)
+ // <BITWIDTH> is 8, 16, 32, or 64.
+ //
+ // Since endianness doesn't matter for a single byte, all the
+ // <BITWIDTH>=8 functions are equivalent.
+ //
+ // These can be used to write both signed and unsigned values, as
+ // the compiler will properly sign-extend a signed value before
+ // passing it to the function, at which point the function's
+ // behavior is the same either way.
+ Section &L8(uint8_t value) { contents_ += value; return *this; }
+ Section &B8(uint8_t value) { contents_ += value; return *this; }
+ Section &D8(uint8_t value) { contents_ += value; return *this; }
+ Section &L16(uint16_t), &L32(uint32_t), &L64(uint64_t),
+ &B16(uint16_t), &B32(uint32_t), &B64(uint64_t),
+ &D16(uint16_t), &D32(uint32_t), &D64(uint64_t);
+ Section &L8(const Label &label), &L16(const Label &label),
+ &L32(const Label &label), &L64(const Label &label),
+ &B8(const Label &label), &B16(const Label &label),
+ &B32(const Label &label), &B64(const Label &label),
+ &D8(const Label &label), &D16(const Label &label),
+ &D32(const Label &label), &D64(const Label &label);
+
+ // Append VALUE in a signed LEB128 (Little-Endian Base 128) form.
+ //
+ // The signed LEB128 representation of an integer N is a variable
+ // number of bytes:
+ //
+ // - If N is between -0x40 and 0x3f, then its signed LEB128
+ // representation is a single byte whose value is N.
+ //
+ // - Otherwise, its signed LEB128 representation is (N & 0x7f) |
+ // 0x80, followed by the signed LEB128 representation of N / 128,
+ // rounded towards negative infinity.
+ //
+ // In other words, we break VALUE into groups of seven bits, put
+ // them in little-endian order, and then write them as eight-bit
+ // bytes with the high bit on all but the last.
+ //
+ // Note that VALUE cannot be a Label (we would have to implement
+ // relaxation).
+ Section &LEB128(long long value);
+
+ // Append VALUE in unsigned LEB128 (Little-Endian Base 128) form.
+ //
+ // The unsigned LEB128 representation of an integer N is a variable
+ // number of bytes:
+ //
+ // - If N is between 0 and 0x7f, then its unsigned LEB128
+ // representation is a single byte whose value is N.
+ //
+ // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) |
+ // 0x80, followed by the unsigned LEB128 representation of N /
+ // 128, rounded towards negative infinity.
+ //
+ // Note that VALUE cannot be a Label (we would have to implement
+ // relaxation).
+ Section &ULEB128(uint64_t value);
+
+ // Jump to the next location aligned on an ALIGNMENT-byte boundary,
+ // relative to the start of the section. Fill the gap with PAD_BYTE.
+ // ALIGNMENT must be a power of two. Return a reference to this
+ // section.
+ Section &Align(size_t alignment, uint8_t pad_byte = 0);
+
+ // Clear the contents of this section.
+ void Clear();
+
+ // Return the current size of the section.
+ size_t Size() const { return contents_.size(); }
+
+ // Return a label representing the start of the section.
+ //
+ // It is up to the user whether this label represents the section's
+ // position in an object file, the section's address in memory, or
+ // what have you; some applications may need both, in which case
+ // this simple-minded interface won't be enough. This class only
+ // provides a single start label, for use with the Here and Mark
+ // member functions.
+ //
+ // Ideally, we'd provide this in a subclass that actually knows more
+ // about the application at hand and can provide an appropriate
+ // collection of start labels. But then the appending member
+ // functions like Append and D32 would return a reference to the
+ // base class, not the derived class, and the chaining won't work.
+ // Since the only value here is in pretty notation, that's a fatal
+ // flaw.
+ Label start() const { return start_; }
+
+ // Return a label representing the point at which the next Appended
+ // item will appear in the section, relative to start().
+ Label Here() const { return start_ + Size(); }
+
+ // Set *LABEL to Here, and return a reference to this section.
+ Section &Mark(Label *label) { *label = Here(); return *this; }
+
+ // If there are no undefined label references left in this
+ // section, set CONTENTS to the contents of this section, as a
+ // string, and clear this section. Return true on success, or false
+ // if there were still undefined labels.
+ bool GetContents(string *contents);
+
+ private:
+ // Used internally. A reference to a label's value.
+ struct Reference {
+ Reference(size_t set_offset, Endianness set_endianness, size_t set_size,
+ const Label &set_label)
+ : offset(set_offset), endianness(set_endianness), size(set_size),
+ label(set_label) { }
+
+ // The offset of the reference within the section.
+ size_t offset;
+
+ // The endianness of the reference.
+ Endianness endianness;
+
+ // The size of the reference.
+ size_t size;
+
+ // The label to which this is a reference.
+ Label label;
+ };
+
+ // The default endianness of this section.
+ Endianness endianness_;
+
+ // The contents of the section.
+ string contents_;
+
+ // References to labels within those contents.
+ vector<Reference> references_;
+
+ // A label referring to the beginning of the section.
+ Label start_;
+};
+
+} // namespace test_assembler
+} // namespace google_breakpad
+
+#endif // PROCESSOR_TEST_ASSEMBLER_H_
diff --git a/3rdParty/Breakpad/src/common/test_assembler_unittest.cc b/3rdParty/Breakpad/src/common/test_assembler_unittest.cc
new file mode 100644
index 0000000..94b5a5c
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/test_assembler_unittest.cc
@@ -0,0 +1,1662 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// test_assembler_unittest.cc: Unit tests for google_breakpad::TestAssembler.
+
+#include <string>
+#include <string.h>
+
+#include "breakpad_googletest_includes.h"
+#include "common/test_assembler.h"
+#include "common/using_std_string.h"
+
+using google_breakpad::test_assembler::Label;
+using google_breakpad::test_assembler::Section;
+using google_breakpad::test_assembler::kBigEndian;
+using google_breakpad::test_assembler::kLittleEndian;
+using testing::Test;
+
+TEST(ConstructLabel, Simple) {
+ Label l;
+}
+
+TEST(ConstructLabel, Undefined) {
+ Label l;
+ EXPECT_FALSE(l.IsKnownConstant());
+}
+
+TEST(ConstructLabelDeathTest, Undefined) {
+ Label l;
+ ASSERT_DEATH(l.Value(), "IsKnownConstant\\(&v\\)");
+}
+
+TEST(ConstructLabel, Constant) {
+ Label l(0x060b9f974eaf301eULL);
+ uint64_t v;
+ EXPECT_TRUE(l.IsKnownConstant(&v));
+ EXPECT_EQ(v, 0x060b9f974eaf301eULL);
+ EXPECT_EQ(l.Value(), 0x060b9f974eaf301eULL);
+}
+
+TEST(ConstructLabel, Copy) {
+ Label l;
+ Label m(l);
+ uint64_t v;
+ EXPECT_TRUE(l.IsKnownOffsetFrom(m, &v));
+ EXPECT_EQ(0U, v);
+}
+
+// The left-hand-side of a label assignment can be either
+// unconstrained, related, or known. The right-hand-side can be any of
+// those, or an integer.
+TEST(Assignment, UnconstrainedToUnconstrained) {
+ Label l, m;
+ l = m;
+ EXPECT_EQ(0U, l-m);
+ EXPECT_TRUE(l.IsKnownOffsetFrom(m));
+ uint64_t d;
+ EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d));
+ EXPECT_EQ(0U, d);
+ EXPECT_FALSE(l.IsKnownConstant());
+}
+
+TEST(Assignment, UnconstrainedToRelated) {
+ Label l, m, n;
+ l = n;
+ l = m;
+ EXPECT_EQ(0U, l-m);
+ EXPECT_TRUE(l.IsKnownOffsetFrom(m));
+ uint64_t d;
+ EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d));
+ EXPECT_EQ(0U, d);
+ EXPECT_FALSE(l.IsKnownConstant());
+}
+
+TEST(Assignment, UnconstrainedToKnown) {
+ Label l, m;
+ l = 0x8fd16e55b20a39c1ULL;
+ l = m;
+ EXPECT_EQ(0U, l-m);
+ EXPECT_TRUE(l.IsKnownOffsetFrom(m));
+ uint64_t d;
+ EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d));
+ EXPECT_EQ(0U, d);
+ EXPECT_TRUE(m.IsKnownConstant());
+ EXPECT_EQ(0x8fd16e55b20a39c1ULL, m.Value());
+}
+
+TEST(Assignment, RelatedToUnconstrained) {
+ Label l, m, n;
+ m = n;
+ l = m;
+ EXPECT_EQ(0U, l-n);
+ EXPECT_TRUE(l.IsKnownOffsetFrom(n));
+ uint64_t d;
+ EXPECT_TRUE(l.IsKnownOffsetFrom(n, &d));
+ EXPECT_EQ(0U, d);
+ EXPECT_FALSE(l.IsKnownConstant());
+}
+
+TEST(Assignment, RelatedToRelated) {
+ Label l, m, n, o;
+ l = n;
+ m = o;
+ l = m;
+ EXPECT_EQ(0U, n-o);
+ EXPECT_TRUE(n.IsKnownOffsetFrom(o));
+ uint64_t d;
+ EXPECT_TRUE(n.IsKnownOffsetFrom(o, &d));
+ EXPECT_EQ(0U, d);
+ EXPECT_FALSE(l.IsKnownConstant());
+}
+
+TEST(Assignment, RelatedToKnown) {
+ Label l, m, n;
+ m = n;
+ l = 0xd2011f8c82ad56f2ULL;
+ l = m;
+ EXPECT_TRUE(l.IsKnownConstant());
+ EXPECT_EQ(0xd2011f8c82ad56f2ULL, l.Value());
+ EXPECT_TRUE(m.IsKnownConstant());
+ EXPECT_EQ(0xd2011f8c82ad56f2ULL, m.Value());
+ EXPECT_TRUE(n.IsKnownConstant());
+ EXPECT_EQ(0xd2011f8c82ad56f2ULL, n.Value());
+}
+
+TEST(Assignment, KnownToUnconstrained) {
+ Label l, m;
+ m = 0x50b024c0d6073887ULL;
+ l = m;
+ EXPECT_TRUE(l.IsKnownConstant());
+ EXPECT_EQ(0x50b024c0d6073887ULL, l.Value());
+ EXPECT_TRUE(m.IsKnownConstant());
+ EXPECT_EQ(0x50b024c0d6073887ULL, m.Value());
+}
+
+TEST(Assignment, KnownToRelated) {
+ Label l, m, n;
+ l = n;
+ m = 0x5348883655c727e5ULL;
+ l = m;
+ EXPECT_TRUE(l.IsKnownConstant());
+ EXPECT_EQ(0x5348883655c727e5ULL, l.Value());
+ EXPECT_TRUE(m.IsKnownConstant());
+ EXPECT_EQ(0x5348883655c727e5ULL, m.Value());
+ EXPECT_TRUE(n.IsKnownConstant());
+ EXPECT_EQ(0x5348883655c727e5ULL, n.Value());
+}
+
+TEST(Assignment, KnownToKnown) {
+ Label l, m;
+ l = 0x36c209c20987564eULL;
+ m = 0x36c209c20987564eULL;
+ l = m;
+ EXPECT_TRUE(l.IsKnownConstant());
+ EXPECT_EQ(0x36c209c20987564eULL, l.Value());
+ EXPECT_TRUE(m.IsKnownConstant());
+ EXPECT_EQ(0x36c209c20987564eULL, m.Value());
+}
+
+TEST(Assignment, ConstantToUnconstrained) {
+ Label l;
+ l = 0xc02495f4d7f5a957ULL;
+ EXPECT_TRUE(l.IsKnownConstant());
+ EXPECT_EQ(0xc02495f4d7f5a957ULL, l.Value());
+}
+
+TEST(Assignment, ConstantToRelated) {
+ Label l, m;
+ l = m;
+ l = 0x4577901cf275488dULL;
+ EXPECT_TRUE(l.IsKnownConstant());
+ EXPECT_EQ(0x4577901cf275488dULL, l.Value());
+ EXPECT_TRUE(m.IsKnownConstant());
+ EXPECT_EQ(0x4577901cf275488dULL, m.Value());
+}
+
+TEST(Assignment, ConstantToKnown) {
+ Label l;
+ l = 0xec0b9c369b7e8ea7ULL;
+ l = 0xec0b9c369b7e8ea7ULL;
+ EXPECT_TRUE(l.IsKnownConstant());
+ EXPECT_EQ(0xec0b9c369b7e8ea7ULL, l.Value());
+}
+
+TEST(AssignmentDeathTest, Self) {
+ Label l;
+ ASSERT_DEATH(l = l, "binding != this");
+}
+
+TEST(AssignmentDeathTest, IndirectCycle) {
+ Label l, m, n;
+ l = m;
+ m = n;
+ ASSERT_DEATH(n = l, "binding != this");
+}
+
+TEST(AssignmentDeathTest, Cycle) {
+ Label l, m, n, o;
+ l = m;
+ m = n;
+ o = n;
+ ASSERT_DEATH(o = l, "binding != this");
+}
+
+TEST(Addition, LabelConstant) {
+ Label l, m;
+ m = l + 0x5248d93e8bbe9497ULL;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l));
+ uint64_t d;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d));
+ EXPECT_EQ(0x5248d93e8bbe9497ULL, d);
+ EXPECT_FALSE(m.IsKnownConstant());
+}
+
+TEST(Addition, ConstantLabel) {
+ Label l, m;
+ m = 0xf51e94e00d6e3c84ULL + l;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l));
+ uint64_t d;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d));
+ EXPECT_EQ(0xf51e94e00d6e3c84ULL, d);
+ EXPECT_FALSE(m.IsKnownConstant());
+}
+
+TEST(Addition, KnownLabelConstant) {
+ Label l, m;
+ l = 0x16286307042ce0d8ULL;
+ m = l + 0x3fdddd91306719d7ULL;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l));
+ uint64_t d;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d));
+ EXPECT_EQ(0x3fdddd91306719d7ULL, d);
+ EXPECT_TRUE(m.IsKnownConstant());
+ EXPECT_EQ(0x16286307042ce0d8ULL + 0x3fdddd91306719d7ULL, m.Value());
+}
+
+TEST(Addition, ConstantKnownLabel) {
+ Label l, m;
+ l = 0x50f62d0cdd1031deULL;
+ m = 0x1b13462d8577c538ULL + l;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l));
+ uint64_t d;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d));
+ EXPECT_EQ(0x1b13462d8577c538ULL, d);
+ EXPECT_TRUE(m.IsKnownConstant());
+ EXPECT_EQ(0x50f62d0cdd1031deULL + 0x1b13462d8577c538ULL, m.Value());
+}
+
+TEST(Subtraction, LabelConstant) {
+ Label l, m;
+ m = l - 0x0620884d21d3138eULL;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l));
+ uint64_t d;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d));
+ EXPECT_EQ(-0x0620884d21d3138eULL, d);
+ EXPECT_FALSE(m.IsKnownConstant());
+}
+
+TEST(Subtraction, KnownLabelConstant) {
+ Label l, m;
+ l = 0x6237fbaf9ef7929eULL;
+ m = l - 0x317730995d2ab6eeULL;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l));
+ uint64_t d;
+ EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d));
+ EXPECT_EQ(-0x317730995d2ab6eeULL, d);
+ EXPECT_TRUE(m.IsKnownConstant());
+ EXPECT_EQ(0x6237fbaf9ef7929eULL - 0x317730995d2ab6eeULL, m.Value());
+}
+
+TEST(SubtractionDeathTest, LabelLabel) {
+ Label l, m;
+ ASSERT_DEATH(l - m, "IsKnownOffsetFrom\\(label, &offset\\)");
+}
+
+TEST(Subtraction, LabelLabel) {
+ Label l, m;
+ l = m + 0x7fa77ec63e28a17aULL;
+ EXPECT_EQ(0x7fa77ec63e28a17aULL, l - m);
+ EXPECT_EQ(-0x7fa77ec63e28a17aULL, m - l);
+}
+
+TEST(IsKnownConstant, Undefined) {
+ Label l;
+ EXPECT_FALSE(l.IsKnownConstant());
+}
+
+TEST(IsKnownConstant, RelatedLabel) {
+ Label l, m;
+ l = m;
+ EXPECT_FALSE(l.IsKnownConstant());
+ EXPECT_FALSE(m.IsKnownConstant());
+}
+
+TEST(IsKnownConstant, Constant) {
+ Label l;
+ l = 0xf374b1bdd6a22576ULL;
+ EXPECT_TRUE(l.IsKnownConstant());
+}
+
+TEST(IsKnownOffsetFrom, Unrelated) {
+ Label l, m;
+ EXPECT_FALSE(l.IsKnownOffsetFrom(m));
+}
+
+TEST(IsKnownOffsetFrom, Related) {
+ Label l, m;
+ l = m;
+ EXPECT_TRUE(l.IsKnownOffsetFrom(m));
+}
+
+// Test the construction of chains of related labels, and the
+// propagation of values through them.
+//
+// Although the relations between labels are supposed to behave
+// symmetrically --- that is, 'a = b' should put a and b in
+// indistinguishable states --- there's a distinction made internally
+// between the target (a) and the source (b).
+//
+// So there are five test axes to cover:
+//
+// - Do we construct the chain with assignment ("Assign") or with constructors
+// ("Construct")?
+//
+// - Do we set the value of the label at the start of the chain
+// ("Start") or the label at the end ("End")?
+//
+// - Are we testing the propagation of a relationship between variable
+// values ("Relation"), or the propagation of a known constant value
+// ("Value")?
+//
+// - Do we set the value before building the chain ("Before") or after
+// the chain has been built ("After")?
+//
+// - Do we add new relationships to the end of the existing chain
+// ("Forward") or to the beginning ("Backward")?
+//
+// Of course, "Construct" and "Backward" can't be combined, which
+// eliminates eight combinations, and "Construct", "End", and "Before"
+// can't be combined, which eliminates two more, so there are are 22
+// combinations, not 32.
+
+TEST(LabelChain, AssignStartRelationBeforeForward) {
+ Label a, b, c, d;
+ Label x;
+ a = x;
+ b = a + 0x1;
+ c = b + 0x10;
+ d = c + 0x100;
+ EXPECT_EQ(0x111U, d-x);
+ EXPECT_EQ(0x11U, c-x);
+ EXPECT_EQ(0x1U, b-x);
+ EXPECT_EQ(0U, a-x);
+}
+
+TEST(LabelChain, AssignStartRelationBeforeBackward) {
+ Label a, b, c, d;
+ Label x;
+ a = x;
+ d = c + 0x100;
+ c = b + 0x10;
+ b = a + 0x1;
+ EXPECT_EQ(0x111U, d-x);
+ EXPECT_EQ(0x11U, c-x);
+ EXPECT_EQ(0x1U, b-x);
+ EXPECT_EQ(0U, a-x);
+}
+
+TEST(LabelChain, AssignStartRelationAfterForward) {
+ Label a, b, c, d;
+ Label x;
+ b = a + 0x1;
+ c = b + 0x10;
+ d = c + 0x100;
+ a = x;
+ EXPECT_EQ(0x111U, d-x);
+ EXPECT_EQ(0x11U, c-x);
+ EXPECT_EQ(0x1U, b-x);
+ EXPECT_EQ(0U, a-x);
+}
+
+TEST(LabelChain, AssignStartRelationAfterBackward) {
+ Label a, b, c, d;
+ Label x;
+ d = c + 0x100;
+ c = b + 0x10;
+ b = a + 0x1;
+ a = x;
+ EXPECT_EQ(0x111U, d-x);
+ EXPECT_EQ(0x11U, c-x);
+ EXPECT_EQ(0x1U, b-x);
+ EXPECT_EQ(0U, a-x);
+}
+
+TEST(LabelChain, AssignStartValueBeforeForward) {
+ Label a, b, c, d;
+ a = 0xa131200190546ac2ULL;
+ b = a + 0x1;
+ c = b + 0x10;
+ d = c + 0x100;
+ EXPECT_EQ(0xa131200190546ac2ULL + 0x111U, d.Value());
+ EXPECT_EQ(0xa131200190546ac2ULL + 0x11U, c.Value());
+ EXPECT_EQ(0xa131200190546ac2ULL + 0x1U, b.Value());
+ EXPECT_EQ(0xa131200190546ac2ULL + 0U, a.Value());
+}
+
+TEST(LabelChain, AssignStartValueBeforeBackward) {
+ Label a, b, c, d;
+ a = 0x8da17e1670ad4fa2ULL;
+ d = c + 0x100;
+ c = b + 0x10;
+ b = a + 0x1;
+ EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x111U, d.Value());
+ EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x11U, c.Value());
+ EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x1U, b.Value());
+ EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0U, a.Value());
+}
+
+TEST(LabelChain, AssignStartValueAfterForward) {
+ Label a, b, c, d;
+ b = a + 0x1;
+ c = b + 0x10;
+ d = c + 0x100;
+ a = 0x99b8f51bafd41adaULL;
+ EXPECT_EQ(0x99b8f51bafd41adaULL + 0x111U, d.Value());
+ EXPECT_EQ(0x99b8f51bafd41adaULL + 0x11U, c.Value());
+ EXPECT_EQ(0x99b8f51bafd41adaULL + 0x1U, b.Value());
+ EXPECT_EQ(0x99b8f51bafd41adaULL + 0U, a.Value());
+}
+
+TEST(LabelChain, AssignStartValueAfterBackward) {
+ Label a, b, c, d;
+ d = c + 0x100;
+ c = b + 0x10;
+ b = a + 0x1;
+ a = 0xc86ca1d97ab5df6eULL;
+ EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x111U, d.Value());
+ EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x11U, c.Value());
+ EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x1U, b.Value());
+ EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0U, a.Value());
+}
+
+TEST(LabelChain, AssignEndRelationBeforeForward) {
+ Label a, b, c, d;
+ Label x;
+ x = d;
+ b = a + 0x1;
+ c = b + 0x10;
+ d = c + 0x100;
+ EXPECT_EQ(-(uint64_t)0x111U, a-x);
+ EXPECT_EQ(-(uint64_t)0x110U, b-x);
+ EXPECT_EQ(-(uint64_t)0x100U, c-x);
+ EXPECT_EQ(-(uint64_t)0U, d-x);
+}
+
+TEST(LabelChain, AssignEndRelationBeforeBackward) {
+ Label a, b, c, d;
+ Label x;
+ x = d;
+ d = c + 0x100;
+ c = b + 0x10;
+ b = a + 0x1;
+ EXPECT_EQ(-(uint64_t)0x111U, a-x);
+ EXPECT_EQ(-(uint64_t)0x110U, b-x);
+ EXPECT_EQ(-(uint64_t)0x100U, c-x);
+ EXPECT_EQ(-(uint64_t)0U, d-x);
+}
+
+TEST(LabelChain, AssignEndRelationAfterForward) {
+ Label a, b, c, d;
+ Label x;
+ b = a + 0x1;
+ c = b + 0x10;
+ d = c + 0x100;
+ x = d;
+ EXPECT_EQ(-(uint64_t)0x111U, a-x);
+ EXPECT_EQ(-(uint64_t)0x110U, b-x);
+ EXPECT_EQ(-(uint64_t)0x100U, c-x);
+ EXPECT_EQ(-(uint64_t)0x000U, d-x);
+}
+
+TEST(LabelChain, AssignEndRelationAfterBackward) {
+ Label a, b, c, d;
+ Label x;
+ d = c + 0x100;
+ c = b + 0x10;
+ b = a + 0x1;
+ x = d;
+ EXPECT_EQ(-(uint64_t)0x111U, a-x);
+ EXPECT_EQ(-(uint64_t)0x110U, b-x);
+ EXPECT_EQ(-(uint64_t)0x100U, c-x);
+ EXPECT_EQ(-(uint64_t)0x000U, d-x);
+}
+
+TEST(LabelChain, AssignEndValueBeforeForward) {
+ Label a, b, c, d;
+ d = 0xa131200190546ac2ULL;
+ b = a + 0x1;
+ c = b + 0x10;
+ d = c + 0x100;
+ EXPECT_EQ(0xa131200190546ac2ULL - 0x111, a.Value());
+ EXPECT_EQ(0xa131200190546ac2ULL - 0x110, b.Value());
+ EXPECT_EQ(0xa131200190546ac2ULL - 0x100, c.Value());
+ EXPECT_EQ(0xa131200190546ac2ULL - 0x000, d.Value());
+}
+
+TEST(LabelChain, AssignEndValueBeforeBackward) {
+ Label a, b, c, d;
+ d = 0x8da17e1670ad4fa2ULL;
+ d = c + 0x100;
+ c = b + 0x10;
+ b = a + 0x1;
+ EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x111, a.Value());
+ EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x110, b.Value());
+ EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x100, c.Value());
+ EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x000, d.Value());
+}
+
+TEST(LabelChain, AssignEndValueAfterForward) {
+ Label a, b, c, d;
+ b = a + 0x1;
+ c = b + 0x10;
+ d = c + 0x100;
+ d = 0x99b8f51bafd41adaULL;
+ EXPECT_EQ(0x99b8f51bafd41adaULL - 0x111, a.Value());
+ EXPECT_EQ(0x99b8f51bafd41adaULL - 0x110, b.Value());
+ EXPECT_EQ(0x99b8f51bafd41adaULL - 0x100, c.Value());
+ EXPECT_EQ(0x99b8f51bafd41adaULL - 0x000, d.Value());
+}
+
+TEST(LabelChain, AssignEndValueAfterBackward) {
+ Label a, b, c, d;
+ d = c + 0x100;
+ c = b + 0x10;
+ b = a + 0x1;
+ d = 0xc86ca1d97ab5df6eULL;
+ EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x111, a.Value());
+ EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x110, b.Value());
+ EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x100, c.Value());
+ EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x000, d.Value());
+}
+
+TEST(LabelChain, ConstructStartRelationBeforeForward) {
+ Label x;
+ Label a(x);
+ Label b(a + 0x1);
+ Label c(b + 0x10);
+ Label d(c + 0x100);
+ EXPECT_EQ(0x111U, d-x);
+ EXPECT_EQ(0x11U, c-x);
+ EXPECT_EQ(0x1U, b-x);
+ EXPECT_EQ(0U, a-x);
+}
+
+TEST(LabelChain, ConstructStartRelationAfterForward) {
+ Label x;
+ Label a;
+ Label b(a + 0x1);
+ Label c(b + 0x10);
+ Label d(c + 0x100);
+ a = x;
+ EXPECT_EQ(0x111U, d-x);
+ EXPECT_EQ(0x11U, c-x);
+ EXPECT_EQ(0x1U, b-x);
+ EXPECT_EQ(0U, a-x);
+}
+
+TEST(LabelChain, ConstructStartValueBeforeForward) {
+ Label a(0x5d234d177d01ccc8ULL);
+ Label b(a + 0x1);
+ Label c(b + 0x10);
+ Label d(c + 0x100);
+ EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x111U, d.Value());
+ EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x011U, c.Value());
+ EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x001U, b.Value());
+ EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x000U, a.Value());
+}
+
+TEST(LabelChain, ConstructStartValueAfterForward) {
+ Label a;
+ Label b(a + 0x1);
+ Label c(b + 0x10);
+ Label d(c + 0x100);
+ a = 0xded85d54586e84fcULL;
+ EXPECT_EQ(0xded85d54586e84fcULL + 0x111U, d.Value());
+ EXPECT_EQ(0xded85d54586e84fcULL + 0x011U, c.Value());
+ EXPECT_EQ(0xded85d54586e84fcULL + 0x001U, b.Value());
+ EXPECT_EQ(0xded85d54586e84fcULL + 0x000U, a.Value());
+}
+
+TEST(LabelChain, ConstructEndRelationAfterForward) {
+ Label x;
+ Label a;
+ Label b(a + 0x1);
+ Label c(b + 0x10);
+ Label d(c + 0x100);
+ x = d;
+ EXPECT_EQ(-(uint64_t)0x111U, a-x);
+ EXPECT_EQ(-(uint64_t)0x110U, b-x);
+ EXPECT_EQ(-(uint64_t)0x100U, c-x);
+ EXPECT_EQ(-(uint64_t)0x000U, d-x);
+}
+
+TEST(LabelChain, ConstructEndValueAfterForward) {
+ Label a;
+ Label b(a + 0x1);
+ Label c(b + 0x10);
+ Label d(c + 0x100);
+ d = 0x99b8f51bafd41adaULL;
+ EXPECT_EQ(0x99b8f51bafd41adaULL - 0x111, a.Value());
+ EXPECT_EQ(0x99b8f51bafd41adaULL - 0x110, b.Value());
+ EXPECT_EQ(0x99b8f51bafd41adaULL - 0x100, c.Value());
+ EXPECT_EQ(0x99b8f51bafd41adaULL - 0x000, d.Value());
+}
+
+TEST(LabelTree, KnownValue) {
+ Label l, m, n, o, p;
+ l = m;
+ m = n;
+ o = p;
+ p = n;
+ l = 0x536b5de3d468a1b5ULL;
+ EXPECT_EQ(0x536b5de3d468a1b5ULL, o.Value());
+}
+
+TEST(LabelTree, Related) {
+ Label l, m, n, o, p;
+ l = m - 1;
+ m = n - 10;
+ o = p + 100;
+ p = n + 1000;
+ EXPECT_EQ(1111U, o - l);
+}
+
+TEST(EquationDeathTest, EqualConstants) {
+ Label m = 0x0d3962f280f07d24ULL;
+ Label n = 0x0d3962f280f07d24ULL;
+ m = n; // no death expected
+}
+
+TEST(EquationDeathTest, EqualIndirectConstants) {
+ Label m = 0xa347f1e5238fe6a1ULL;
+ Label n;
+ Label o = n;
+ n = 0xa347f1e5238fe6a1ULL;
+ n = m; // no death expected
+}
+
+TEST(EquationDeathTest, ConstantClash) {
+ Label m = 0xd4cc0f4f630ec741ULL;
+ Label n = 0x934cd2d8254fc3eaULL;
+ ASSERT_DEATH(m = n, "addend_ == addend");
+}
+
+TEST(EquationDeathTest, IndirectConstantClash) {
+ Label m = 0xd4cc0f4f630ec741ULL;
+ Label n, o;
+ n = o;
+ o = 0xcfbe3b83ac49ce86ULL;
+ ASSERT_DEATH(m = n, "addend_ == addend");
+}
+
+// Assigning to a related label may free the next Binding on its
+// chain. This test always passes; it is interesting to memory
+// checkers and coverage analysis.
+TEST(LabelReferenceCount, AssignmentFree) {
+ Label l;
+ {
+ Label m;
+ l = m;
+ }
+ // This should free m's Binding.
+ l = 0xca8bae92f0376d4fULL;
+ ASSERT_EQ(0xca8bae92f0376d4fULL, l.Value());
+}
+
+// Finding the value of a label may free the Binding it refers to. This test
+// always passes; it is interesting to memory checkers and coverage analysis.
+TEST(LabelReferenceCount, FindValueFree) {
+ Label l;
+ {
+ Label m, n;
+ l = m;
+ m = n;
+ n = 0x7a0b0c576672daafULL;
+ // At this point, l's Binding refers to m's Binding, which refers
+ // to n's binding.
+ }
+ // Now, l is the only reference keeping the three Bindings alive.
+ // Resolving its value should free l's and m's original bindings.
+ ASSERT_EQ(0x7a0b0c576672daafULL, l.Value());
+}
+
+TEST(ConstructSection, Simple) {
+ Section s;
+}
+
+TEST(ConstructSection, WithEndian) {
+ Section s(kBigEndian);
+}
+
+// A fixture class for TestAssembler::Section tests.
+class SectionFixture {
+ public:
+ Section section;
+ string contents;
+ static const uint8_t data[];
+ static const size_t data_size;
+};
+
+const uint8_t SectionFixture::data[] = {
+ 0x87, 0x4f, 0x43, 0x67, 0x30, 0xd0, 0xd4, 0x0e
+};
+
+#define I0()
+#define I1(a) { a }
+#define I2(a,b) { a,b }
+#define I3(a,b,c) { a,b,c }
+#define I4(a,b,c,d) { a,b,c,d }
+#define I5(a,b,c,d,e) { a,b,c,d,e }
+#define I6(a,b,c,d,e,f) { a,b,c,d,e,f }
+#define I7(a,b,c,d,e,f,g) { a,b,c,d,e,f,g }
+#define I8(a,b,c,d,e,f,g,h) { a,b,c,d,e,f,g,h }
+#define I9(a,b,c,d,e,f,g,h,i) { a,b,c,d,e,f,g,h,i }
+#define ASSERT_BYTES(s, b) \
+ do \
+ { \
+ static const uint8_t expected_bytes[] = b; \
+ ASSERT_EQ(sizeof(expected_bytes), s.size()); \
+ ASSERT_TRUE(memcmp(s.data(), (const char *) expected_bytes, \
+ sizeof(expected_bytes)) == 0); \
+ } \
+ while(0)
+
+class Append: public SectionFixture, public Test { };
+
+TEST_F(Append, Bytes) {
+ section.Append(data, sizeof(data));
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_EQ(sizeof(data), contents.size());
+ EXPECT_TRUE(0 == memcmp(contents.data(), (const char *) data, sizeof(data)));
+}
+
+TEST_F(Append, BytesTwice) {
+ section.Append(data, sizeof(data));
+ section.Append(data, sizeof(data));
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_EQ(2 * sizeof(data), contents.size());
+ ASSERT_TRUE(0 == memcmp(contents.data(), (const char *) data, sizeof(data)));
+ ASSERT_TRUE(0 == memcmp(contents.data() + sizeof(data),
+ (const char *) data, sizeof(data)));
+}
+
+TEST_F(Append, String) {
+ string s1 = "howdy ";
+ string s2 = "there";
+ section.Append(s1);
+ section.Append(s2);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_STREQ(contents.c_str(), "howdy there");
+}
+
+TEST_F(Append, CString) {
+ section.AppendCString("howdy");
+ section.AppendCString("");
+ section.AppendCString("there");
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_EQ(string("howdy\0\0there\0", 13), contents);
+}
+
+TEST_F(Append, CStringSize) {
+ section.AppendCString("howdy", 3);
+ section.AppendCString("there", 5);
+ section.AppendCString("fred", 6);
+ section.AppendCString("natalie", 0);
+ section.AppendCString("", 10);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_EQ(string("howtherefred\0\0\0\0\0\0\0\0\0\0\0\0", 24), contents);
+}
+
+TEST_F(Append, RepeatedBytes) {
+ section.Append((size_t) 10, '*');
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_STREQ(contents.c_str(), "**********");
+}
+
+TEST_F(Append, GeneralLE1) {
+ section.Append(kLittleEndian, 1, 42);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I1(42));
+}
+
+TEST_F(Append, GeneralLE2) {
+ section.Append(kLittleEndian, 2, 0x15a1);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0xa1, 0x15));
+}
+
+TEST_F(Append, GeneralLE3) {
+ section.Append(kLittleEndian, 3, 0x59ae8d);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x8d, 0xae, 0x59));
+}
+
+TEST_F(Append, GeneralLE4) {
+ section.Append(kLittleEndian, 4, 0x51603c56);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I4(0x56, 0x3c, 0x60, 0x51));
+}
+
+TEST_F(Append, GeneralLE5) {
+ section.Append(kLittleEndian, 5, 0x385e2803b4ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0xb4, 0x03, 0x28, 0x5e, 0x38));
+}
+
+TEST_F(Append, GeneralLE6) {
+ section.Append(kLittleEndian, 6, 0xc7db9534dd1fULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I6(0x1f, 0xdd, 0x34, 0x95, 0xdb, 0xc7));
+}
+
+TEST_F(Append, GeneralLE7) {
+ section.Append(kLittleEndian, 7, 0x1445c9f1b843e6ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I7(0xe6, 0x43, 0xb8, 0xf1, 0xc9, 0x45, 0x14));
+}
+
+TEST_F(Append, GeneralLE8) {
+ section.Append(kLittleEndian, 8, 0xaf48019dfe5c01e5ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I8(0xe5, 0x01, 0x5c, 0xfe, 0x9d, 0x01, 0x48, 0xaf));
+}
+
+TEST_F(Append, GeneralBE1) {
+ section.Append(kBigEndian, 1, 0xd0ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I1(0xd0));
+}
+
+TEST_F(Append, GeneralBE2) {
+ section.Append(kBigEndian, 2, 0x2e7eULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2e, 0x7e));
+}
+
+TEST_F(Append, GeneralBE3) {
+ section.Append(kBigEndian, 3, 0x37dad6ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x37, 0xda, 0xd6));
+}
+
+TEST_F(Append, GeneralBE4) {
+ section.Append(kBigEndian, 4, 0x715935c7ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I4(0x71, 0x59, 0x35, 0xc7));
+}
+
+TEST_F(Append, GeneralBE5) {
+ section.Append(kBigEndian, 5, 0x42baeb02b7ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x42, 0xba, 0xeb, 0x02, 0xb7));
+}
+
+TEST_F(Append, GeneralBE6) {
+ section.Append(kBigEndian, 6, 0xf1cdf10e7b18ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I6(0xf1, 0xcd, 0xf1, 0x0e, 0x7b, 0x18));
+}
+
+TEST_F(Append, GeneralBE7) {
+ section.Append(kBigEndian, 7, 0xf50a724f0b0d20ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I7(0xf5, 0x0a, 0x72, 0x4f, 0x0b, 0x0d, 0x20));
+}
+
+TEST_F(Append, GeneralBE8) {
+ section.Append(kBigEndian, 8, 0xa6b2cb5e98dc9c16ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I8(0xa6, 0xb2, 0xcb, 0x5e, 0x98, 0xdc, 0x9c, 0x16));
+}
+
+TEST_F(Append, GeneralLE1Label) {
+ Label l;
+ section.Append(kLittleEndian, 1, l);
+ l = 42;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I1(42));
+}
+
+TEST_F(Append, GeneralLE2Label) {
+ Label l;
+ section.Append(kLittleEndian, 2, l);
+ l = 0x15a1;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0xa1, 0x15));
+}
+
+TEST_F(Append, GeneralLE3Label) {
+ Label l;
+ section.Append(kLittleEndian, 3, l);
+ l = 0x59ae8d;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x8d, 0xae, 0x59));
+}
+
+TEST_F(Append, GeneralLE4Label) {
+ Label l;
+ section.Append(kLittleEndian, 4, l);
+ l = 0x51603c56;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I4(0x56, 0x3c, 0x60, 0x51));
+}
+
+TEST_F(Append, GeneralLE5Label) {
+ Label l;
+ section.Append(kLittleEndian, 5, l);
+ l = 0x385e2803b4ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0xb4, 0x03, 0x28, 0x5e, 0x38));
+}
+
+TEST_F(Append, GeneralLE6Label) {
+ Label l;
+ section.Append(kLittleEndian, 6, l);
+ l = 0xc7db9534dd1fULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I6(0x1f, 0xdd, 0x34, 0x95, 0xdb, 0xc7));
+}
+
+TEST_F(Append, GeneralLE7Label) {
+ Label l;
+ section.Append(kLittleEndian, 7, l);
+ l = 0x1445c9f1b843e6ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I7(0xe6, 0x43, 0xb8, 0xf1, 0xc9, 0x45, 0x14));
+}
+
+TEST_F(Append, GeneralLE8Label) {
+ Label l;
+ section.Append(kLittleEndian, 8, l);
+ l = 0xaf48019dfe5c01e5ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I8(0xe5, 0x01, 0x5c, 0xfe, 0x9d, 0x01, 0x48, 0xaf));
+}
+
+TEST_F(Append, GeneralBE1Label) {
+ Label l;
+ section.Append(kBigEndian, 1, l);
+ l = 0xd0ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I1(0xd0));
+}
+
+TEST_F(Append, GeneralBE2Label) {
+ Label l;
+ section.Append(kBigEndian, 2, l);
+ l = 0x2e7eULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2e, 0x7e));
+}
+
+TEST_F(Append, GeneralBE3Label) {
+ Label l;
+ section.Append(kBigEndian, 3, l);
+ l = 0x37dad6ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x37, 0xda, 0xd6));
+}
+
+TEST_F(Append, GeneralBE4Label) {
+ Label l;
+ section.Append(kBigEndian, 4, l);
+ l = 0x715935c7ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I4(0x71, 0x59, 0x35, 0xc7));
+}
+
+TEST_F(Append, GeneralBE5Label) {
+ Label l;
+ section.Append(kBigEndian, 5, l);
+ l = 0x42baeb02b7ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x42, 0xba, 0xeb, 0x02, 0xb7));
+}
+
+TEST_F(Append, GeneralBE6Label) {
+ Label l;
+ section.Append(kBigEndian, 6, l);
+ l = 0xf1cdf10e7b18ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I6(0xf1, 0xcd, 0xf1, 0x0e, 0x7b, 0x18));
+}
+
+TEST_F(Append, GeneralBE7Label) {
+ Label l;
+ section.Append(kBigEndian, 7, l);
+ l = 0xf50a724f0b0d20ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I7(0xf5, 0x0a, 0x72, 0x4f, 0x0b, 0x0d, 0x20));
+}
+
+TEST_F(Append, GeneralBE8Label) {
+ Label l;
+ section.Append(kBigEndian, 8, l);
+ l = 0xa6b2cb5e98dc9c16ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I8(0xa6, 0xb2, 0xcb, 0x5e, 0x98, 0xdc, 0x9c, 0x16));
+}
+
+TEST_F(Append, B8) {
+ section.Append(1, 0x2a);
+ section.B8(0xd3U);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2a, 0xd3));
+}
+
+TEST_F(Append, B8Label) {
+ Label l;
+ section.Append(1, 0x2a);
+ section.B8(l);
+ l = 0x4bU;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2a, 0x4b));
+}
+
+TEST_F(Append, B16) {
+ section.Append(1, 0x2a);
+ section.B16(0x472aU);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x2a, 0x47, 0x2a));
+}
+
+TEST_F(Append, B16Label) {
+ Label l;
+ section.Append(1, 0x2a);
+ section.B16(l);
+ l = 0x55e8U;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x2a, 0x55, 0xe8));
+}
+
+TEST_F(Append, B32) {
+ section.Append(1, 0x2a);
+ section.B32(0xbd412cbcU);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x2a, 0xbd, 0x41, 0x2c, 0xbc));
+}
+
+TEST_F(Append, B32Label) {
+ Label l;
+ section.Append(1, 0x2a);
+ section.B32(l);
+ l = 0x208e37d5U;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x2a, 0x20, 0x8e, 0x37, 0xd5));
+}
+
+TEST_F(Append, B64) {
+ section.Append(1, 0x2a);
+ section.B64(0x3402a013111e68adULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents,
+ I9(0x2a, 0x34, 0x02, 0xa0, 0x13, 0x11, 0x1e, 0x68, 0xad));
+}
+
+TEST_F(Append, B64Label) {
+ Label l;
+ section.Append(1, 0x2a);
+ section.B64(l);
+ l = 0x355dbfbb4ac6d57fULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents,
+ I9(0x2a, 0x35, 0x5d, 0xbf, 0xbb, 0x4a, 0xc6, 0xd5, 0x7f));
+}
+
+TEST_F(Append, L8) {
+ section.Append(1, 0x2a);
+ section.L8(0x26U);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2a, 0x26));
+}
+
+TEST_F(Append, L8Label) {
+ Label l;
+ section.Append(1, 0x2a);
+ section.L8(l);
+ l = 0xa8U;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2a, 0xa8));
+}
+
+TEST_F(Append, L16) {
+ section.Append(1, 0x2a);
+ section.L16(0xca6dU);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x2a, 0x6d, 0xca));
+}
+
+TEST_F(Append, L16Label) {
+ Label l;
+ section.Append(1, 0x2a);
+ section.L16(l);
+ l = 0xd21fU;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x2a, 0x1f, 0xd2));
+}
+
+TEST_F(Append, L32) {
+ section.Append(1, 0x2a);
+ section.L32(0x558f6181U);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x2a, 0x81, 0x61, 0x8f, 0x55));
+}
+
+TEST_F(Append, L32Label) {
+ Label l;
+ section.Append(1, 0x2a);
+ section.L32(l);
+ l = 0x4b810f82U;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x2a, 0x82, 0x0f, 0x81, 0x4b));
+}
+
+TEST_F(Append, L64) {
+ section.Append(1, 0x2a);
+ section.L64(0x564384f7579515bfULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents,
+ I9(0x2a, 0xbf, 0x15, 0x95, 0x57, 0xf7, 0x84, 0x43, 0x56));
+}
+
+TEST_F(Append, L64Label) {
+ Label l;
+ section.Append(1, 0x2a);
+ section.L64(l);
+ l = 0x424b1d020667c8dbULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents,
+ I9(0x2a, 0xdb, 0xc8, 0x67, 0x06, 0x02, 0x1d, 0x4b, 0x42));
+}
+
+TEST_F(Append, D8Big) {
+ section.set_endianness(kBigEndian);
+ section.Append(1, 0x2a);
+ section.D8(0xe6U);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2a, 0xe6));
+}
+
+TEST_F(Append, D8BigLabel) {
+ Label l;
+ section.set_endianness(kBigEndian);
+ section.Append(1, 0x2a);
+ section.D8(l);
+ l = 0xeeU;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2a, 0xee));
+}
+
+TEST_F(Append, D16Big) {
+ section.set_endianness(kBigEndian);
+ section.Append(1, 0x2a);
+ section.D16(0x83b1U);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x2a, 0x83, 0xb1));
+}
+
+TEST_F(Append, D16BigLabel) {
+ Label l;
+ section.set_endianness(kBigEndian);
+ section.Append(1, 0x2a);
+ section.D16(l);
+ l = 0x5b55U;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x2a, 0x5b, 0x55));
+}
+
+TEST_F(Append, D32Big) {
+ section.set_endianness(kBigEndian);
+ section.Append(1, 0x2a);
+ section.D32(0xd0b0e431U);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x2a, 0xd0, 0xb0, 0xe4, 0x31));
+}
+
+TEST_F(Append, D32BigLabel) {
+ Label l;
+ section.set_endianness(kBigEndian);
+ section.Append(1, 0x2a);
+ section.D32(l);
+ l = 0x312fb340U;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x2a, 0x31, 0x2f, 0xb3, 0x40));
+}
+
+TEST_F(Append, D64Big) {
+ section.set_endianness(kBigEndian);
+ section.Append(1, 0x2a);
+ section.D64(0xb109843500dbcb16ULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents,
+ I9(0x2a, 0xb1, 0x09, 0x84, 0x35, 0x00, 0xdb, 0xcb, 0x16));
+}
+
+TEST_F(Append, D64BigLabel) {
+ Label l;
+ section.set_endianness(kBigEndian);
+ section.Append(1, 0x2a);
+ section.D64(l);
+ l = 0x9a0d61b70f671fd7ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents,
+ I9(0x2a, 0x9a, 0x0d, 0x61, 0xb7, 0x0f, 0x67, 0x1f, 0xd7));
+}
+
+TEST_F(Append, D8Little) {
+ section.set_endianness(kLittleEndian);
+ section.Append(1, 0x2a);
+ section.D8(0x42U);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2a, 0x42));
+}
+
+TEST_F(Append, D8LittleLabel) {
+ Label l;
+ section.set_endianness(kLittleEndian);
+ section.Append(1, 0x2a);
+ section.D8(l);
+ l = 0x05U;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I2(0x2a, 0x05));
+}
+
+TEST_F(Append, D16Little) {
+ section.set_endianness(kLittleEndian);
+ section.Append(1, 0x2a);
+ section.D16(0xc5c5U);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x2a, 0xc5, 0xc5));
+}
+
+TEST_F(Append, D16LittleLabel) {
+ Label l;
+ section.set_endianness(kLittleEndian);
+ section.Append(1, 0x2a);
+ section.D16(l);
+ l = 0xb620U;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I3(0x2a, 0x20, 0xb6));
+}
+
+TEST_F(Append, D32Little) {
+ section.set_endianness(kLittleEndian);
+ section.Append(1, 0x2a);
+ section.D32(0x1a87d0feU);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x2a, 0xfe, 0xd0, 0x87, 0x1a));
+}
+
+TEST_F(Append, D32LittleLabel) {
+ Label l;
+ section.set_endianness(kLittleEndian);
+ section.Append(1, 0x2a);
+ section.D32(l);
+ l = 0xb8012d6bU;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I5(0x2a, 0x6b, 0x2d, 0x01, 0xb8));
+}
+
+TEST_F(Append, D64Little) {
+ section.set_endianness(kLittleEndian);
+ section.Append(1, 0x2a);
+ section.D64(0x42de75c61375a1deULL);
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents,
+ I9(0x2a, 0xde, 0xa1, 0x75, 0x13, 0xc6, 0x75, 0xde, 0x42));
+}
+
+TEST_F(Append, D64LittleLabel) {
+ Label l;
+ section.set_endianness(kLittleEndian);
+ section.Append(1, 0x2a);
+ section.D64(l);
+ l = 0x8b3bececf3fb5312ULL;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents,
+ I9(0x2a, 0x12, 0x53, 0xfb, 0xf3, 0xec, 0xec, 0x3b, 0x8b));
+}
+
+TEST_F(Append, Variety) {
+ Label a, b, c, d, e, f, g, h;
+ section.Append(kBigEndian, 1, a)
+ .Append(kLittleEndian, 8, h)
+ .Append(kBigEndian, 1, 0x8bULL)
+ .Append(kLittleEndian, 8, 0x0ea56540448f4439ULL)
+ .Append(kBigEndian, 2, b)
+ .Append(kLittleEndian, 7, g)
+ .Append(kBigEndian, 2, 0xcf15ULL)
+ .Append(kLittleEndian, 7, 0x29694f04c5724aULL)
+ .Append(kBigEndian, 3, c)
+ .Append(kLittleEndian, 6, f)
+ .Append(kBigEndian, 3, 0x8c3ffdULL)
+ .Append(kLittleEndian, 6, 0x6f11ba80187aULL)
+ .Append(kBigEndian, 4, d)
+ .Append(kLittleEndian, 5, e)
+ .Append(kBigEndian, 4, 0x2fda2472ULL)
+ .Append(kLittleEndian, 5, 0x0aa02d423fULL)
+ .Append(kBigEndian, 5, e)
+ .Append(kLittleEndian, 4, d)
+ .Append(kBigEndian, 5, 0x53ba432138ULL)
+ .Append(kLittleEndian, 4, 0xf139ae60ULL)
+ .Append(kBigEndian, 6, f)
+ .Append(kLittleEndian, 3, c)
+ .Append(kBigEndian, 6, 0x168e436af716ULL)
+ .Append(kLittleEndian, 3, 0x3ef189ULL)
+ .Append(kBigEndian, 7, g)
+ .Append(kLittleEndian, 2, b)
+ .Append(kBigEndian, 7, 0xacd4ef233e47d9ULL)
+ .Append(kLittleEndian, 2, 0x5311ULL)
+ .Append(kBigEndian, 8, h)
+ .Append(kLittleEndian, 1, a)
+ .Append(kBigEndian, 8, 0x4668d5f1c93637a1ULL)
+ .Append(kLittleEndian, 1, 0x65ULL);
+ a = 0x79ac9bd8aa256b35ULL;
+ b = 0x22d13097ef86c91cULL;
+ c = 0xf204968b0a05862fULL;
+ d = 0x163177f15a0eb4ecULL;
+ e = 0xbd1b0f1d977f2246ULL;
+ f = 0x2b0842eee83c6461ULL;
+ g = 0x92f4b928a4bf875eULL;
+ h = 0x61a199a8f7286ba6ULL;
+ ASSERT_EQ(8 * 18U, section.Size());
+ ASSERT_TRUE(section.GetContents(&contents));
+
+ static const uint8_t expected[] = {
+ 0x35, 0xa6, 0x6b, 0x28, 0xf7, 0xa8, 0x99, 0xa1, 0x61,
+ 0x8b, 0x39, 0x44, 0x8f, 0x44, 0x40, 0x65, 0xa5, 0x0e,
+ 0xc9, 0x1c, 0x5e, 0x87, 0xbf, 0xa4, 0x28, 0xb9, 0xf4,
+ 0xcf, 0x15, 0x4a, 0x72, 0xc5, 0x04, 0x4f, 0x69, 0x29,
+ 0x05, 0x86, 0x2f, 0x61, 0x64, 0x3c, 0xe8, 0xee, 0x42,
+ 0x8c, 0x3f, 0xfd, 0x7a, 0x18, 0x80, 0xba, 0x11, 0x6f,
+ 0x5a, 0x0e, 0xb4, 0xec, 0x46, 0x22, 0x7f, 0x97, 0x1d,
+ 0x2f, 0xda, 0x24, 0x72, 0x3f, 0x42, 0x2d, 0xa0, 0x0a,
+ 0x1d, 0x97, 0x7f, 0x22, 0x46, 0xec, 0xb4, 0x0e, 0x5a,
+ 0x53, 0xba, 0x43, 0x21, 0x38, 0x60, 0xae, 0x39, 0xf1,
+ 0x42, 0xee, 0xe8, 0x3c, 0x64, 0x61, 0x2f, 0x86, 0x05,
+ 0x16, 0x8e, 0x43, 0x6a, 0xf7, 0x16, 0x89, 0xf1, 0x3e,
+ 0xf4, 0xb9, 0x28, 0xa4, 0xbf, 0x87, 0x5e, 0x1c, 0xc9,
+ 0xac, 0xd4, 0xef, 0x23, 0x3e, 0x47, 0xd9, 0x11, 0x53,
+ 0x61, 0xa1, 0x99, 0xa8, 0xf7, 0x28, 0x6b, 0xa6, 0x35,
+ 0x46, 0x68, 0xd5, 0xf1, 0xc9, 0x36, 0x37, 0xa1, 0x65,
+ };
+
+ ASSERT_TRUE(0 == memcmp(contents.data(), expected, sizeof(expected)));
+}
+
+TEST_F(Append, Section) {
+ section.Append("murder");
+ {
+ Section middle;
+ middle.Append(" she");
+ section.Append(middle);
+ }
+ section.Append(" wrote");
+ EXPECT_EQ(16U, section.Size());
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_STREQ(contents.c_str(), "murder she wrote");
+}
+
+TEST_F(Append, SectionRefs) {
+ section.Append("sugar ");
+ Label l;
+ {
+ Section middle;
+ Label m;
+ middle.B32(m);
+ section.Append(middle);
+ m = 0x66726565;
+ }
+ section.Append(" jazz");
+ EXPECT_EQ(15U, section.Size());
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_STREQ(contents.c_str(), "sugar free jazz");
+}
+
+TEST_F(Append, LEB128_0) {
+ section.LEB128(0);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\0", 1), contents);
+}
+
+TEST_F(Append, LEB128_0x3f) {
+ section.LEB128(0x3f);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x3f", 1), contents);
+}
+
+TEST_F(Append, LEB128_0x40) {
+ section.LEB128(0x40);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xc0\x00", 2), contents);
+}
+
+TEST_F(Append, LEB128_0x7f) {
+ section.LEB128(0x7f);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xff\x00", 2), contents);
+}
+
+TEST_F(Append, LEB128_0x80) {
+ section.LEB128(0x80);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x80\x01", 2), contents);
+}
+
+TEST_F(Append, LEB128_0xff) {
+ section.LEB128(0xff);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xff\x01", 2), contents);
+}
+
+TEST_F(Append, LEB128_0x1fff) {
+ section.LEB128(0x1fff);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xff\x3f", 2), contents);
+}
+
+TEST_F(Append, LEB128_0x2000) {
+ section.LEB128(0x2000);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x80\xc0\x00", 3), contents);
+}
+
+TEST_F(Append, LEB128_n1) {
+ section.LEB128(-1);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x7f", 1), contents);
+}
+
+TEST_F(Append, LEB128_n0x40) {
+ section.LEB128(-0x40);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x40", 1), contents);
+}
+
+TEST_F(Append, LEB128_n0x41) {
+ section.LEB128(-0x41);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xbf\x7f", 2), contents);
+}
+
+TEST_F(Append, LEB128_n0x7f) {
+ section.LEB128(-0x7f);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x81\x7f", 2), contents);
+}
+
+TEST_F(Append, LEB128_n0x80) {
+ section.LEB128(-0x80);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x80\x7f", 2), contents);
+}
+
+TEST_F(Append, LEB128_n0x2000) {
+ section.LEB128(-0x2000);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x80\x40", 2), contents);
+}
+
+TEST_F(Append, LEB128_n0x2001) {
+ section.LEB128(-0x2001);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xff\xbf\x7f", 3), contents);
+}
+
+TEST_F(Append,ULEB128_0) {
+ section.ULEB128(0);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\0", 1), contents);
+}
+
+TEST_F(Append,ULEB128_1) {
+ section.ULEB128(1);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x01", 1), contents);
+}
+
+TEST_F(Append,ULEB128_0x3f) {
+ section.ULEB128(0x3f);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x3f", 1), contents);
+}
+
+TEST_F(Append,ULEB128_0x40) {
+ section.ULEB128(0x40);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x40", 1), contents);
+}
+
+TEST_F(Append,ULEB128_0x7f) {
+ section.ULEB128(0x7f);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x7f", 1), contents);
+}
+
+TEST_F(Append,ULEB128_0x80) {
+ section.ULEB128(0x80);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x80\x01", 2), contents);
+}
+
+TEST_F(Append,ULEB128_0xff) {
+ section.ULEB128(0xff);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xff\x01", 2), contents);
+}
+
+TEST_F(Append,ULEB128_0x100) {
+ section.ULEB128(0x100);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x80\x02", 2), contents);
+}
+
+TEST_F(Append,ULEB128_0x1fff) {
+ section.ULEB128(0x1fff);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xff\x3f", 2), contents);
+}
+
+TEST_F(Append,ULEB128_0x2000) {
+ section.ULEB128(0x2000);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x80\x40", 2), contents);
+}
+
+TEST_F(Append,ULEB128_0x3fff) {
+ section.ULEB128(0x3fff);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xff\x7f", 2), contents);
+}
+
+TEST_F(Append,ULEB128_0x4000) {
+ section.ULEB128(0x4000);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x80\x80\x01", 3), contents);
+}
+
+TEST_F(Append,ULEB128_12857) {
+ section.ULEB128(12857);
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\xb9\x64", 2), contents);
+}
+
+TEST_F(Append, LEBChain) {
+ section.LEB128(-0x80).ULEB128(12857).Append("*");
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(string("\x80\x7f\xb9\x64*", 5), contents);
+}
+
+
+class GetContents: public SectionFixture, public Test { };
+
+TEST_F(GetContents, Undefined) {
+ Label l;
+ section.Append(kLittleEndian, 8, l);
+ ASSERT_FALSE(section.GetContents(&contents));
+}
+
+TEST_F(GetContents, ClearsContents) {
+ section.Append((size_t) 10, '*');
+ EXPECT_EQ(10U, section.Size());
+ EXPECT_TRUE(section.GetContents(&contents));
+ EXPECT_EQ(0U, section.Size());
+}
+
+TEST_F(GetContents, ClearsReferences) {
+ Label l;
+ section.Append(kBigEndian, 1, l);
+ l = 42;
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_BYTES(contents, I1(42));
+ ASSERT_TRUE(section.GetContents(&contents)); // should not die
+}
+
+class Miscellanea: public SectionFixture, public Test { };
+
+TEST_F(Miscellanea, Clear) {
+ section.Append("howdy");
+ Label l;
+ section.L32(l);
+ EXPECT_EQ(9U, section.Size());
+ section.Clear();
+ EXPECT_EQ(0U, section.Size());
+ l = 0x8d231bf0U;
+ ASSERT_TRUE(section.GetContents(&contents)); // should not die
+}
+
+TEST_F(Miscellanea, Align) {
+ section.Append("*");
+ EXPECT_EQ(1U, section.Size());
+ section.Align(4).Append("*");
+ EXPECT_EQ(5U, section.Size());
+ section.Append("*").Align(2);
+ EXPECT_EQ(6U, section.Size());
+}
+
+TEST_F(Miscellanea, AlignPad) {
+ section.Append("*");
+ EXPECT_EQ(1U, section.Size());
+ section.Align(4, ' ').Append("*");
+ EXPECT_EQ(5U, section.Size());
+ section.Append("*").Align(2, ' ');
+ EXPECT_EQ(6U, section.Size());
+ ASSERT_TRUE(section.GetContents(&contents));
+ ASSERT_EQ(string("* **"), contents);
+}
+
+TEST_F(Miscellanea, StartHereMark) {
+ Label m;
+ section.Append(42, ' ').Mark(&m).Append(13, '+');
+ EXPECT_EQ(42U, m - section.start());
+ EXPECT_EQ(42U + 13U, section.Here() - section.start());
+ EXPECT_FALSE(section.start().IsKnownConstant());
+ EXPECT_FALSE(m.IsKnownConstant());
+ EXPECT_FALSE(section.Here().IsKnownConstant());
+}
+
+TEST_F(Miscellanea, Endianness) {
+ section.set_endianness(kBigEndian);
+ EXPECT_EQ(kBigEndian, section.endianness());
+ section.set_endianness(kLittleEndian);
+ EXPECT_EQ(kLittleEndian, section.endianness());
+}
diff --git a/3rdParty/Breakpad/src/common/unordered.h b/3rdParty/Breakpad/src/common/unordered.h
new file mode 100644
index 0000000..ec665cc
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/unordered.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Include this file to use unordered_map and unordered_set. If tr1
+// or C++11 is not available, you can switch to using hash_set and
+// hash_map by defining BP_USE_HASH_SET.
+
+#ifndef COMMON_UNORDERED_H_
+#define COMMON_UNORDERED_H_
+
+#if defined(BP_USE_HASH_SET)
+#include <hash_map>
+#include <hash_set>
+
+// For hash<string>.
+#include "util/hash/hash.h"
+
+template <class T, class U, class H = __gnu_cxx::hash<T> >
+struct unordered_map : public hash_map<T, U, H> {};
+template <class T, class H = __gnu_cxx::hash<T> >
+struct unordered_set : public hash_set<T, H> {};
+
+#elif defined(_LIBCPP_VERSION) // c++11
+#include <unordered_map>
+#include <unordered_set>
+using std::unordered_map;
+using std::unordered_set;
+
+#else // Fallback to tr1::unordered
+#include <tr1/unordered_map>
+#include <tr1/unordered_set>
+using std::tr1::unordered_map;
+using std::tr1::unordered_set;
+#endif
+
+#endif // COMMON_UNORDERED_H_
diff --git a/3rdParty/Breakpad/src/common/windows/dia_util.cc b/3rdParty/Breakpad/src/common/windows/dia_util.cc
new file mode 100644
index 0000000..f4cfa1d
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/dia_util.cc
@@ -0,0 +1,92 @@
+// Copyright 2013 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "common/windows/dia_util.h"
+
+#include <atlbase.h>
+
+namespace google_breakpad {
+
+bool FindDebugStream(const wchar_t* name,
+ IDiaSession* session,
+ IDiaEnumDebugStreamData** debug_stream) {
+ CComPtr<IDiaEnumDebugStreams> enum_debug_streams;
+ if (FAILED(session->getEnumDebugStreams(&enum_debug_streams))) {
+ fprintf(stderr, "IDiaSession::getEnumDebugStreams failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaEnumDebugStreamData> temp_debug_stream;
+ ULONG fetched = 0;
+ while (SUCCEEDED(enum_debug_streams->Next(1, &temp_debug_stream, &fetched)) &&
+ fetched == 1) {
+ CComBSTR stream_name;
+ if (FAILED(temp_debug_stream->get_name(&stream_name))) {
+ fprintf(stderr, "IDiaEnumDebugStreamData::get_name failed\n");
+ return false;
+ }
+
+ // Found the stream?
+ if (wcsncmp((LPWSTR)stream_name, name, stream_name.Length()) == 0) {
+ *debug_stream = temp_debug_stream.Detach();
+ return true;
+ }
+
+ temp_debug_stream.Release();
+ }
+
+ // No table was found.
+ return false;
+}
+
+bool FindTable(REFIID iid, IDiaSession* session, void** table) {
+ // Get the table enumerator.
+ CComPtr<IDiaEnumTables> enum_tables;
+ if (FAILED(session->getEnumTables(&enum_tables))) {
+ fprintf(stderr, "IDiaSession::getEnumTables failed\n");
+ return false;
+ }
+
+ // Iterate through the tables.
+ CComPtr<IDiaTable> temp_table;
+ ULONG fetched = 0;
+ while (SUCCEEDED(enum_tables->Next(1, &temp_table, &fetched)) &&
+ fetched == 1) {
+ void* temp = NULL;
+ if (SUCCEEDED(temp_table->QueryInterface(iid, &temp))) {
+ *table = temp;
+ return true;
+ }
+ temp_table.Release();
+ }
+
+ // The table was not found.
+ return false;
+}
+
+} // namespace google_breakpad \ No newline at end of file
diff --git a/3rdParty/Breakpad/src/common/windows/dia_util.h b/3rdParty/Breakpad/src/common/windows/dia_util.h
new file mode 100644
index 0000000..b9e0df2
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/dia_util.h
@@ -0,0 +1,64 @@
+// Copyright 2013 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Utilities for loading debug streams and tables from a PDB file.
+
+#ifndef COMMON_WINDOWS_DIA_UTIL_H_
+#define COMMON_WINDOWS_DIA_UTIL_H_
+
+#include <Windows.h>
+#include <dia2.h>
+
+namespace google_breakpad {
+
+// Find the debug stream of the given |name| in the given |session|. Returns
+// true on success, false on error of if the stream does not exist. On success
+// the stream will be returned via |debug_stream|.
+bool FindDebugStream(const wchar_t* name,
+ IDiaSession* session,
+ IDiaEnumDebugStreamData** debug_stream);
+
+// Finds the first table implementing the COM interface with ID |iid| in the
+// given |session|. Returns true on success, false on error or if no such
+// table is found. On success the table will be returned via |table|.
+bool FindTable(REFIID iid, IDiaSession* session, void** table);
+
+// A templated version of FindTable. Finds the first table implementing type
+// |InterfaceType| in the given |session|. Returns true on success, false on
+// error or if no such table is found. On success the table will be returned via
+// |table|.
+template<typename InterfaceType>
+bool FindTable(IDiaSession* session, InterfaceType** table) {
+ return FindTable(__uuidof(InterfaceType),
+ session,
+ reinterpret_cast<void**>(table));
+}
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_DIA_UTIL_H_
diff --git a/3rdParty/Breakpad/src/common/windows/guid_string.h b/3rdParty/Breakpad/src/common/windows/guid_string.h
index f8aa8a2..48a5c1d 100644
--- a/3rdParty/Breakpad/src/common/windows/guid_string.h
+++ b/3rdParty/Breakpad/src/common/windows/guid_string.h
@@ -29,10 +29,10 @@
// guid_string.cc: Convert GUIDs to strings.
-#ifndef COMMON_WINDOWS_GUID_STRING_H__
-#define COMMON_WINDOWS_GUID_STRING_H__
+#ifndef COMMON_WINDOWS_GUID_STRING_H_
+#define COMMON_WINDOWS_GUID_STRING_H_
-#include <Guiddef.h>
+#include <guiddef.h>
#include <string>
@@ -55,4 +55,4 @@ class GUIDString {
} // namespace google_breakpad
-#endif // COMMON_WINDOWS_GUID_STRING_H__
+#endif // COMMON_WINDOWS_GUID_STRING_H_
diff --git a/3rdParty/Breakpad/src/common/windows/http_upload.cc b/3rdParty/Breakpad/src/common/windows/http_upload.cc
new file mode 100644
index 0000000..7676bdc
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/http_upload.cc
@@ -0,0 +1,420 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <assert.h>
+
+// Disable exception handler warnings.
+#pragma warning(disable:4530)
+
+#include <fstream>
+
+#include "common/windows/string_utils-inl.h"
+
+#include "common/windows/http_upload.h"
+
+namespace google_breakpad {
+
+using std::ifstream;
+using std::ios;
+
+static const wchar_t kUserAgent[] = L"Breakpad/1.0 (Windows)";
+
+// Helper class which closes an internet handle when it goes away
+class HTTPUpload::AutoInternetHandle {
+ public:
+ explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {}
+ ~AutoInternetHandle() {
+ if (handle_) {
+ InternetCloseHandle(handle_);
+ }
+ }
+
+ HINTERNET get() { return handle_; }
+
+ private:
+ HINTERNET handle_;
+};
+
+// static
+bool HTTPUpload::SendRequest(const wstring &url,
+ const map<wstring, wstring> &parameters,
+ const map<wstring, wstring> &files,
+ int *timeout,
+ wstring *response_body,
+ int *response_code) {
+ if (response_code) {
+ *response_code = 0;
+ }
+
+ // TODO(bryner): support non-ASCII parameter names
+ if (!CheckParameters(parameters)) {
+ return false;
+ }
+
+ // Break up the URL and make sure we can handle it
+ wchar_t scheme[16], host[256], path[256];
+ URL_COMPONENTS components;
+ memset(&components, 0, sizeof(components));
+ components.dwStructSize = sizeof(components);
+ components.lpszScheme = scheme;
+ components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]);
+ components.lpszHostName = host;
+ components.dwHostNameLength = sizeof(host) / sizeof(host[0]);
+ components.lpszUrlPath = path;
+ components.dwUrlPathLength = sizeof(path) / sizeof(path[0]);
+ if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
+ 0, &components)) {
+ return false;
+ }
+ bool secure = false;
+ if (wcscmp(scheme, L"https") == 0) {
+ secure = true;
+ } else if (wcscmp(scheme, L"http") != 0) {
+ return false;
+ }
+
+ AutoInternetHandle internet(InternetOpen(kUserAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG,
+ NULL, // proxy name
+ NULL, // proxy bypass
+ 0)); // flags
+ if (!internet.get()) {
+ return false;
+ }
+
+ AutoInternetHandle connection(InternetConnect(internet.get(),
+ host,
+ components.nPort,
+ NULL, // user name
+ NULL, // password
+ INTERNET_SERVICE_HTTP,
+ 0, // flags
+ NULL)); // context
+ if (!connection.get()) {
+ return false;
+ }
+
+ DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0;
+ http_open_flags |= INTERNET_FLAG_NO_COOKIES;
+ AutoInternetHandle request(HttpOpenRequest(connection.get(),
+ L"POST",
+ path,
+ NULL, // version
+ NULL, // referer
+ NULL, // agent type
+ http_open_flags,
+ NULL)); // context
+ if (!request.get()) {
+ return false;
+ }
+
+ wstring boundary = GenerateMultipartBoundary();
+ wstring content_type_header = GenerateRequestHeader(boundary);
+ HttpAddRequestHeaders(request.get(),
+ content_type_header.c_str(),
+ static_cast<DWORD>(-1),
+ HTTP_ADDREQ_FLAG_ADD);
+
+ string request_body;
+ if (!GenerateRequestBody(parameters, files, boundary, &request_body)) {
+ return false;
+ }
+
+ if (timeout) {
+ if (!InternetSetOption(request.get(),
+ INTERNET_OPTION_SEND_TIMEOUT,
+ timeout,
+ sizeof(*timeout))) {
+ fwprintf(stderr, L"Could not unset send timeout, continuing...\n");
+ }
+
+ if (!InternetSetOption(request.get(),
+ INTERNET_OPTION_RECEIVE_TIMEOUT,
+ timeout,
+ sizeof(*timeout))) {
+ fwprintf(stderr, L"Could not unset receive timeout, continuing...\n");
+ }
+ }
+
+ if (!HttpSendRequest(request.get(), NULL, 0,
+ const_cast<char *>(request_body.data()),
+ static_cast<DWORD>(request_body.size()))) {
+ return false;
+ }
+
+ // The server indicates a successful upload with HTTP status 200.
+ wchar_t http_status[4];
+ DWORD http_status_size = sizeof(http_status);
+ if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE,
+ static_cast<LPVOID>(&http_status), &http_status_size,
+ 0)) {
+ return false;
+ }
+
+ int http_response = wcstol(http_status, NULL, 10);
+ if (response_code) {
+ *response_code = http_response;
+ }
+
+ bool result = (http_response == 200);
+
+ if (result) {
+ result = ReadResponse(request.get(), response_body);
+ }
+
+ return result;
+}
+
+// static
+bool HTTPUpload::ReadResponse(HINTERNET request, wstring *response) {
+ bool has_content_length_header = false;
+ wchar_t content_length[32];
+ DWORD content_length_size = sizeof(content_length);
+ DWORD claimed_size = 0;
+ string response_body;
+
+ if (HttpQueryInfo(request, HTTP_QUERY_CONTENT_LENGTH,
+ static_cast<LPVOID>(&content_length),
+ &content_length_size, 0)) {
+ has_content_length_header = true;
+ claimed_size = wcstol(content_length, NULL, 10);
+ response_body.reserve(claimed_size);
+ }
+
+
+ DWORD bytes_available;
+ DWORD total_read = 0;
+ BOOL return_code;
+
+ while (((return_code = InternetQueryDataAvailable(request, &bytes_available,
+ 0, 0)) != 0) && bytes_available > 0) {
+ vector<char> response_buffer(bytes_available);
+ DWORD size_read;
+
+ return_code = InternetReadFile(request,
+ &response_buffer[0],
+ bytes_available, &size_read);
+
+ if (return_code && size_read > 0) {
+ total_read += size_read;
+ response_body.append(&response_buffer[0], size_read);
+ } else {
+ break;
+ }
+ }
+
+ bool succeeded = return_code && (!has_content_length_header ||
+ (total_read == claimed_size));
+ if (succeeded && response) {
+ *response = UTF8ToWide(response_body);
+ }
+
+ return succeeded;
+}
+
+// static
+wstring HTTPUpload::GenerateMultipartBoundary() {
+ // The boundary has 27 '-' characters followed by 16 hex digits
+ static const wchar_t kBoundaryPrefix[] = L"---------------------------";
+ static const int kBoundaryLength = 27 + 16 + 1;
+
+ // Generate some random numbers to fill out the boundary
+ int r0 = rand();
+ int r1 = rand();
+
+ wchar_t temp[kBoundaryLength];
+ swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
+
+ // remove when VC++7.1 is no longer supported
+ temp[kBoundaryLength - 1] = L'\0';
+
+ return wstring(temp);
+}
+
+// static
+wstring HTTPUpload::GenerateRequestHeader(const wstring &boundary) {
+ wstring header = L"Content-Type: multipart/form-data; boundary=";
+ header += boundary;
+ return header;
+}
+
+// static
+bool HTTPUpload::GenerateRequestBody(const map<wstring, wstring> &parameters,
+ const map<wstring, wstring> &files,
+ const wstring &boundary,
+ string *request_body) {
+ string boundary_str = WideToUTF8(boundary);
+ if (boundary_str.empty()) {
+ return false;
+ }
+
+ request_body->clear();
+
+ // Append each of the parameter pairs as a form-data part
+ for (map<wstring, wstring>::const_iterator pos = parameters.begin();
+ pos != parameters.end(); ++pos) {
+ request_body->append("--" + boundary_str + "\r\n");
+ request_body->append("Content-Disposition: form-data; name=\"" +
+ WideToUTF8(pos->first) + "\"\r\n\r\n" +
+ WideToUTF8(pos->second) + "\r\n");
+ }
+
+ for (map<wstring, wstring>::const_iterator pos = files.begin();
+ pos != files.end(); ++pos) {
+ vector<char> contents;
+ if (!GetFileContents(pos->second, &contents)) {
+ return false;
+ }
+
+ // Now append the upload files as a binary (octet-stream) part
+ string filename_utf8 = WideToUTF8(pos->second);
+ if (filename_utf8.empty()) {
+ return false;
+ }
+
+ string file_part_name_utf8 = WideToUTF8(pos->first);
+ if (file_part_name_utf8.empty()) {
+ return false;
+ }
+
+ request_body->append("--" + boundary_str + "\r\n");
+ request_body->append("Content-Disposition: form-data; "
+ "name=\"" + file_part_name_utf8 + "\"; "
+ "filename=\"" + filename_utf8 + "\"\r\n");
+ request_body->append("Content-Type: application/octet-stream\r\n");
+ request_body->append("\r\n");
+
+ if (!contents.empty()) {
+ request_body->append(&(contents[0]), contents.size());
+ }
+ request_body->append("\r\n");
+ }
+ request_body->append("--" + boundary_str + "--\r\n");
+ return true;
+}
+
+// static
+bool HTTPUpload::GetFileContents(const wstring &filename,
+ vector<char> *contents) {
+ bool rv = false;
+ // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
+ // wchar_t* filename, so use _wfopen directly in that case. For VC8 and
+ // later, _wfopen has been deprecated in favor of _wfopen_s, which does
+ // not exist in earlier versions, so let the ifstream open the file itself.
+ // GCC doesn't support wide file name and opening on FILE* requires ugly
+ // hacks, so fallback to multi byte file.
+#ifdef _MSC_VER
+ ifstream file;
+ file.open(filename.c_str(), ios::binary);
+#else // GCC
+ ifstream file(WideToMBCP(filename, CP_ACP).c_str(), ios::binary);
+#endif // _MSC_VER >= 1400
+ if (file.is_open()) {
+ file.seekg(0, ios::end);
+ std::streamoff length = file.tellg();
+ // Check for loss of data when converting lenght from std::streamoff into
+ // std::vector<char>::size_type
+ std::vector<char>::size_type vector_size =
+ static_cast<std::vector<char>::size_type>(length);
+ if (static_cast<std::streamoff>(vector_size) == length) {
+ contents->resize(vector_size);
+ if (length != 0) {
+ file.seekg(0, ios::beg);
+ file.read(&((*contents)[0]), length);
+ }
+ rv = true;
+ }
+ file.close();
+ }
+ return rv;
+}
+
+// static
+wstring HTTPUpload::UTF8ToWide(const string &utf8) {
+ if (utf8.length() == 0) {
+ return wstring();
+ }
+
+ // compute the length of the buffer we'll need
+ int charcount = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0);
+
+ if (charcount == 0) {
+ return wstring();
+ }
+
+ // convert
+ wchar_t* buf = new wchar_t[charcount];
+ MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, buf, charcount);
+ wstring result(buf);
+ delete[] buf;
+ return result;
+}
+
+// static
+string HTTPUpload::WideToMBCP(const wstring &wide, unsigned int cp) {
+ if (wide.length() == 0) {
+ return string();
+ }
+
+ // compute the length of the buffer we'll need
+ int charcount = WideCharToMultiByte(cp, 0, wide.c_str(), -1,
+ NULL, 0, NULL, NULL);
+ if (charcount == 0) {
+ return string();
+ }
+
+ // convert
+ char *buf = new char[charcount];
+ WideCharToMultiByte(cp, 0, wide.c_str(), -1, buf, charcount,
+ NULL, NULL);
+
+ string result(buf);
+ delete[] buf;
+ return result;
+}
+
+// static
+bool HTTPUpload::CheckParameters(const map<wstring, wstring> &parameters) {
+ for (map<wstring, wstring>::const_iterator pos = parameters.begin();
+ pos != parameters.end(); ++pos) {
+ const wstring &str = pos->first;
+ if (str.size() == 0) {
+ return false; // disallow empty parameter names
+ }
+ for (unsigned int i = 0; i < str.size(); ++i) {
+ wchar_t c = str[i];
+ if (c < 32 || c == '"' || c > 127) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/windows/http_upload.h b/3rdParty/Breakpad/src/common/windows/http_upload.h
new file mode 100644
index 0000000..f8d48cb
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/http_upload.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST
+// request using wininet. It currently supports requests that contain
+// a set of string parameters (key/value pairs), and a file to upload.
+
+#ifndef COMMON_WINDOWS_HTTP_UPLOAD_H_
+#define COMMON_WINDOWS_HTTP_UPLOAD_H_
+
+#pragma warning(push)
+// Disable exception handler warnings.
+#pragma warning(disable : 4530)
+
+#include <windows.h>
+#include <wininet.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace google_breakpad {
+
+using std::string;
+using std::wstring;
+using std::map;
+using std::vector;
+
+class HTTPUpload {
+ public:
+ // Sends the given sets of parameters and files as a multipart POST
+ // request to the given URL.
+ // Each key in |files| is the name of the file part of the request
+ // (i.e. it corresponds to the name= attribute on an <input type="file">.
+ // Parameter names must contain only printable ASCII characters,
+ // and may not contain a quote (") character.
+ // Only HTTP(S) URLs are currently supported. Returns true on success.
+ // If the request is successful and response_body is non-NULL,
+ // the response body will be returned in response_body.
+ // If response_code is non-NULL, it will be set to the HTTP response code
+ // received (or 0 if the request failed before getting an HTTP response).
+ static bool SendRequest(const wstring &url,
+ const map<wstring, wstring> &parameters,
+ const map<wstring, wstring> &files,
+ int *timeout,
+ wstring *response_body,
+ int *response_code);
+
+ private:
+ class AutoInternetHandle;
+
+ // Retrieves the HTTP response. If NULL is passed in for response,
+ // this merely checks (via the return value) that we were successfully
+ // able to retrieve exactly as many bytes of content in the response as
+ // were specified in the Content-Length header.
+ static bool ReadResponse(HINTERNET request, wstring* response);
+
+ // Generates a new multipart boundary for a POST request
+ static wstring GenerateMultipartBoundary();
+
+ // Generates a HTTP request header for a multipart form submit.
+ static wstring GenerateRequestHeader(const wstring &boundary);
+
+ // Given a set of parameters, a set of upload files, and a file part name,
+ // generates a multipart request body string with these parameters
+ // and minidump contents. Returns true on success.
+ static bool GenerateRequestBody(const map<wstring, wstring> &parameters,
+ const map<wstring, wstring> &files,
+ const wstring &boundary,
+ string *request_body);
+
+ // Fills the supplied vector with the contents of filename.
+ static bool GetFileContents(const wstring &filename, vector<char> *contents);
+
+ // Converts a UTF8 string to UTF16.
+ static wstring UTF8ToWide(const string &utf8);
+
+ // Converts a UTF16 string to UTF8.
+ static string WideToUTF8(const wstring &wide) {
+ return WideToMBCP(wide, CP_UTF8);
+ }
+
+ // Converts a UTF16 string to specified code page.
+ static string WideToMBCP(const wstring &wide, unsigned int cp);
+
+ // Checks that the given list of parameters has only printable
+ // ASCII characters in the parameter name, and does not contain
+ // any quote (") characters. Returns true if so.
+ static bool CheckParameters(const map<wstring, wstring> &parameters);
+
+ // No instances of this class should be created.
+ // Disallow all constructors, destructors, and operator=.
+ HTTPUpload();
+ explicit HTTPUpload(const HTTPUpload &);
+ void operator=(const HTTPUpload &);
+ ~HTTPUpload();
+};
+
+} // namespace google_breakpad
+
+#pragma warning(pop)
+
+#endif // COMMON_WINDOWS_HTTP_UPLOAD_H_
diff --git a/3rdParty/Breakpad/src/common/windows/omap.cc b/3rdParty/Breakpad/src/common/windows/omap.cc
new file mode 100644
index 0000000..ba3ce86
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/omap.cc
@@ -0,0 +1,716 @@
+// Copyright 2013 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This contains a suite of tools for transforming symbol information when
+// when that information has been extracted from a PDB containing OMAP
+// information.
+
+// OMAP information is a lightweight description of a mapping between two
+// address spaces. It consists of two streams, each of them a vector 2-tuples.
+// The OMAPTO stream contains tuples of the form
+//
+// (RVA in transformed image, RVA in original image)
+//
+// while the OMAPFROM stream contains tuples of the form
+//
+// (RVA in original image, RVA in transformed image)
+//
+// The entries in each vector are sorted by the first value of the tuple, and
+// the lengths associated with a mapping are implicit as the distance between
+// two successive addresses in the vector.
+
+// Consider a trivial 10-byte function described by the following symbol:
+//
+// Function: RVA 0x00001000, length 10, "foo"
+//
+// Now consider the same function, but with 5-bytes of instrumentation injected
+// at offset 5. The OMAP streams describing this would look like:
+//
+// OMAPTO : [ [0x00001000, 0x00001000],
+// [0x00001005, 0xFFFFFFFF],
+// [0x0000100a, 0x00001005] ]
+// OMAPFROM: [ [0x00001000, 0x00001000],
+// [0x00001005, 0x0000100a] ]
+//
+// In this case the injected code has been marked as not originating in the
+// source image, and thus it will have no symbol information at all. However,
+// the injected code may also be associated with an original address range;
+// for example, when prepending instrumentation to a basic block the
+// instrumentation can be labelled as originating from the same source BB such
+// that symbol resolution will still find the appropriate source code line
+// number. In this case the OMAP stream would look like:
+//
+// OMAPTO : [ [0x00001000, 0x00001000],
+// [0x00001005, 0x00001005],
+// [0x0000100a, 0x00001005] ]
+// OMAPFROM: [ [0x00001000, 0x00001000],
+// [0x00001005, 0x0000100a] ]
+//
+// Suppose we asked DIA to lookup the symbol at location 0x0000100a of the
+// instrumented image. It would first run this through the OMAPTO table and
+// translate that address to 0x00001005. It would then lookup the symbol
+// at that address and return the symbol for the function "foo". This is the
+// correct result.
+//
+// However, if we query DIA for the length and address of the symbol it will
+// tell us that it has length 10 and is at RVA 0x00001000. The location is
+// correct, but the length doesn't take into account the 5-bytes of injected
+// code. Symbol resolution works (starting from an instrumented address,
+// mapping to an original address, and looking up a symbol), but the symbol
+// metadata is incorrect.
+//
+// If we dump the symbols using DIA they will have their addresses
+// appropriately transformed and reflect positions in the instrumented image.
+// However, if we try to do a lookup using those symbols resolution can fail.
+// For example, the address 0x0000100a will not map to the symbol for "foo",
+// because DIA tells us it is at location 0x00001000 (correct) with length
+// 10 (incorrect). The problem is one of order of operations: in this case
+// we're attempting symbol resolution by looking up an instrumented address
+// in the table of translated symbols.
+//
+// One way to handle this is to dump the OMAP information as part of the
+// breakpad symbols. This requires the rest of the toolchain to be aware of
+// OMAP information and to use it when present prior to performing lookup. The
+// other option is to properly transform the symbols (updating length as well as
+// position) so that resolution will work as expected for translated addresses.
+// This is transparent to the rest of the toolchain.
+
+#include "common/windows/omap.h"
+
+#include <atlbase.h>
+
+#include <algorithm>
+#include <cassert>
+#include <set>
+
+#include "common/windows/dia_util.h"
+
+namespace google_breakpad {
+
+namespace {
+
+static const wchar_t kOmapToDebugStreamName[] = L"OMAPTO";
+static const wchar_t kOmapFromDebugStreamName[] = L"OMAPFROM";
+
+// Dependending on where this is used in breakpad we sometimes get min/max from
+// windef, and other times from algorithm. To get around this we simply
+// define our own min/max functions.
+template<typename T>
+const T& Min(const T& t1, const T& t2) { return t1 < t2 ? t1 : t2; }
+template<typename T>
+const T& Max(const T& t1, const T& t2) { return t1 > t2 ? t1 : t2; }
+
+// It makes things more readable to have two different OMAP types. We cast
+// normal OMAPs into these. They must be the same size as the OMAP structure
+// for this to work, hence the static asserts.
+struct OmapOrigToTran {
+ DWORD rva_original;
+ DWORD rva_transformed;
+};
+struct OmapTranToOrig {
+ DWORD rva_transformed;
+ DWORD rva_original;
+};
+static_assert(sizeof(OmapOrigToTran) == sizeof(OMAP),
+ "OmapOrigToTran must have same size as OMAP.");
+static_assert(sizeof(OmapTranToOrig) == sizeof(OMAP),
+ "OmapTranToOrig must have same size as OMAP.");
+typedef std::vector<OmapOrigToTran> OmapFromTable;
+typedef std::vector<OmapTranToOrig> OmapToTable;
+
+// Used for sorting and searching through a Mapping.
+bool MappedRangeOriginalLess(const MappedRange& lhs, const MappedRange& rhs) {
+ if (lhs.rva_original < rhs.rva_original)
+ return true;
+ if (lhs.rva_original > rhs.rva_original)
+ return false;
+ return lhs.length < rhs.length;
+}
+bool MappedRangeMappedLess(const MappedRange& lhs, const MappedRange& rhs) {
+ if (lhs.rva_transformed < rhs.rva_transformed)
+ return true;
+ if (lhs.rva_transformed > rhs.rva_transformed)
+ return false;
+ return lhs.length < rhs.length;
+}
+
+// Used for searching through the EndpointIndexMap.
+bool EndpointIndexLess(const EndpointIndex& ei1, const EndpointIndex& ei2) {
+ return ei1.endpoint < ei2.endpoint;
+}
+
+// Finds the debug stream with the given |name| in the given |session|, and
+// populates |table| with its contents. Casts the data directly into OMAP
+// structs.
+bool FindAndLoadOmapTable(const wchar_t* name,
+ IDiaSession* session,
+ OmapTable* table) {
+ assert(name != NULL);
+ assert(session != NULL);
+ assert(table != NULL);
+
+ CComPtr<IDiaEnumDebugStreamData> stream;
+ if (!FindDebugStream(name, session, &stream))
+ return false;
+ assert(stream.p != NULL);
+
+ LONG count = 0;
+ if (FAILED(stream->get_Count(&count))) {
+ fprintf(stderr, "IDiaEnumDebugStreamData::get_Count failed for stream "
+ "\"%ws\"\n", name);
+ return false;
+ }
+
+ // Get the length of the stream in bytes.
+ DWORD bytes_read = 0;
+ ULONG count_read = 0;
+ if (FAILED(stream->Next(count, 0, &bytes_read, NULL, &count_read))) {
+ fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
+ "length of stream \"%ws\"\n", name);
+ return false;
+ }
+
+ // Ensure it's consistent with the OMAP data type.
+ DWORD bytes_expected = count * sizeof(OmapTable::value_type);
+ if (count * sizeof(OmapTable::value_type) != bytes_read) {
+ fprintf(stderr, "DIA debug stream \"%ws\" has an unexpected length", name);
+ return false;
+ }
+
+ // Read the table.
+ table->resize(count);
+ bytes_read = 0;
+ count_read = 0;
+ if (FAILED(stream->Next(count, bytes_expected, &bytes_read,
+ reinterpret_cast<BYTE*>(&table->at(0)),
+ &count_read))) {
+ fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
+ "data from stream \"%ws\"\n", name);
+ return false;
+ }
+
+ return true;
+}
+
+// This determines the original image length by looking through the segment
+// table.
+bool GetOriginalImageLength(IDiaSession* session, DWORD* image_length) {
+ assert(session != NULL);
+ assert(image_length != NULL);
+
+ CComPtr<IDiaEnumSegments> enum_segments;
+ if (!FindTable(session, &enum_segments))
+ return false;
+ assert(enum_segments.p != NULL);
+
+ DWORD temp_image_length = 0;
+ CComPtr<IDiaSegment> segment;
+ ULONG fetched = 0;
+ while (SUCCEEDED(enum_segments->Next(1, &segment, &fetched)) &&
+ fetched == 1) {
+ assert(segment.p != NULL);
+
+ DWORD rva = 0;
+ DWORD length = 0;
+ DWORD frame = 0;
+ if (FAILED(segment->get_relativeVirtualAddress(&rva)) ||
+ FAILED(segment->get_length(&length)) ||
+ FAILED(segment->get_frame(&frame))) {
+ fprintf(stderr, "Failed to get basic properties for IDiaSegment\n");
+ return false;
+ }
+
+ if (frame > 0) {
+ DWORD segment_end = rva + length;
+ if (segment_end > temp_image_length)
+ temp_image_length = segment_end;
+ }
+ segment.Release();
+ }
+
+ *image_length = temp_image_length;
+ return true;
+}
+
+// Detects regions of the original image that have been removed in the
+// transformed image, and sets the 'removed' property on all mapped ranges
+// immediately preceding a gap. The mapped ranges must be sorted by
+// 'rva_original'.
+void FillInRemovedLengths(Mapping* mapping) {
+ assert(mapping != NULL);
+
+ // Find and fill gaps. We do this with two sweeps. We first sweep forward
+ // looking for gaps. When we identify a gap we then sweep forward with a
+ // second scan and set the 'removed' property for any intervals that
+ // immediately precede the gap.
+ //
+ // Gaps are typically between two successive intervals, but not always:
+ //
+ // Range 1: ---------------
+ // Range 2: -------
+ // Range 3: -------------
+ // Gap : ******
+ //
+ // In the above example the gap is between range 1 and range 3. A forward
+ // sweep finds the gap, and a second forward sweep identifies that range 1
+ // immediately precedes the gap and sets its 'removed' property.
+
+ size_t fill = 0;
+ DWORD rva_front = 0;
+ for (size_t find = 0; find < mapping->size(); ++find) {
+#ifndef NDEBUG
+ // We expect the mapped ranges to be sorted by 'rva_original'.
+ if (find > 0) {
+ assert(mapping->at(find - 1).rva_original <=
+ mapping->at(find).rva_original);
+ }
+#endif
+
+ if (rva_front < mapping->at(find).rva_original) {
+ // We've found a gap. Fill it in by setting the 'removed' property for
+ // any affected intervals.
+ DWORD removed = mapping->at(find).rva_original - rva_front;
+ for (; fill < find; ++fill) {
+ if (mapping->at(fill).rva_original + mapping->at(fill).length !=
+ rva_front) {
+ continue;
+ }
+
+ // This interval ends right where the gap starts. It needs to have its
+ // 'removed' information filled in.
+ mapping->at(fill).removed = removed;
+ }
+ }
+
+ // Advance the front that indicates the covered portion of the image.
+ rva_front = mapping->at(find).rva_original + mapping->at(find).length;
+ }
+}
+
+// Builds a unified view of the mapping between the original and transformed
+// image space by merging OMAPTO and OMAPFROM data.
+void BuildMapping(const OmapData& omap_data, Mapping* mapping) {
+ assert(mapping != NULL);
+
+ mapping->clear();
+
+ if (omap_data.omap_from.empty() || omap_data.omap_to.empty())
+ return;
+
+ // The names 'omap_to' and 'omap_from' are awfully confusing, so we make
+ // ourselves more explicit here. This cast is only safe because the underlying
+ // types have the exact same size.
+ const OmapToTable& tran2orig =
+ reinterpret_cast<const OmapToTable&>(omap_data.omap_to);
+ const OmapFromTable& orig2tran = reinterpret_cast<const OmapFromTable&>(
+ omap_data.omap_from);
+
+ // Handle the range of data at the beginning of the image. This is not usually
+ // specified by the OMAP data.
+ if (tran2orig[0].rva_transformed > 0 && orig2tran[0].rva_original > 0) {
+ DWORD header_transformed = tran2orig[0].rva_transformed;
+ DWORD header_original = orig2tran[0].rva_original;
+ DWORD header = Min(header_transformed, header_original);
+
+ MappedRange mr = {};
+ mr.length = header;
+ mr.injected = header_transformed - header;
+ mr.removed = header_original - header;
+ mapping->push_back(mr);
+ }
+
+ // Convert the implied lengths to explicit lengths, and infer which content
+ // has been injected into the transformed image. Injected content is inferred
+ // as regions of the transformed address space that does not map back to
+ // known valid content in the original image.
+ for (size_t i = 0; i < tran2orig.size(); ++i) {
+ const OmapTranToOrig& o1 = tran2orig[i];
+
+ // This maps to content that is outside the original image, thus it
+ // describes injected content. We can skip this entry.
+ if (o1.rva_original >= omap_data.length_original)
+ continue;
+
+ // Calculate the length of the current OMAP entry. This is implicit as the
+ // distance between successive |rva| values, capped at the end of the
+ // original image.
+ DWORD length = 0;
+ if (i + 1 < tran2orig.size()) {
+ const OmapTranToOrig& o2 = tran2orig[i + 1];
+
+ // We expect the table to be sorted by rva_transformed.
+ assert(o1.rva_transformed <= o2.rva_transformed);
+
+ length = o2.rva_transformed - o1.rva_transformed;
+ if (o1.rva_original + length > omap_data.length_original) {
+ length = omap_data.length_original - o1.rva_original;
+ }
+ } else {
+ length = omap_data.length_original - o1.rva_original;
+ }
+
+ // Zero-length entries don't describe anything and can be ignored.
+ if (length == 0)
+ continue;
+
+ // Any gaps in the transformed address-space are due to injected content.
+ if (!mapping->empty()) {
+ MappedRange& prev_mr = mapping->back();
+ prev_mr.injected += o1.rva_transformed -
+ (prev_mr.rva_transformed + prev_mr.length);
+ }
+
+ MappedRange mr = {};
+ mr.rva_original = o1.rva_original;
+ mr.rva_transformed = o1.rva_transformed;
+ mr.length = length;
+ mapping->push_back(mr);
+ }
+
+ // Sort based on the original image addresses.
+ std::sort(mapping->begin(), mapping->end(), MappedRangeOriginalLess);
+
+ // Fill in the 'removed' lengths by looking for gaps in the coverage of the
+ // original address space.
+ FillInRemovedLengths(mapping);
+
+ return;
+}
+
+void BuildEndpointIndexMap(ImageMap* image_map) {
+ assert(image_map != NULL);
+
+ if (image_map->mapping.size() == 0)
+ return;
+
+ const Mapping& mapping = image_map->mapping;
+ EndpointIndexMap& eim = image_map->endpoint_index_map;
+
+ // Get the unique set of interval endpoints.
+ std::set<DWORD> endpoints;
+ for (size_t i = 0; i < mapping.size(); ++i) {
+ endpoints.insert(mapping[i].rva_original);
+ endpoints.insert(mapping[i].rva_original +
+ mapping[i].length +
+ mapping[i].removed);
+ }
+
+ // Use the endpoints to initialize the secondary search structure for the
+ // mapping.
+ eim.resize(endpoints.size());
+ std::set<DWORD>::const_iterator it = endpoints.begin();
+ for (size_t i = 0; it != endpoints.end(); ++it, ++i) {
+ eim[i].endpoint = *it;
+ eim[i].index = mapping.size();
+ }
+
+ // For each endpoint we want the smallest index of any interval containing
+ // it. We iterate over the intervals and update the indices associated with
+ // each interval endpoint contained in the current interval. In the general
+ // case of an arbitrary set of intervals this is O(n^2), but the structure of
+ // OMAP data makes this O(n).
+ for (size_t i = 0; i < mapping.size(); ++i) {
+ EndpointIndex ei1 = { mapping[i].rva_original, 0 };
+ EndpointIndexMap::iterator it1 = std::lower_bound(
+ eim.begin(), eim.end(), ei1, EndpointIndexLess);
+
+ EndpointIndex ei2 = { mapping[i].rva_original + mapping[i].length +
+ mapping[i].removed, 0 };
+ EndpointIndexMap::iterator it2 = std::lower_bound(
+ eim.begin(), eim.end(), ei2, EndpointIndexLess);
+
+ for (; it1 != it2; ++it1)
+ it1->index = Min(i, it1->index);
+ }
+}
+
+void BuildSubsequentRVAMap(const OmapData &omap_data,
+ std::map<DWORD, DWORD> *subsequent) {
+ assert(subsequent->empty());
+ const OmapFromTable &orig2tran =
+ reinterpret_cast<const OmapFromTable &>(omap_data.omap_from);
+
+ if (orig2tran.empty())
+ return;
+
+ for (size_t i = 0; i < orig2tran.size() - 1; ++i) {
+ // Expect that orig2tran is sorted.
+ if (orig2tran[i].rva_original >= orig2tran[i + 1].rva_original) {
+ fprintf(stderr, "OMAP 'from' table unexpectedly unsorted\n");
+ subsequent->clear();
+ return;
+ }
+ subsequent->insert(std::make_pair(orig2tran[i].rva_original,
+ orig2tran[i + 1].rva_original));
+ }
+}
+
+// Clips the given mapped range.
+void ClipMappedRangeOriginal(const AddressRange& clip_range,
+ MappedRange* mapped_range) {
+ assert(mapped_range != NULL);
+
+ // The clipping range is entirely outside of the mapped range.
+ if (clip_range.end() <= mapped_range->rva_original ||
+ mapped_range->rva_original + mapped_range->length +
+ mapped_range->removed <= clip_range.rva) {
+ mapped_range->length = 0;
+ mapped_range->injected = 0;
+ mapped_range->removed = 0;
+ return;
+ }
+
+ // Clip the left side.
+ if (mapped_range->rva_original < clip_range.rva) {
+ DWORD clip_left = clip_range.rva - mapped_range->rva_original;
+ mapped_range->rva_original += clip_left;
+ mapped_range->rva_transformed += clip_left;
+
+ if (clip_left > mapped_range->length) {
+ // The left clipping boundary entirely erases the content section of the
+ // range.
+ DWORD trim = clip_left - mapped_range->length;
+ mapped_range->length = 0;
+ mapped_range->injected -= Min(trim, mapped_range->injected);
+ // We know that trim <= mapped_range->remove.
+ mapped_range->removed -= trim;
+ } else {
+ // The left clipping boundary removes some, but not all, of the content.
+ // As such it leaves the removed/injected component intact.
+ mapped_range->length -= clip_left;
+ }
+ }
+
+ // Clip the right side.
+ DWORD end_original = mapped_range->rva_original + mapped_range->length;
+ if (clip_range.end() < end_original) {
+ // The right clipping boundary lands in the 'content' section of the range,
+ // entirely clearing the injected/removed portion.
+ DWORD clip_right = end_original - clip_range.end();
+ mapped_range->length -= clip_right;
+ mapped_range->injected = 0;
+ mapped_range->removed = 0;
+ return;
+ } else {
+ // The right clipping boundary is outside of the content, but may affect
+ // the removed/injected portion of the range.
+ DWORD end_removed = end_original + mapped_range->removed;
+ if (clip_range.end() < end_removed)
+ mapped_range->removed = clip_range.end() - end_original;
+
+ DWORD end_injected = end_original + mapped_range->injected;
+ if (clip_range.end() < end_injected)
+ mapped_range->injected = clip_range.end() - end_original;
+ }
+
+ return;
+}
+
+} // namespace
+
+int AddressRange::Compare(const AddressRange& rhs) const {
+ if (end() <= rhs.rva)
+ return -1;
+ if (rhs.end() <= rva)
+ return 1;
+ return 0;
+}
+
+bool GetOmapDataAndDisableTranslation(IDiaSession* session,
+ OmapData* omap_data) {
+ assert(session != NULL);
+ assert(omap_data != NULL);
+
+ CComPtr<IDiaAddressMap> address_map;
+ if (FAILED(session->QueryInterface(&address_map))) {
+ fprintf(stderr, "IDiaSession::QueryInterface(IDiaAddressMap) failed\n");
+ return false;
+ }
+ assert(address_map.p != NULL);
+
+ BOOL omap_enabled = FALSE;
+ if (FAILED(address_map->get_addressMapEnabled(&omap_enabled))) {
+ fprintf(stderr, "IDiaAddressMap::get_addressMapEnabled failed\n");
+ return false;
+ }
+
+ if (!omap_enabled) {
+ // We indicate the non-presence of OMAP data by returning empty tables.
+ omap_data->omap_from.clear();
+ omap_data->omap_to.clear();
+ omap_data->length_original = 0;
+ return true;
+ }
+
+ // OMAP data is present. Disable translation.
+ if (FAILED(address_map->put_addressMapEnabled(FALSE))) {
+ fprintf(stderr, "IDiaAddressMap::put_addressMapEnabled failed\n");
+ return false;
+ }
+
+ // Read the OMAP streams.
+ if (!FindAndLoadOmapTable(kOmapFromDebugStreamName,
+ session,
+ &omap_data->omap_from)) {
+ return false;
+ }
+ if (!FindAndLoadOmapTable(kOmapToDebugStreamName,
+ session,
+ &omap_data->omap_to)) {
+ return false;
+ }
+
+ // Get the lengths of the address spaces.
+ if (!GetOriginalImageLength(session, &omap_data->length_original))
+ return false;
+
+ return true;
+}
+
+void BuildImageMap(const OmapData& omap_data, ImageMap* image_map) {
+ assert(image_map != NULL);
+
+ BuildMapping(omap_data, &image_map->mapping);
+ BuildEndpointIndexMap(image_map);
+ BuildSubsequentRVAMap(omap_data, &image_map->subsequent_rva_block);
+}
+
+void MapAddressRange(const ImageMap& image_map,
+ const AddressRange& original_range,
+ AddressRangeVector* mapped_ranges) {
+ assert(mapped_ranges != NULL);
+
+ const Mapping& map = image_map.mapping;
+
+ // Handle the trivial case of an empty image_map. This means that there is
+ // no transformation to be applied, and we can simply return the original
+ // range.
+ if (map.empty()) {
+ mapped_ranges->push_back(original_range);
+ return;
+ }
+
+ // If we get a query of length 0 we need to handle it by using a non-zero
+ // query length.
+ AddressRange query_range(original_range);
+ if (query_range.length == 0)
+ query_range.length = 1;
+
+ // Find the range of intervals that can potentially intersect our query range.
+ size_t imin = 0;
+ size_t imax = 0;
+ {
+ // The index of the earliest possible range that can affect is us done by
+ // searching through the secondary indexing structure.
+ const EndpointIndexMap& eim = image_map.endpoint_index_map;
+ EndpointIndex q1 = { query_range.rva, 0 };
+ EndpointIndexMap::const_iterator it1 = std::lower_bound(
+ eim.begin(), eim.end(), q1, EndpointIndexLess);
+ if (it1 == eim.end()) {
+ imin = map.size();
+ } else {
+ // Backup to find the interval that contains our query point.
+ if (it1 != eim.begin() && query_range.rva < it1->endpoint)
+ --it1;
+ imin = it1->index;
+ }
+
+ // The first range that can't possibly intersect us is found by searching
+ // through the image map directly as it is already sorted by interval start
+ // point.
+ MappedRange q2 = { query_range.end(), 0 };
+ Mapping::const_iterator it2 = std::lower_bound(
+ map.begin(), map.end(), q2, MappedRangeOriginalLess);
+ imax = it2 - map.begin();
+ }
+
+ // Find all intervals that intersect the query range.
+ Mapping temp_map;
+ for (size_t i = imin; i < imax; ++i) {
+ MappedRange mr = map[i];
+ ClipMappedRangeOriginal(query_range, &mr);
+ if (mr.length + mr.injected > 0)
+ temp_map.push_back(mr);
+ }
+
+ // If there are no intersecting ranges then the query range has been removed
+ // from the image in question.
+ if (temp_map.empty())
+ return;
+
+ // Sort based on transformed addresses.
+ std::sort(temp_map.begin(), temp_map.end(), MappedRangeMappedLess);
+
+ // Zero-length queries can't actually be merged. We simply output the set of
+ // unique RVAs that correspond to the query RVA.
+ if (original_range.length == 0) {
+ mapped_ranges->push_back(AddressRange(temp_map[0].rva_transformed, 0));
+ for (size_t i = 1; i < temp_map.size(); ++i) {
+ if (temp_map[i].rva_transformed > mapped_ranges->back().rva)
+ mapped_ranges->push_back(AddressRange(temp_map[i].rva_transformed, 0));
+ }
+ return;
+ }
+
+ // Merge any ranges that are consecutive in the mapped image. We merge over
+ // injected content if it makes ranges contiguous, but we ignore any injected
+ // content at the tail end of a range. This allows us to detect symbols that
+ // have been lengthened by injecting content in the middle. However, it
+ // misses the case where content has been injected at the head or the tail.
+ // The problem is that it doesn't know whether to attribute it to the
+ // preceding or following symbol. It is up to the author of the transform to
+ // output explicit OMAP info in these cases to ensure full coverage of the
+ // transformed address space.
+ DWORD rva_begin = temp_map[0].rva_transformed;
+ DWORD rva_cur_content = rva_begin + temp_map[0].length;
+ DWORD rva_cur_injected = rva_cur_content + temp_map[0].injected;
+ for (size_t i = 1; i < temp_map.size(); ++i) {
+ if (rva_cur_injected < temp_map[i].rva_transformed) {
+ // This marks the end of a continuous range in the image. Output the
+ // current range and start a new one.
+ if (rva_begin < rva_cur_content) {
+ mapped_ranges->push_back(
+ AddressRange(rva_begin, rva_cur_content - rva_begin));
+ }
+ rva_begin = temp_map[i].rva_transformed;
+ }
+
+ rva_cur_content = temp_map[i].rva_transformed + temp_map[i].length;
+ rva_cur_injected = rva_cur_content + temp_map[i].injected;
+ }
+
+ // Output the range in progress.
+ if (rva_begin < rva_cur_content) {
+ mapped_ranges->push_back(
+ AddressRange(rva_begin, rva_cur_content - rva_begin));
+ }
+
+ return;
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/windows/omap.h b/3rdParty/Breakpad/src/common/windows/omap.h
new file mode 100644
index 0000000..bc293af
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/omap.h
@@ -0,0 +1,72 @@
+// Copyright 2013 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Provides an API for mapping symbols through OMAP information, if a PDB file
+// is augmented with it. This allows breakpad to work with addresses in
+// transformed images by transforming the symbols themselves, rather than
+// transforming addresses prior to querying symbols (the way it is typically
+// done by Windows-native tools, including the DIA).
+
+#ifndef COMMON_WINDOWS_OMAP_H_
+#define COMMON_WINDOWS_OMAP_H_
+
+#include "common/windows/omap_internal.h"
+
+namespace google_breakpad {
+
+// If the given session contains OMAP data this extracts it, populating
+// |omap_data|, and then disabling automatic translation for the session.
+// OMAP data is present in the PDB if |omap_data| is not empty. This returns
+// true on success, false otherwise.
+bool GetOmapDataAndDisableTranslation(IDiaSession* dia_session,
+ OmapData* omap_data);
+
+// Given raw OMAP data builds an ImageMap. This can be used to query individual
+// image ranges using MapAddressRange.
+// |omap_data|| is the OMAP data extracted from the PDB.
+// |image_map| will be populated with a description of the image mapping. If
+// |omap_data| is empty then this will also be empty.
+void BuildImageMap(const OmapData& omap_data, ImageMap* image_map);
+
+// Given an address range in the original image space determines how exactly it
+// has been tranformed.
+// |omap_data| is the OMAP data extracted from the PDB, which must not be
+// empty.
+// |original_range| is the address range in the original image being queried.
+// |mapped_ranges| will be populated with a full description of the mapping.
+// They may be disjoint in the transformed image so a vector is needed to
+// fully represent the mapping. This will be appended to if it is not
+// empty. If |omap_data| is empty then |mapped_ranges| will simply be
+// populated with a copy of |original_range| (the identity transform).
+void MapAddressRange(const ImageMap& image_map,
+ const AddressRange& original_range,
+ AddressRangeVector* mapped_ranges);
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_OMAP_H_
diff --git a/3rdParty/Breakpad/src/common/windows/omap_internal.h b/3rdParty/Breakpad/src/common/windows/omap_internal.h
new file mode 100644
index 0000000..2a4713d
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/omap_internal.h
@@ -0,0 +1,140 @@
+// Copyright 2013 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Declares internal implementation details for functionality in omap.h and
+// omap.cc.
+
+#ifndef COMMON_WINDOWS_OMAP_INTERNAL_H_
+#define COMMON_WINDOWS_OMAP_INTERNAL_H_
+
+#include <windows.h>
+#include <dia2.h>
+
+#include <map>
+#include <vector>
+
+namespace google_breakpad {
+
+// The OMAP struct is defined by debughlp.h, which doesn't play nicely with
+// imagehlp.h. We simply redefine it.
+struct OMAP {
+ DWORD rva;
+ DWORD rvaTo;
+};
+static_assert(sizeof(OMAP) == 8, "Wrong size for OMAP structure.");
+typedef std::vector<OMAP> OmapTable;
+
+// This contains the OMAP data extracted from an image.
+struct OmapData {
+ // The table of OMAP entries describing the transformation from the
+ // original image to the transformed image.
+ OmapTable omap_from;
+ // The table of OMAP entries describing the transformation from the
+ // instrumented image to the original image.
+ OmapTable omap_to;
+ // The length of the original untransformed image.
+ DWORD length_original;
+
+ OmapData() : length_original(0) { }
+};
+
+// This represents a range of addresses in an image.
+struct AddressRange {
+ DWORD rva;
+ DWORD length;
+
+ AddressRange() : rva(0), length(0) { }
+ AddressRange(DWORD rva, DWORD length) : rva(rva), length(length) { }
+
+ // Returns the end address of this range.
+ DWORD end() const { return rva + length; }
+
+ // Addreses only compare as less-than or greater-than if they are not
+ // overlapping. Otherwise, they compare equal.
+ int Compare(const AddressRange& rhs) const;
+ bool operator<(const AddressRange& rhs) const { return Compare(rhs) == -1; }
+ bool operator>(const AddressRange& rhs) const { return Compare(rhs) == 1; }
+
+ // Equality operators compare exact values.
+ bool operator==(const AddressRange& rhs) const {
+ return rva == rhs.rva && length == rhs.length;
+ }
+ bool operator!=(const AddressRange& rhs) const { return !((*this) == rhs); }
+};
+
+typedef std::vector<AddressRange> AddressRangeVector;
+
+// This represents an address range in an original image, and its corresponding
+// range in the transformed image.
+struct MappedRange {
+ // An address in the original image.
+ DWORD rva_original;
+ // The corresponding addresses in the transformed image.
+ DWORD rva_transformed;
+ // The length of the address range.
+ DWORD length;
+ // It is possible for code to be injected into a transformed image, for which
+ // there is no corresponding code in the original image. If this range of
+ // transformed image is immediately followed by such injected code we maintain
+ // a record of its length here.
+ DWORD injected;
+ // It is possible for code to be removed from the original image. This happens
+ // for things like padding between blocks. There is no actual content lost,
+ // but the spacing between items may be lost. This keeps track of any removed
+ // content immediately following the |original| range.
+ DWORD removed;
+};
+// A vector of mapped ranges is used as a more useful representation of
+// OMAP data.
+typedef std::vector<MappedRange> Mapping;
+
+// Used as a secondary search structure accompanying a Mapping.
+struct EndpointIndex {
+ DWORD endpoint;
+ size_t index;
+};
+typedef std::vector<EndpointIndex> EndpointIndexMap;
+
+// An ImageMap is vector of mapped ranges, plus a secondary index into it for
+// doing interval searches. (An interval tree would also work, but is overkill
+// because we don't need insertion and deletion.)
+struct ImageMap {
+ // This is a description of the mapping between original and transformed
+ // image, sorted by addresses in the original image.
+ Mapping mapping;
+ // For all interval endpoints in |mapping| this stores the minimum index of
+ // an interval in |mapping| that contains the endpoint. Useful for doing
+ // interval intersection queries.
+ EndpointIndexMap endpoint_index_map;
+
+ std::map<DWORD, DWORD> subsequent_rva_block;
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_OMAP_INTERNAL_H_
diff --git a/3rdParty/Breakpad/src/common/windows/omap_unittest.cc b/3rdParty/Breakpad/src/common/windows/omap_unittest.cc
new file mode 100644
index 0000000..b244334
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/omap_unittest.cc
@@ -0,0 +1,329 @@
+// Copyright 2013 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Unittests for OMAP related functions.
+
+#include "common/windows/omap.h"
+
+#include "breakpad_googletest_includes.h"
+
+namespace google_breakpad {
+
+// Equality operators for ContainerEq. These must be outside of the anonymous
+// namespace in order for them to be found.
+bool operator==(const MappedRange& mr1, const MappedRange& mr2) {
+ return mr1.rva_original == mr2.rva_original &&
+ mr1.rva_transformed == mr2.rva_transformed &&
+ mr1.length == mr2.length &&
+ mr1.injected == mr2.injected &&
+ mr1.removed == mr2.removed;
+}
+bool operator==(const EndpointIndex& ei1, const EndpointIndex& ei2) {
+ return ei1.endpoint == ei2.endpoint && ei1.index == ei2.index;
+}
+
+// Pretty printers for more meaningful error messages. Also need to be outside
+// the anonymous namespace.
+std::ostream& operator<<(std::ostream& os, const MappedRange& mr) {
+ os << "MappedRange(rva_original=" << mr.rva_original
+ << ", rva_transformed=" << mr.rva_transformed
+ << ", length=" << mr.length
+ << ", injected=" << mr.injected
+ << ", removed=" << mr.removed << ")";
+ return os;
+}
+std::ostream& operator<<(std::ostream& os, const EndpointIndex& ei) {
+ os << "EndpointIndex(endpoint=" << ei.endpoint
+ << ", index=" << ei.index << ")";
+ return os;
+}
+std::ostream& operator<<(std::ostream& os, const AddressRange& ar) {
+ os << "AddressRange(rva=" << ar.rva << ", length=" << ar.length << ")";
+ return os;
+}
+
+namespace {
+
+OMAP CreateOmap(DWORD rva, DWORD rvaTo) {
+ OMAP o = { rva, rvaTo };
+ return o;
+}
+
+MappedRange CreateMappedRange(DWORD rva_original,
+ DWORD rva_transformed,
+ DWORD length,
+ DWORD injected,
+ DWORD removed) {
+ MappedRange mr = { rva_original, rva_transformed, length, injected, removed };
+ return mr;
+}
+
+EndpointIndex CreateEndpointIndex(DWORD endpoint, size_t index) {
+ EndpointIndex ei = { endpoint, index };
+ return ei;
+}
+
+// (C is removed)
+// Original : A B C D E F G H
+// Transformed: A B D F E * H1 G1 G2 H2
+// (* is injected, G is copied, H is split)
+// A is implied.
+
+// Layout of the original image.
+const AddressRange B(100, 15);
+const AddressRange C(B.end(), 10);
+const AddressRange D(C.end(), 25);
+const AddressRange E(D.end(), 10);
+const AddressRange F(E.end(), 40);
+const AddressRange G(F.end(), 3);
+const AddressRange H(G.end(), 7);
+
+// Layout of the transformed image.
+const AddressRange Bt(100, 15);
+const AddressRange Dt(Bt.end(), 20); // D is shortened.
+const AddressRange Ft(Dt.end(), F.length);
+const AddressRange Et(Ft.end(), E.length);
+const AddressRange injected(Et.end(), 5);
+const AddressRange H1t(injected.end(), 4); // H is split.
+const AddressRange G1t(H1t.end(), G.length); // G is copied.
+const AddressRange G2t(G1t.end(), G.length); // G is copied.
+const AddressRange H2t(G2t.end(), 3); // H is split.
+
+class BuildImageMapTest : public testing::Test {
+ public:
+ static const DWORD kInvalidAddress = 0xFFFFFFFF;
+
+ void InitOmapData() {
+ omap_data.length_original = H.end();
+
+ // Build the OMAPTO vector (from transformed to original).
+ omap_data.omap_to.push_back(CreateOmap(Bt.rva, B.rva));
+ omap_data.omap_to.push_back(CreateOmap(Dt.rva, D.rva));
+ omap_data.omap_to.push_back(CreateOmap(Ft.rva, F.rva));
+ omap_data.omap_to.push_back(CreateOmap(Et.rva, E.rva));
+ omap_data.omap_to.push_back(CreateOmap(injected.rva, kInvalidAddress));
+ omap_data.omap_to.push_back(CreateOmap(H1t.rva, H.rva));
+ omap_data.omap_to.push_back(CreateOmap(G1t.rva, G.rva));
+ omap_data.omap_to.push_back(CreateOmap(G2t.rva, G.rva));
+ omap_data.omap_to.push_back(CreateOmap(H2t.rva, H.rva + H1t.length));
+ omap_data.omap_to.push_back(CreateOmap(H2t.end(), kInvalidAddress));
+
+ // Build the OMAPFROM vector (from original to transformed).
+ omap_data.omap_from.push_back(CreateOmap(B.rva, Bt.rva));
+ omap_data.omap_from.push_back(CreateOmap(C.rva, kInvalidAddress));
+ omap_data.omap_from.push_back(CreateOmap(D.rva, Dt.rva));
+ omap_data.omap_from.push_back(CreateOmap(E.rva, Et.rva));
+ omap_data.omap_from.push_back(CreateOmap(F.rva, Ft.rva));
+ omap_data.omap_from.push_back(CreateOmap(G.rva, G1t.rva));
+ omap_data.omap_from.push_back(CreateOmap(H.rva, H1t.rva));
+ omap_data.omap_from.push_back(CreateOmap(H.rva + H1t.length, H2t.rva));
+ omap_data.omap_from.push_back(CreateOmap(H.end(), kInvalidAddress));
+ }
+
+ OmapData omap_data;
+};
+
+} // namespace
+
+TEST_F(BuildImageMapTest, EmptyImageMapOnEmptyOmapData) {
+ ASSERT_EQ(0u, omap_data.omap_from.size());
+ ASSERT_EQ(0u, omap_data.omap_to.size());
+ ASSERT_EQ(0u, omap_data.length_original);
+
+ ImageMap image_map;
+ BuildImageMap(omap_data, &image_map);
+ EXPECT_EQ(0u, image_map.mapping.size());
+ EXPECT_EQ(0u, image_map.endpoint_index_map.size());
+}
+
+TEST_F(BuildImageMapTest, ImageMapIsCorrect) {
+ InitOmapData();
+ ASSERT_LE(0u, omap_data.omap_from.size());
+ ASSERT_LE(0u, omap_data.omap_to.size());
+ ASSERT_LE(0u, omap_data.length_original);
+
+ ImageMap image_map;
+ BuildImageMap(omap_data, &image_map);
+ EXPECT_LE(9u, image_map.mapping.size());
+ EXPECT_LE(9u, image_map.endpoint_index_map.size());
+
+ Mapping mapping;
+ mapping.push_back(CreateMappedRange(0, 0, B.rva, 0, 0));
+ // C is removed, and it originally comes immediately after B.
+ mapping.push_back(CreateMappedRange(B.rva, Bt.rva, B.length, 0, C.length));
+ // D is shortened by a length of 5.
+ mapping.push_back(CreateMappedRange(D.rva, Dt.rva, Dt.length, 0, 5));
+ // The injected content comes immediately after E in the transformed image.
+ mapping.push_back(CreateMappedRange(E.rva, Et.rva, E.length, injected.length,
+ 0));
+ mapping.push_back(CreateMappedRange(F.rva, Ft.rva, F.length, 0, 0));
+ // G is copied so creates two entries.
+ mapping.push_back(CreateMappedRange(G.rva, G1t.rva, G.length, 0, 0));
+ mapping.push_back(CreateMappedRange(G.rva, G2t.rva, G.length, 0, 0));
+ // H is split, so create two entries.
+ mapping.push_back(CreateMappedRange(H.rva, H1t.rva, H1t.length, 0, 0));
+ mapping.push_back(CreateMappedRange(H.rva + H1t.length, H2t.rva, H2t.length,
+ 0, 0));
+ EXPECT_THAT(mapping,
+ testing::ContainerEq(image_map.mapping));
+
+ EndpointIndexMap endpoint_index_map;
+ endpoint_index_map.push_back(CreateEndpointIndex(0, 0));
+ endpoint_index_map.push_back(CreateEndpointIndex(B.rva, 1));
+ endpoint_index_map.push_back(CreateEndpointIndex(D.rva, 2));
+ endpoint_index_map.push_back(CreateEndpointIndex(E.rva, 3));
+ endpoint_index_map.push_back(CreateEndpointIndex(F.rva, 4));
+ // G is duplicated so 2 ranges map back to it, hence the skip from 5 to 7.
+ endpoint_index_map.push_back(CreateEndpointIndex(G.rva, 5));
+ // H is split so we expect 2 endpoints to show up attributed to it.
+ endpoint_index_map.push_back(CreateEndpointIndex(H.rva, 7));
+ endpoint_index_map.push_back(CreateEndpointIndex(H.rva + H1t.length, 8));
+ endpoint_index_map.push_back(CreateEndpointIndex(H.end(), 9));
+ EXPECT_THAT(endpoint_index_map,
+ testing::ContainerEq(image_map.endpoint_index_map));
+}
+
+namespace {
+
+class MapAddressRangeTest : public BuildImageMapTest {
+ public:
+ typedef BuildImageMapTest Super;
+ virtual void SetUp() {
+ Super::SetUp();
+ InitOmapData();
+ BuildImageMap(omap_data, &image_map);
+ }
+
+ ImageMap image_map;
+
+ private:
+ using BuildImageMapTest::InitOmapData;
+ using BuildImageMapTest::omap_data;
+};
+
+} // namespace
+
+TEST_F(MapAddressRangeTest, EmptyImageMapReturnsIdentity) {
+ ImageMap im;
+ AddressRangeVector mapped_ranges;
+ AddressRange ar(0, 1024);
+ MapAddressRange(im, ar, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_EQ(ar, mapped_ranges[0]);
+}
+
+TEST_F(MapAddressRangeTest, MapOutOfImage) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, AddressRange(H.end() + 10, 10), &mapped_ranges);
+ EXPECT_EQ(0u, mapped_ranges.size());
+}
+
+TEST_F(MapAddressRangeTest, MapIdentity) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, B, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(B));
+}
+
+TEST_F(MapAddressRangeTest, MapReorderedContiguous) {
+ AddressRangeVector mapped_ranges;
+
+ AddressRange DEF(D.rva, F.end() - D.rva);
+ MapAddressRange(image_map, DEF, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+
+ AddressRange DFEt(Dt.rva, Et.end() - Dt.rva);
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(DFEt));
+}
+
+TEST_F(MapAddressRangeTest, MapEmptySingle) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, AddressRange(D.rva, 0), &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(Dt.rva, 0)));
+}
+
+TEST_F(MapAddressRangeTest, MapEmptyCopied) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, AddressRange(G.rva, 0), &mapped_ranges);
+ EXPECT_EQ(2u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(G1t.rva, 0),
+ AddressRange(G2t.rva, 0)));
+}
+
+TEST_F(MapAddressRangeTest, MapCopiedContiguous) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, G, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(
+ AddressRange(G1t.rva, G2t.end() - G1t.rva)));
+}
+
+TEST_F(MapAddressRangeTest, MapSplitDiscontiguous) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, H, &mapped_ranges);
+ EXPECT_EQ(2u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(H1t, H2t));
+}
+
+TEST_F(MapAddressRangeTest, MapInjected) {
+ AddressRangeVector mapped_ranges;
+
+ AddressRange EFGH(E.rva, H.end() - E.rva);
+ MapAddressRange(image_map, EFGH, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+
+ AddressRange FEHGGHt(Ft.rva, H2t.end() - Ft.rva);
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(FEHGGHt));
+}
+
+TEST_F(MapAddressRangeTest, MapRemovedEntirely) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, C, &mapped_ranges);
+ EXPECT_EQ(0u, mapped_ranges.size());
+}
+
+TEST_F(MapAddressRangeTest, MapRemovedPartly) {
+ AddressRangeVector mapped_ranges;
+ MapAddressRange(image_map, D, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(Dt));
+}
+
+TEST_F(MapAddressRangeTest, MapFull) {
+ AddressRangeVector mapped_ranges;
+
+ AddressRange AH(0, H.end());
+ MapAddressRange(image_map, AH, &mapped_ranges);
+ EXPECT_EQ(1u, mapped_ranges.size());
+
+ AddressRange AHt(0, H2t.end());
+ EXPECT_THAT(mapped_ranges, testing::ElementsAre(AHt));
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.cc b/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.cc
new file mode 100644
index 0000000..31920ba
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.cc
@@ -0,0 +1,1412 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "common/windows/pdb_source_line_writer.h"
+
+#include <windows.h>
+#include <winnt.h>
+#include <atlbase.h>
+#include <dia2.h>
+#include <diacreate.h>
+#include <ImageHlp.h>
+#include <stdio.h>
+
+#include <limits>
+#include <set>
+
+#include "common/windows/dia_util.h"
+#include "common/windows/guid_string.h"
+#include "common/windows/string_utils-inl.h"
+
+// This constant may be missing from DbgHelp.h. See the documentation for
+// IDiaSymbol::get_undecoratedNameEx.
+#ifndef UNDNAME_NO_ECSU
+#define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union.
+#endif // UNDNAME_NO_ECSU
+
+/*
+ * Not defined in WinNT.h for some reason. Definitions taken from:
+ * http://uninformed.org/index.cgi?v=4&a=1&p=13
+ *
+ */
+typedef unsigned char UBYTE;
+
+#if !defined(_WIN64)
+#define UNW_FLAG_EHANDLER 0x01
+#define UNW_FLAG_UHANDLER 0x02
+#define UNW_FLAG_CHAININFO 0x04
+#endif
+
+union UnwindCode {
+ struct {
+ UBYTE offset_in_prolog;
+ UBYTE unwind_operation_code : 4;
+ UBYTE operation_info : 4;
+ };
+ USHORT frame_offset;
+};
+
+enum UnwindOperationCodes {
+ UWOP_PUSH_NONVOL = 0, /* info == register number */
+ UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
+ UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
+ UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
+ UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
+ UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
+ // XXX: these are missing from MSDN!
+ // See: http://www.osronline.com/ddkx/kmarch/64bitamd_4rs7.htm
+ UWOP_SAVE_XMM,
+ UWOP_SAVE_XMM_FAR,
+ UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */
+ UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
+ UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
+};
+
+// See: http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
+// Note: some fields removed as we don't use them.
+struct UnwindInfo {
+ UBYTE version : 3;
+ UBYTE flags : 5;
+ UBYTE size_of_prolog;
+ UBYTE count_of_codes;
+ UBYTE frame_register : 4;
+ UBYTE frame_offset : 4;
+ UnwindCode unwind_code[1];
+};
+
+namespace google_breakpad {
+
+namespace {
+
+using std::vector;
+
+// A helper class to scope a PLOADED_IMAGE.
+class AutoImage {
+ public:
+ explicit AutoImage(PLOADED_IMAGE img) : img_(img) {}
+ ~AutoImage() {
+ if (img_)
+ ImageUnload(img_);
+ }
+
+ operator PLOADED_IMAGE() { return img_; }
+ PLOADED_IMAGE operator->() { return img_; }
+
+ private:
+ PLOADED_IMAGE img_;
+};
+
+bool SymbolsMatch(IDiaSymbol* a, IDiaSymbol* b) {
+ DWORD a_section, a_offset, b_section, b_offset;
+ if (FAILED(a->get_addressSection(&a_section)) ||
+ FAILED(a->get_addressOffset(&a_offset)) ||
+ FAILED(b->get_addressSection(&b_section)) ||
+ FAILED(b->get_addressOffset(&b_offset)))
+ return false;
+ return a_section == b_section && a_offset == b_offset;
+}
+
+bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) {
+ if (SUCCEEDED(data_source.CoCreateInstance(CLSID_DiaSource))) {
+ return true;
+ }
+
+ class DECLSPEC_UUID("B86AE24D-BF2F-4ac9-B5A2-34B14E4CE11D") DiaSource100;
+ class DECLSPEC_UUID("761D3BCD-1304-41D5-94E8-EAC54E4AC172") DiaSource110;
+ class DECLSPEC_UUID("3BFCEA48-620F-4B6B-81F7-B9AF75454C7D") DiaSource120;
+ class DECLSPEC_UUID("E6756135-1E65-4D17-8576-610761398C3C") DiaSource140;
+
+ // If the CoCreateInstance call above failed, msdia*.dll is not registered.
+ // We can try loading the DLL corresponding to the #included DIA SDK, but
+ // the DIA headers don't provide a version. Lets try to figure out which DIA
+ // version we're compiling against by comparing CLSIDs.
+ const wchar_t *msdia_dll = nullptr;
+ if (CLSID_DiaSource == _uuidof(DiaSource100)) {
+ msdia_dll = L"msdia100.dll";
+ } else if (CLSID_DiaSource == _uuidof(DiaSource110)) {
+ msdia_dll = L"msdia110.dll";
+ } else if (CLSID_DiaSource == _uuidof(DiaSource120)) {
+ msdia_dll = L"msdia120.dll";
+ } else if (CLSID_DiaSource == _uuidof(DiaSource140)) {
+ msdia_dll = L"msdia140.dll";
+ }
+
+ if (msdia_dll &&
+ SUCCEEDED(NoRegCoCreate(msdia_dll, CLSID_DiaSource, IID_IDiaDataSource,
+ reinterpret_cast<void **>(&data_source)))) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) {
+}
+
+PDBSourceLineWriter::~PDBSourceLineWriter() {
+}
+
+bool PDBSourceLineWriter::SetCodeFile(const wstring &exe_file) {
+ if (code_file_.empty()) {
+ code_file_ = exe_file;
+ return true;
+ }
+ // Setting a different code file path is an error. It is success only if the
+ // file paths are the same.
+ return exe_file == code_file_;
+}
+
+bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) {
+ Close();
+ code_file_.clear();
+
+ if (FAILED(CoInitialize(NULL))) {
+ fprintf(stderr, "CoInitialize failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaDataSource> data_source;
+ if (!CreateDiaDataSourceInstance(data_source)) {
+ const int kGuidSize = 64;
+ wchar_t classid[kGuidSize] = {0};
+ StringFromGUID2(CLSID_DiaSource, classid, kGuidSize);
+ fprintf(stderr, "CoCreateInstance CLSID_DiaSource %S failed "
+ "(msdia*.dll unregistered?)\n", classid);
+ return false;
+ }
+
+ switch (format) {
+ case PDB_FILE:
+ if (FAILED(data_source->loadDataFromPdb(file.c_str()))) {
+ fprintf(stderr, "loadDataFromPdb failed for %ws\n", file.c_str());
+ return false;
+ }
+ break;
+ case EXE_FILE:
+ if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) {
+ fprintf(stderr, "loadDataForExe failed for %ws\n", file.c_str());
+ return false;
+ }
+ code_file_ = file;
+ break;
+ case ANY_FILE:
+ if (FAILED(data_source->loadDataFromPdb(file.c_str()))) {
+ if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) {
+ fprintf(stderr, "loadDataForPdb and loadDataFromExe failed for %ws\n",
+ file.c_str());
+ return false;
+ }
+ code_file_ = file;
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown file format\n");
+ return false;
+ }
+
+ if (FAILED(data_source->openSession(&session_))) {
+ fprintf(stderr, "openSession failed\n");
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) {
+ // The line number format is:
+ // <rva> <line number> <source file id>
+ CComPtr<IDiaLineNumber> line;
+ ULONG count;
+
+ while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) {
+ DWORD rva;
+ if (FAILED(line->get_relativeVirtualAddress(&rva))) {
+ fprintf(stderr, "failed to get line rva\n");
+ return false;
+ }
+
+ DWORD length;
+ if (FAILED(line->get_length(&length))) {
+ fprintf(stderr, "failed to get line code length\n");
+ return false;
+ }
+
+ DWORD dia_source_id;
+ if (FAILED(line->get_sourceFileId(&dia_source_id))) {
+ fprintf(stderr, "failed to get line source file id\n");
+ return false;
+ }
+ // duplicate file names are coalesced to share one ID
+ DWORD source_id = GetRealFileID(dia_source_id);
+
+ DWORD line_num;
+ if (FAILED(line->get_lineNumber(&line_num))) {
+ fprintf(stderr, "failed to get line number\n");
+ return false;
+ }
+
+ AddressRangeVector ranges;
+ MapAddressRange(image_map_, AddressRange(rva, length), &ranges);
+ for (size_t i = 0; i < ranges.size(); ++i) {
+ fprintf(output_, "%lx %lx %lu %lu\n", ranges[i].rva, ranges[i].length,
+ line_num, source_id);
+ }
+ line.Release();
+ }
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function,
+ IDiaSymbol *block) {
+ // The function format is:
+ // FUNC <address> <length> <param_stack_size> <function>
+ DWORD rva;
+ if (FAILED(block->get_relativeVirtualAddress(&rva))) {
+ fprintf(stderr, "couldn't get rva\n");
+ return false;
+ }
+
+ ULONGLONG length;
+ if (FAILED(block->get_length(&length))) {
+ fprintf(stderr, "failed to get function length\n");
+ return false;
+ }
+
+ if (length == 0) {
+ // Silently ignore zero-length functions, which can infrequently pop up.
+ return true;
+ }
+
+ CComBSTR name;
+ int stack_param_size;
+ if (!GetSymbolFunctionName(function, &name, &stack_param_size)) {
+ return false;
+ }
+
+ // If the decorated name didn't give the parameter size, try to
+ // calculate it.
+ if (stack_param_size < 0) {
+ stack_param_size = GetFunctionStackParamSize(function);
+ }
+
+ AddressRangeVector ranges;
+ MapAddressRange(image_map_, AddressRange(rva, static_cast<DWORD>(length)),
+ &ranges);
+ for (size_t i = 0; i < ranges.size(); ++i) {
+ fprintf(output_, "FUNC %lx %lx %x %ws\n",
+ ranges[i].rva, ranges[i].length, stack_param_size,
+ name.m_str);
+ }
+
+ CComPtr<IDiaEnumLineNumbers> lines;
+ if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) {
+ return false;
+ }
+
+ if (!PrintLines(lines)) {
+ return false;
+ }
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintSourceFiles() {
+ CComPtr<IDiaSymbol> global;
+ if (FAILED(session_->get_globalScope(&global))) {
+ fprintf(stderr, "get_globalScope failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaEnumSymbols> compilands;
+ if (FAILED(global->findChildren(SymTagCompiland, NULL,
+ nsNone, &compilands))) {
+ fprintf(stderr, "findChildren failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaSymbol> compiland;
+ ULONG count;
+ while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) {
+ CComPtr<IDiaEnumSourceFiles> source_files;
+ if (FAILED(session_->findFile(compiland, NULL, nsNone, &source_files))) {
+ return false;
+ }
+ CComPtr<IDiaSourceFile> file;
+ while (SUCCEEDED(source_files->Next(1, &file, &count)) && count == 1) {
+ DWORD file_id;
+ if (FAILED(file->get_uniqueId(&file_id))) {
+ return false;
+ }
+
+ CComBSTR file_name;
+ if (FAILED(file->get_fileName(&file_name))) {
+ return false;
+ }
+
+ wstring file_name_string(file_name);
+ if (!FileIDIsCached(file_name_string)) {
+ // this is a new file name, cache it and output a FILE line.
+ CacheFileID(file_name_string, file_id);
+ fwprintf(output_, L"FILE %d %ws\n", file_id, file_name_string.c_str());
+ } else {
+ // this file name has already been seen, just save this
+ // ID for later lookup.
+ StoreDuplicateFileID(file_name_string, file_id);
+ }
+ file.Release();
+ }
+ compiland.Release();
+ }
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFunctions() {
+ ULONG count = 0;
+ DWORD rva = 0;
+ CComPtr<IDiaSymbol> global;
+ HRESULT hr;
+
+ if (FAILED(session_->get_globalScope(&global))) {
+ fprintf(stderr, "get_globalScope failed\n");
+ return false;
+ }
+
+ CComPtr<IDiaEnumSymbols> symbols = NULL;
+
+ // Find all function symbols first.
+ std::set<DWORD> rvas;
+ hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols);
+
+ if (SUCCEEDED(hr)) {
+ CComPtr<IDiaSymbol> symbol = NULL;
+
+ while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
+ if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
+ // To maintain existing behavior of one symbol per address, place the
+ // rva onto a set here to uniquify them.
+ rvas.insert(rva);
+ } else {
+ fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
+ return false;
+ }
+
+ symbol.Release();
+ }
+
+ symbols.Release();
+ }
+
+ // Find all public symbols. Store public symbols that are not also private
+ // symbols for later.
+ std::set<DWORD> public_only_rvas;
+ hr = global->findChildren(SymTagPublicSymbol, NULL, nsNone, &symbols);
+
+ if (SUCCEEDED(hr)) {
+ CComPtr<IDiaSymbol> symbol = NULL;
+
+ while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
+ if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
+ if (rvas.count(rva) == 0) {
+ rvas.insert(rva); // Keep symbols in rva order.
+ public_only_rvas.insert(rva);
+ }
+ } else {
+ fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
+ return false;
+ }
+
+ symbol.Release();
+ }
+
+ symbols.Release();
+ }
+
+ std::set<DWORD>::iterator it;
+
+ // For each rva, dump the first symbol DIA knows about at the address.
+ for (it = rvas.begin(); it != rvas.end(); ++it) {
+ CComPtr<IDiaSymbol> symbol = NULL;
+ // If the symbol is not in the public list, look for SymTagFunction. This is
+ // a workaround to a bug where DIA will hang if searching for a private
+ // symbol at an address where only a public symbol exists.
+ // See http://connect.microsoft.com/VisualStudio/feedback/details/722366
+ if (public_only_rvas.count(*it) == 0) {
+ if (SUCCEEDED(session_->findSymbolByRVA(*it, SymTagFunction, &symbol))) {
+ // Sometimes findSymbolByRVA returns S_OK, but NULL.
+ if (symbol) {
+ if (!PrintFunction(symbol, symbol))
+ return false;
+ symbol.Release();
+ }
+ } else {
+ fprintf(stderr, "findSymbolByRVA SymTagFunction failed\n");
+ return false;
+ }
+ } else if (SUCCEEDED(session_->findSymbolByRVA(*it,
+ SymTagPublicSymbol,
+ &symbol))) {
+ // Sometimes findSymbolByRVA returns S_OK, but NULL.
+ if (symbol) {
+ if (!PrintCodePublicSymbol(symbol))
+ return false;
+ symbol.Release();
+ }
+ } else {
+ fprintf(stderr, "findSymbolByRVA SymTagPublicSymbol failed\n");
+ return false;
+ }
+ }
+
+ // When building with PGO, the compiler can split functions into
+ // "hot" and "cold" blocks, and move the "cold" blocks out to separate
+ // pages, so the function can be noncontiguous. To find these blocks,
+ // we have to iterate over all the compilands, and then find blocks
+ // that are children of them. We can then find the lexical parents
+ // of those blocks and print out an extra FUNC line for blocks
+ // that are not contained in their parent functions.
+ CComPtr<IDiaEnumSymbols> compilands;
+ if (FAILED(global->findChildren(SymTagCompiland, NULL,
+ nsNone, &compilands))) {
+ fprintf(stderr, "findChildren failed on the global\n");
+ return false;
+ }
+
+ CComPtr<IDiaSymbol> compiland;
+ while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) {
+ CComPtr<IDiaEnumSymbols> blocks;
+ if (FAILED(compiland->findChildren(SymTagBlock, NULL,
+ nsNone, &blocks))) {
+ fprintf(stderr, "findChildren failed on a compiland\n");
+ return false;
+ }
+
+ CComPtr<IDiaSymbol> block;
+ while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) {
+ // find this block's lexical parent function
+ CComPtr<IDiaSymbol> parent;
+ DWORD tag;
+ if (SUCCEEDED(block->get_lexicalParent(&parent)) &&
+ SUCCEEDED(parent->get_symTag(&tag)) &&
+ tag == SymTagFunction) {
+ // now get the block's offset and the function's offset and size,
+ // and determine if the block is outside of the function
+ DWORD func_rva, block_rva;
+ ULONGLONG func_length;
+ if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) &&
+ SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) &&
+ SUCCEEDED(parent->get_length(&func_length))) {
+ if (block_rva < func_rva || block_rva > (func_rva + func_length)) {
+ if (!PrintFunction(parent, block)) {
+ return false;
+ }
+ }
+ }
+ }
+ parent.Release();
+ block.Release();
+ }
+ blocks.Release();
+ compiland.Release();
+ }
+
+ global.Release();
+ return true;
+}
+
+#undef max
+
+bool PDBSourceLineWriter::PrintFrameDataUsingPDB() {
+ // It would be nice if it were possible to output frame data alongside the
+ // associated function, as is done with line numbers, but the DIA API
+ // doesn't make it possible to get the frame data in that way.
+
+ CComPtr<IDiaEnumFrameData> frame_data_enum;
+ if (!FindTable(session_, &frame_data_enum))
+ return false;
+
+ DWORD last_type = std::numeric_limits<DWORD>::max();
+ DWORD last_rva = std::numeric_limits<DWORD>::max();
+ DWORD last_code_size = 0;
+ DWORD last_prolog_size = std::numeric_limits<DWORD>::max();
+
+ CComPtr<IDiaFrameData> frame_data;
+ ULONG count = 0;
+ while (SUCCEEDED(frame_data_enum->Next(1, &frame_data, &count)) &&
+ count == 1) {
+ DWORD type;
+ if (FAILED(frame_data->get_type(&type)))
+ return false;
+
+ DWORD rva;
+ if (FAILED(frame_data->get_relativeVirtualAddress(&rva)))
+ return false;
+
+ DWORD code_size;
+ if (FAILED(frame_data->get_lengthBlock(&code_size)))
+ return false;
+
+ DWORD prolog_size;
+ if (FAILED(frame_data->get_lengthProlog(&prolog_size)))
+ return false;
+
+ // parameter_size is the size of parameters passed on the stack. If any
+ // parameters are not passed on the stack (such as in registers), their
+ // sizes will not be included in parameter_size.
+ DWORD parameter_size;
+ if (FAILED(frame_data->get_lengthParams(&parameter_size)))
+ return false;
+
+ DWORD saved_register_size;
+ if (FAILED(frame_data->get_lengthSavedRegisters(&saved_register_size)))
+ return false;
+
+ DWORD local_size;
+ if (FAILED(frame_data->get_lengthLocals(&local_size)))
+ return false;
+
+ // get_maxStack can return S_FALSE, just use 0 in that case.
+ DWORD max_stack_size = 0;
+ if (FAILED(frame_data->get_maxStack(&max_stack_size)))
+ return false;
+
+ // get_programString can return S_FALSE, indicating that there is no
+ // program string. In that case, check whether %ebp is used.
+ HRESULT program_string_result;
+ CComBSTR program_string;
+ if (FAILED(program_string_result = frame_data->get_program(
+ &program_string))) {
+ return false;
+ }
+
+ // get_allocatesBasePointer can return S_FALSE, treat that as though
+ // %ebp is not used.
+ BOOL allocates_base_pointer = FALSE;
+ if (program_string_result != S_OK) {
+ if (FAILED(frame_data->get_allocatesBasePointer(
+ &allocates_base_pointer))) {
+ return false;
+ }
+ }
+
+ // Only print out a line if type, rva, code_size, or prolog_size have
+ // changed from the last line. It is surprisingly common (especially in
+ // system library PDBs) for DIA to return a series of identical
+ // IDiaFrameData objects. For kernel32.pdb from Windows XP SP2 on x86,
+ // this check reduces the size of the dumped symbol file by a third.
+ if (type != last_type || rva != last_rva || code_size != last_code_size ||
+ prolog_size != last_prolog_size) {
+ // The prolog and the code portions of the frame have to be treated
+ // independently as they may have independently changed in size, or may
+ // even have been split.
+ // NOTE: If epilog size is ever non-zero, we have to do something
+ // similar with it.
+
+ // Figure out where the prolog bytes have landed.
+ AddressRangeVector prolog_ranges;
+ if (prolog_size > 0) {
+ MapAddressRange(image_map_, AddressRange(rva, prolog_size),
+ &prolog_ranges);
+ }
+
+ // And figure out where the code bytes have landed.
+ AddressRangeVector code_ranges;
+ MapAddressRange(image_map_,
+ AddressRange(rva + prolog_size,
+ code_size - prolog_size),
+ &code_ranges);
+
+ struct FrameInfo {
+ DWORD rva;
+ DWORD code_size;
+ DWORD prolog_size;
+ };
+ std::vector<FrameInfo> frame_infos;
+
+ // Special case: The prolog and the code bytes remain contiguous. This is
+ // only done for compactness of the symbol file, and we could actually
+ // be outputting independent frame info for the prolog and code portions.
+ if (prolog_ranges.size() == 1 && code_ranges.size() == 1 &&
+ prolog_ranges[0].end() == code_ranges[0].rva) {
+ FrameInfo fi = { prolog_ranges[0].rva,
+ prolog_ranges[0].length + code_ranges[0].length,
+ prolog_ranges[0].length };
+ frame_infos.push_back(fi);
+ } else {
+ // Otherwise we output the prolog and code frame info independently.
+ for (size_t i = 0; i < prolog_ranges.size(); ++i) {
+ FrameInfo fi = { prolog_ranges[i].rva,
+ prolog_ranges[i].length,
+ prolog_ranges[i].length };
+ frame_infos.push_back(fi);
+ }
+ for (size_t i = 0; i < code_ranges.size(); ++i) {
+ FrameInfo fi = { code_ranges[i].rva, code_ranges[i].length, 0 };
+ frame_infos.push_back(fi);
+ }
+ }
+
+ for (size_t i = 0; i < frame_infos.size(); ++i) {
+ const FrameInfo& fi(frame_infos[i]);
+ fprintf(output_, "STACK WIN %lx %lx %lx %lx %x %lx %lx %lx %lx %d ",
+ type, fi.rva, fi.code_size, fi.prolog_size,
+ 0 /* epilog_size */, parameter_size, saved_register_size,
+ local_size, max_stack_size, program_string_result == S_OK);
+ if (program_string_result == S_OK) {
+ fprintf(output_, "%ws\n", program_string.m_str);
+ } else {
+ fprintf(output_, "%d\n", allocates_base_pointer);
+ }
+ }
+
+ last_type = type;
+ last_rva = rva;
+ last_code_size = code_size;
+ last_prolog_size = prolog_size;
+ }
+
+ frame_data.Release();
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFrameDataUsingEXE() {
+ if (code_file_.empty() && !FindPEFile()) {
+ fprintf(stderr, "Couldn't locate EXE or DLL file.\n");
+ return false;
+ }
+
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string code_file;
+ if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) {
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to load %s\n", code_file.c_str());
+ return false;
+ }
+ PIMAGE_OPTIONAL_HEADER64 optional_header =
+ &(reinterpret_cast<PIMAGE_NT_HEADERS64>(img->FileHeader))->OptionalHeader;
+ if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ fprintf(stderr, "Not a PE32+ image\n");
+ return false;
+ }
+
+ // Read Exception Directory
+ DWORD exception_rva = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
+ DWORD exception_size = optional_header->
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY funcs =
+ static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ exception_rva,
+ &img->LastRvaSection));
+ for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) {
+ DWORD unwind_rva = funcs[i].UnwindInfoAddress;
+ // handle chaining
+ while (unwind_rva & 0x1) {
+ unwind_rva ^= 0x1;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
+ static_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ unwind_rva,
+ &img->LastRvaSection));
+ unwind_rva = chained_func->UnwindInfoAddress;
+ }
+
+ UnwindInfo *unwind_info = static_cast<UnwindInfo *>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ unwind_rva,
+ &img->LastRvaSection));
+
+ DWORD stack_size = 8; // minimal stack size is 8 for RIP
+ DWORD rip_offset = 8;
+ do {
+ for (UBYTE c = 0; c < unwind_info->count_of_codes; c++) {
+ UnwindCode *unwind_code = &unwind_info->unwind_code[c];
+ switch (unwind_code->unwind_operation_code) {
+ case UWOP_PUSH_NONVOL: {
+ stack_size += 8;
+ break;
+ }
+ case UWOP_ALLOC_LARGE: {
+ if (unwind_code->operation_info == 0) {
+ c++;
+ if (c < unwind_info->count_of_codes)
+ stack_size += (unwind_code + 1)->frame_offset * 8;
+ } else {
+ c += 2;
+ if (c < unwind_info->count_of_codes)
+ stack_size += (unwind_code + 1)->frame_offset |
+ ((unwind_code + 2)->frame_offset << 16);
+ }
+ break;
+ }
+ case UWOP_ALLOC_SMALL: {
+ stack_size += unwind_code->operation_info * 8 + 8;
+ break;
+ }
+ case UWOP_SET_FPREG:
+ case UWOP_SAVE_XMM:
+ case UWOP_SAVE_XMM_FAR:
+ break;
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM128: {
+ c++; // skip slot with offset
+ break;
+ }
+ case UWOP_SAVE_NONVOL_FAR:
+ case UWOP_SAVE_XMM128_FAR: {
+ c += 2; // skip 2 slots with offset
+ break;
+ }
+ case UWOP_PUSH_MACHFRAME: {
+ if (unwind_code->operation_info) {
+ stack_size += 88;
+ } else {
+ stack_size += 80;
+ }
+ rip_offset += 80;
+ break;
+ }
+ }
+ }
+ if (unwind_info->flags & UNW_FLAG_CHAININFO) {
+ PIMAGE_RUNTIME_FUNCTION_ENTRY chained_func =
+ reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(
+ (unwind_info->unwind_code +
+ ((unwind_info->count_of_codes + 1) & ~1)));
+
+ unwind_info = static_cast<UnwindInfo *>(
+ ImageRvaToVa(img->FileHeader,
+ img->MappedAddress,
+ chained_func->UnwindInfoAddress,
+ &img->LastRvaSection));
+ } else {
+ unwind_info = NULL;
+ }
+ } while (unwind_info);
+ fprintf(output_, "STACK CFI INIT %lx %lx .cfa: $rsp .ra: .cfa %lu - ^\n",
+ funcs[i].BeginAddress,
+ funcs[i].EndAddress - funcs[i].BeginAddress, rip_offset);
+ fprintf(output_, "STACK CFI %lx .cfa: $rsp %lu +\n",
+ funcs[i].BeginAddress, stack_size);
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintFrameData() {
+ PDBModuleInfo info;
+ if (GetModuleInfo(&info) && info.cpu == L"x86_64") {
+ return PrintFrameDataUsingEXE();
+ } else {
+ return PrintFrameDataUsingPDB();
+ }
+ return false;
+}
+
+bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
+ BOOL is_code;
+ if (FAILED(symbol->get_code(&is_code))) {
+ return false;
+ }
+ if (!is_code) {
+ return true;
+ }
+
+ DWORD rva;
+ if (FAILED(symbol->get_relativeVirtualAddress(&rva))) {
+ return false;
+ }
+
+ CComBSTR name;
+ int stack_param_size;
+ if (!GetSymbolFunctionName(symbol, &name, &stack_param_size)) {
+ return false;
+ }
+
+ AddressRangeVector ranges;
+ MapAddressRange(image_map_, AddressRange(rva, 1), &ranges);
+ for (size_t i = 0; i < ranges.size(); ++i) {
+ fprintf(output_, "PUBLIC %lx %x %ws\n", ranges[i].rva,
+ stack_param_size > 0 ? stack_param_size : 0,
+ name.m_str);
+ }
+
+ // Now walk the function in the original untranslated space, asking DIA
+ // what function is at that location, stepping through OMAP blocks. If
+ // we're still in the same function, emit another entry, because the
+ // symbol could have been split into multiple pieces. If we've gotten to
+ // another symbol in the original address space, then we're done for
+ // this symbol. See https://crbug.com/678874.
+ for (;;) {
+ // This steps to the next block in the original image. Simply doing
+ // rva++ would also be correct, but would emit tons of unnecessary
+ // entries.
+ rva = image_map_.subsequent_rva_block[rva];
+ if (rva == 0)
+ break;
+
+ CComPtr<IDiaSymbol> next_sym = NULL;
+ LONG displacement;
+ if (FAILED(session_->findSymbolByRVAEx(rva, SymTagPublicSymbol, &next_sym,
+ &displacement))) {
+ break;
+ }
+
+ if (!SymbolsMatch(symbol, next_sym))
+ break;
+
+ AddressRangeVector next_ranges;
+ MapAddressRange(image_map_, AddressRange(rva, 1), &next_ranges);
+ for (size_t i = 0; i < next_ranges.size(); ++i) {
+ fprintf(output_, "PUBLIC %lx %x %ws\n", next_ranges[i].rva,
+ stack_param_size > 0 ? stack_param_size : 0, name.m_str);
+ }
+ }
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintPDBInfo() {
+ PDBModuleInfo info;
+ if (!GetModuleInfo(&info)) {
+ return false;
+ }
+
+ // Hard-code "windows" for the OS because that's the only thing that makes
+ // sense for PDB files. (This might not be strictly correct for Windows CE
+ // support, but we don't care about that at the moment.)
+ fprintf(output_, "MODULE windows %ws %ws %ws\n",
+ info.cpu.c_str(), info.debug_identifier.c_str(),
+ info.debug_file.c_str());
+
+ return true;
+}
+
+bool PDBSourceLineWriter::PrintPEInfo() {
+ PEModuleInfo info;
+ if (!GetPEInfo(&info)) {
+ return false;
+ }
+
+ fprintf(output_, "INFO CODE_ID %ws %ws\n",
+ info.code_identifier.c_str(),
+ info.code_file.c_str());
+ return true;
+}
+
+// wcstol_positive_strict is sort of like wcstol, but much stricter. string
+// should be a buffer pointing to a null-terminated string containing only
+// decimal digits. If the entire string can be converted to an integer
+// without overflowing, and there are no non-digit characters before the
+// result is set to the value and this function returns true. Otherwise,
+// this function returns false. This is an alternative to the strtol, atoi,
+// and scanf families, which are not as strict about input and in some cases
+// don't provide a good way for the caller to determine if a conversion was
+// successful.
+static bool wcstol_positive_strict(wchar_t *string, int *result) {
+ int value = 0;
+ for (wchar_t *c = string; *c != '\0'; ++c) {
+ int last_value = value;
+ value *= 10;
+ // Detect overflow.
+ if (value / 10 != last_value || value < 0) {
+ return false;
+ }
+ if (*c < '0' || *c > '9') {
+ return false;
+ }
+ unsigned int c_value = *c - '0';
+ last_value = value;
+ value += c_value;
+ // Detect overflow.
+ if (value < last_value) {
+ return false;
+ }
+ // Forbid leading zeroes unless the string is just "0".
+ if (value == 0 && *(c+1) != '\0') {
+ return false;
+ }
+ }
+ *result = value;
+ return true;
+}
+
+bool PDBSourceLineWriter::FindPEFile() {
+ CComPtr<IDiaSymbol> global;
+ if (FAILED(session_->get_globalScope(&global))) {
+ fprintf(stderr, "get_globalScope failed\n");
+ return false;
+ }
+
+ CComBSTR symbols_file;
+ if (SUCCEEDED(global->get_symbolsFileName(&symbols_file))) {
+ wstring file(symbols_file);
+
+ // Look for an EXE or DLL file.
+ const wchar_t *extensions[] = { L"exe", L"dll" };
+ for (size_t i = 0; i < sizeof(extensions) / sizeof(extensions[0]); i++) {
+ size_t dot_pos = file.find_last_of(L".");
+ if (dot_pos != wstring::npos) {
+ file.replace(dot_pos + 1, wstring::npos, extensions[i]);
+ // Check if this file exists.
+ if (GetFileAttributesW(file.c_str()) != INVALID_FILE_ATTRIBUTES) {
+ code_file_ = file;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+// static
+bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function,
+ BSTR *name,
+ int *stack_param_size) {
+ *stack_param_size = -1;
+ const DWORD undecorate_options = UNDNAME_NO_MS_KEYWORDS |
+ UNDNAME_NO_FUNCTION_RETURNS |
+ UNDNAME_NO_ALLOCATION_MODEL |
+ UNDNAME_NO_ALLOCATION_LANGUAGE |
+ UNDNAME_NO_THISTYPE |
+ UNDNAME_NO_ACCESS_SPECIFIERS |
+ UNDNAME_NO_THROW_SIGNATURES |
+ UNDNAME_NO_MEMBER_TYPE |
+ UNDNAME_NO_RETURN_UDT_MODEL |
+ UNDNAME_NO_ECSU;
+
+ // Use get_undecoratedNameEx to get readable C++ names with arguments.
+ if (function->get_undecoratedNameEx(undecorate_options, name) != S_OK) {
+ if (function->get_name(name) != S_OK) {
+ fprintf(stderr, "failed to get function name\n");
+ return false;
+ }
+
+ // It's possible for get_name to return an empty string, so
+ // special-case that.
+ if (wcscmp(*name, L"") == 0) {
+ SysFreeString(*name);
+ // dwarf_cu_to_module.cc uses "<name omitted>", so match that.
+ *name = SysAllocString(L"<name omitted>");
+ return true;
+ }
+
+ // If a name comes from get_name because no undecorated form existed,
+ // it's already formatted properly to be used as output. Don't do any
+ // additional processing.
+ //
+ // MSVC7's DIA seems to not undecorate names in as many cases as MSVC8's.
+ // This will result in calling get_name for some C++ symbols, so
+ // all of the parameter and return type information may not be included in
+ // the name string.
+ } else {
+ // C++ uses a bogus "void" argument for functions and methods that don't
+ // take any parameters. Take it out of the undecorated name because it's
+ // ugly and unnecessary.
+ const wchar_t *replace_string = L"(void)";
+ const size_t replace_length = wcslen(replace_string);
+ const wchar_t *replacement_string = L"()";
+ size_t length = wcslen(*name);
+ if (length >= replace_length) {
+ wchar_t *name_end = *name + length - replace_length;
+ if (wcscmp(name_end, replace_string) == 0) {
+ WindowsStringUtils::safe_wcscpy(name_end, replace_length,
+ replacement_string);
+ length = wcslen(*name);
+ }
+ }
+
+ // Undecorate names used for stdcall and fastcall. These names prefix
+ // the identifier with '_' (stdcall) or '@' (fastcall) and suffix it
+ // with '@' followed by the number of bytes of parameters, in decimal.
+ // If such a name is found, take note of the size and undecorate it.
+ // Only do this for names that aren't C++, which is determined based on
+ // whether the undecorated name contains any ':' or '(' characters.
+ if (!wcschr(*name, ':') && !wcschr(*name, '(') &&
+ (*name[0] == '_' || *name[0] == '@')) {
+ wchar_t *last_at = wcsrchr(*name + 1, '@');
+ if (last_at && wcstol_positive_strict(last_at + 1, stack_param_size)) {
+ // If this function adheres to the fastcall convention, it accepts up
+ // to the first 8 bytes of parameters in registers (%ecx and %edx).
+ // We're only interested in the stack space used for parameters, so
+ // so subtract 8 and don't let the size go below 0.
+ if (*name[0] == '@') {
+ if (*stack_param_size > 8) {
+ *stack_param_size -= 8;
+ } else {
+ *stack_param_size = 0;
+ }
+ }
+
+ // Undecorate the name by moving it one character to the left in its
+ // buffer, and terminating it where the last '@' had been.
+ WindowsStringUtils::safe_wcsncpy(*name, length,
+ *name + 1, last_at - *name - 1);
+ } else if (*name[0] == '_') {
+ // This symbol's name is encoded according to the cdecl rules. The
+ // name doesn't end in a '@' character followed by a decimal positive
+ // integer, so it's not a stdcall name. Strip off the leading
+ // underscore.
+ WindowsStringUtils::safe_wcsncpy(*name, length, *name + 1, length);
+ }
+ }
+ }
+
+ return true;
+}
+
+// static
+int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) {
+ // This implementation is highly x86-specific.
+
+ // Gather the symbols corresponding to data.
+ CComPtr<IDiaEnumSymbols> data_children;
+ if (FAILED(function->findChildren(SymTagData, NULL, nsNone,
+ &data_children))) {
+ return 0;
+ }
+
+ // lowest_base is the lowest %ebp-relative byte offset used for a parameter.
+ // highest_end is one greater than the highest offset (i.e. base + length).
+ // Stack parameters are assumed to be contiguous, because in reality, they
+ // are.
+ int lowest_base = INT_MAX;
+ int highest_end = INT_MIN;
+
+ CComPtr<IDiaSymbol> child;
+ DWORD count;
+ while (SUCCEEDED(data_children->Next(1, &child, &count)) && count == 1) {
+ // If any operation fails at this point, just proceed to the next child.
+ // Use the next_child label instead of continue because child needs to
+ // be released before it's reused. Declare constructable/destructable
+ // types early to avoid gotos that cross initializations.
+ CComPtr<IDiaSymbol> child_type;
+
+ // DataIsObjectPtr is only used for |this|. Because |this| can be passed
+ // as a stack parameter, look for it in addition to traditional
+ // parameters.
+ DWORD child_kind;
+ if (FAILED(child->get_dataKind(&child_kind)) ||
+ (child_kind != DataIsParam && child_kind != DataIsObjectPtr)) {
+ goto next_child;
+ }
+
+ // Only concentrate on register-relative parameters. Parameters may also
+ // be enregistered (passed directly in a register), but those don't
+ // consume any stack space, so they're not of interest.
+ DWORD child_location_type;
+ if (FAILED(child->get_locationType(&child_location_type)) ||
+ child_location_type != LocIsRegRel) {
+ goto next_child;
+ }
+
+ // Of register-relative parameters, the only ones that make any sense are
+ // %ebp- or %esp-relative. Note that MSVC's debugging information always
+ // gives parameters as %ebp-relative even when a function doesn't use a
+ // traditional frame pointer and stack parameters are accessed relative to
+ // %esp, so just look for %ebp-relative parameters. If you wanted to
+ // access parameters, you'd probably want to treat these %ebp-relative
+ // offsets as if they were relative to %esp before a function's prolog
+ // executed.
+ DWORD child_register;
+ if (FAILED(child->get_registerId(&child_register)) ||
+ child_register != CV_REG_EBP) {
+ goto next_child;
+ }
+
+ LONG child_register_offset;
+ if (FAILED(child->get_offset(&child_register_offset))) {
+ goto next_child;
+ }
+
+ // IDiaSymbol::get_type can succeed but still pass back a NULL value.
+ if (FAILED(child->get_type(&child_type)) || !child_type) {
+ goto next_child;
+ }
+
+ ULONGLONG child_length;
+ if (FAILED(child_type->get_length(&child_length))) {
+ goto next_child;
+ }
+
+ int child_end = child_register_offset + static_cast<ULONG>(child_length);
+ if (child_register_offset < lowest_base) {
+ lowest_base = child_register_offset;
+ }
+ if (child_end > highest_end) {
+ highest_end = child_end;
+ }
+
+next_child:
+ child.Release();
+ }
+
+ int param_size = 0;
+ // Make sure lowest_base isn't less than 4, because [%esp+4] is the lowest
+ // possible address to find a stack parameter before executing a function's
+ // prolog (see above). Some optimizations cause parameter offsets to be
+ // lower than 4, but we're not concerned with those because we're only
+ // looking for parameters contained in addresses higher than where the
+ // return address is stored.
+ if (lowest_base < 4) {
+ lowest_base = 4;
+ }
+ if (highest_end > lowest_base) {
+ // All stack parameters are pushed as at least 4-byte quantities. If the
+ // last type was narrower than 4 bytes, promote it. This assumes that all
+ // parameters' offsets are 4-byte-aligned, which is always the case. Only
+ // worry about the last type, because we're not summing the type sizes,
+ // just looking at the lowest and highest offsets.
+ int remainder = highest_end % 4;
+ if (remainder) {
+ highest_end += 4 - remainder;
+ }
+
+ param_size = highest_end - lowest_base;
+ }
+
+ return param_size;
+}
+
+bool PDBSourceLineWriter::WriteMap(FILE *map_file) {
+ output_ = map_file;
+
+ // Load the OMAP information, and disable auto-translation of addresses in
+ // preference of doing it ourselves.
+ OmapData omap_data;
+ if (!GetOmapDataAndDisableTranslation(session_, &omap_data))
+ return false;
+ BuildImageMap(omap_data, &image_map_);
+
+ bool ret = PrintPDBInfo();
+ // This is not a critical piece of the symbol file.
+ PrintPEInfo();
+ ret = ret &&
+ PrintSourceFiles() &&
+ PrintFunctions() &&
+ PrintFrameData();
+
+ output_ = NULL;
+ return ret;
+}
+
+void PDBSourceLineWriter::Close() {
+ session_.Release();
+}
+
+bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) {
+ if (!info) {
+ return false;
+ }
+
+ info->debug_file.clear();
+ info->debug_identifier.clear();
+ info->cpu.clear();
+
+ CComPtr<IDiaSymbol> global;
+ if (FAILED(session_->get_globalScope(&global))) {
+ return false;
+ }
+
+ DWORD machine_type;
+ // get_machineType can return S_FALSE.
+ if (global->get_machineType(&machine_type) == S_OK) {
+ // The documentation claims that get_machineType returns a value from
+ // the CV_CPU_TYPE_e enumeration, but that's not the case.
+ // Instead, it returns one of the IMAGE_FILE_MACHINE values as
+ // defined here:
+ // http://msdn.microsoft.com/en-us/library/ms680313%28VS.85%29.aspx
+ switch (machine_type) {
+ case IMAGE_FILE_MACHINE_I386:
+ info->cpu = L"x86";
+ break;
+ case IMAGE_FILE_MACHINE_AMD64:
+ info->cpu = L"x86_64";
+ break;
+ default:
+ info->cpu = L"unknown";
+ break;
+ }
+ } else {
+ // Unexpected, but handle gracefully.
+ info->cpu = L"unknown";
+ }
+
+ // DWORD* and int* are not compatible. This is clean and avoids a cast.
+ DWORD age;
+ if (FAILED(global->get_age(&age))) {
+ return false;
+ }
+
+ bool uses_guid;
+ if (!UsesGUID(&uses_guid)) {
+ return false;
+ }
+
+ if (uses_guid) {
+ GUID guid;
+ if (FAILED(global->get_guid(&guid))) {
+ return false;
+ }
+
+ // Use the same format that the MS symbol server uses in filesystem
+ // hierarchies.
+ wchar_t age_string[9];
+ swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]),
+ L"%x", age);
+
+ // remove when VC++7.1 is no longer supported
+ age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0';
+
+ info->debug_identifier = GUIDString::GUIDToSymbolServerWString(&guid);
+ info->debug_identifier.append(age_string);
+ } else {
+ DWORD signature;
+ if (FAILED(global->get_signature(&signature))) {
+ return false;
+ }
+
+ // Use the same format that the MS symbol server uses in filesystem
+ // hierarchies.
+ wchar_t identifier_string[17];
+ swprintf(identifier_string,
+ sizeof(identifier_string) / sizeof(identifier_string[0]),
+ L"%08X%x", signature, age);
+
+ // remove when VC++7.1 is no longer supported
+ identifier_string[sizeof(identifier_string) /
+ sizeof(identifier_string[0]) - 1] = L'\0';
+
+ info->debug_identifier = identifier_string;
+ }
+
+ CComBSTR debug_file_string;
+ if (FAILED(global->get_symbolsFileName(&debug_file_string))) {
+ return false;
+ }
+ info->debug_file =
+ WindowsStringUtils::GetBaseName(wstring(debug_file_string));
+
+ return true;
+}
+
+bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) {
+ if (!info) {
+ return false;
+ }
+
+ if (code_file_.empty() && !FindPEFile()) {
+ fprintf(stderr, "Couldn't locate EXE or DLL file.\n");
+ return false;
+ }
+
+ // Convert wchar to native charset because ImageLoad only takes
+ // a PSTR as input.
+ string code_file;
+ if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) {
+ return false;
+ }
+
+ AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL));
+ if (!img) {
+ fprintf(stderr, "Failed to open PE file: %s\n", code_file.c_str());
+ return false;
+ }
+
+ info->code_file = WindowsStringUtils::GetBaseName(code_file_);
+
+ // The date and time that the file was created by the linker.
+ DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp;
+ // The size of the file in bytes, including all headers.
+ DWORD SizeOfImage = 0;
+ PIMAGE_OPTIONAL_HEADER64 opt =
+ &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader;
+ if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ // 64-bit PE file.
+ SizeOfImage = opt->SizeOfImage;
+ } else {
+ // 32-bit PE file.
+ SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage;
+ }
+ wchar_t code_identifier[32];
+ swprintf(code_identifier,
+ sizeof(code_identifier) / sizeof(code_identifier[0]),
+ L"%08X%X", TimeDateStamp, SizeOfImage);
+ info->code_identifier = code_identifier;
+
+ return true;
+}
+
+bool PDBSourceLineWriter::UsesGUID(bool *uses_guid) {
+ if (!uses_guid)
+ return false;
+
+ CComPtr<IDiaSymbol> global;
+ if (FAILED(session_->get_globalScope(&global)))
+ return false;
+
+ GUID guid;
+ if (FAILED(global->get_guid(&guid)))
+ return false;
+
+ DWORD signature;
+ if (FAILED(global->get_signature(&signature)))
+ return false;
+
+ // There are two possibilities for guid: either it's a real 128-bit GUID
+ // as identified in a code module by a new-style CodeView record, or it's
+ // a 32-bit signature (timestamp) as identified by an old-style record.
+ // See MDCVInfoPDB70 and MDCVInfoPDB20 in minidump_format.h.
+ //
+ // Because DIA doesn't provide a way to directly determine whether a module
+ // uses a GUID or a 32-bit signature, this code checks whether the first 32
+ // bits of guid are the same as the signature, and if the rest of guid is
+ // zero. If so, then with a pretty high degree of certainty, there's an
+ // old-style CodeView record in use. This method will only falsely find an
+ // an old-style CodeView record if a real 128-bit GUID has its first 32
+ // bits set the same as the module's signature (timestamp) and the rest of
+ // the GUID is set to 0. This is highly unlikely.
+
+ GUID signature_guid = {signature}; // 0-initializes other members
+ *uses_guid = !IsEqualGUID(guid, signature_guid);
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.h b/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.h
new file mode 100644
index 0000000..e9e89bb
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/pdb_source_line_writer.h
@@ -0,0 +1,257 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// PDBSourceLineWriter uses a pdb file produced by Visual C++ to output
+// a line/address map for use with BasicSourceLineResolver.
+
+#ifndef COMMON_WINDOWS_PDB_SOURCE_LINE_WRITER_H_
+#define COMMON_WINDOWS_PDB_SOURCE_LINE_WRITER_H_
+
+#include <atlcomcli.h>
+
+#include <unordered_map>
+#include <string>
+
+#include "common/windows/omap.h"
+
+struct IDiaEnumLineNumbers;
+struct IDiaSession;
+struct IDiaSymbol;
+
+namespace google_breakpad {
+
+using std::wstring;
+using std::unordered_map;
+
+// A structure that carries information that identifies a pdb file.
+struct PDBModuleInfo {
+ public:
+ // The basename of the pdb file from which information was loaded.
+ wstring debug_file;
+
+ // The pdb's identifier. For recent pdb files, the identifier consists
+ // of the pdb's guid, in uppercase hexadecimal form without any dashes
+ // or separators, followed immediately by the pdb's age, also in
+ // uppercase hexadecimal form. For older pdb files which have no guid,
+ // the identifier is the pdb's 32-bit signature value, in zero-padded
+ // hexadecimal form, followed immediately by the pdb's age, in lowercase
+ // hexadecimal form.
+ wstring debug_identifier;
+
+ // A string identifying the cpu that the pdb is associated with.
+ // Currently, this may be "x86" or "unknown".
+ wstring cpu;
+};
+
+// A structure that carries information that identifies a PE file,
+// either an EXE or a DLL.
+struct PEModuleInfo {
+ // The basename of the PE file.
+ wstring code_file;
+
+ // The PE file's code identifier, which consists of its timestamp
+ // and file size concatenated together into a single hex string.
+ // (The fields IMAGE_OPTIONAL_HEADER::SizeOfImage and
+ // IMAGE_FILE_HEADER::TimeDateStamp, as defined in the ImageHlp
+ // documentation.) This is not well documented, if it's documented
+ // at all, but it's what symstore does and what DbgHelp supports.
+ wstring code_identifier;
+};
+
+class PDBSourceLineWriter {
+ public:
+ enum FileFormat {
+ PDB_FILE, // a .pdb file containing debug symbols
+ EXE_FILE, // a .exe or .dll file
+ ANY_FILE // try PDB_FILE and then EXE_FILE
+ };
+
+ explicit PDBSourceLineWriter();
+ ~PDBSourceLineWriter();
+
+ // Opens the given file. For executable files, the corresponding pdb
+ // file must be available; Open will be if it is not.
+ // If there is already a pdb file open, it is automatically closed.
+ // Returns true on success.
+ bool Open(const wstring &file, FileFormat format);
+
+ // Sets the code file full path. This is optional for 32-bit modules. It is
+ // also optional for 64-bit modules when there is an executable file stored
+ // in the same directory as the PDB file. It is only required for 64-bit
+ // modules when the executable file is not in the same location as the PDB
+ // file and it must be called after Open() and before WriteMap().
+ // If Open() was called for an executable file, then it is an error to call
+ // SetCodeFile() with a different file path and it will return false.
+ bool SetCodeFile(const wstring &exe_file);
+
+ // Writes a map file from the current pdb file to the given file stream.
+ // Returns true on success.
+ bool WriteMap(FILE *map_file);
+
+ // Closes the current pdb file and its associated resources.
+ void Close();
+
+ // Retrieves information about the module's debugging file. Returns
+ // true on success and false on failure.
+ bool GetModuleInfo(PDBModuleInfo *info);
+
+ // Retrieves information about the module's PE file. Returns
+ // true on success and false on failure.
+ bool GetPEInfo(PEModuleInfo *info);
+
+ // Sets uses_guid to true if the opened file uses a new-style CodeView
+ // record with a 128-bit GUID, or false if the opened file uses an old-style
+ // CodeView record. When no GUID is available, a 32-bit signature should be
+ // used to identify the module instead. If the information cannot be
+ // determined, this method returns false.
+ bool UsesGUID(bool *uses_guid);
+
+ private:
+ // Outputs the line/address pairs for each line in the enumerator.
+ // Returns true on success.
+ bool PrintLines(IDiaEnumLineNumbers *lines);
+
+ // Outputs a function address and name, followed by its source line list.
+ // block can be the same object as function, or it can be a reference
+ // to a code block that is lexically part of this function, but
+ // resides at a separate address.
+ // Returns true on success.
+ bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block);
+
+ // Outputs all functions as described above. Returns true on success.
+ bool PrintFunctions();
+
+ // Outputs all of the source files in the session's pdb file.
+ // Returns true on success.
+ bool PrintSourceFiles();
+
+ // Outputs all of the frame information necessary to construct stack
+ // backtraces in the absence of frame pointers. For x86 data stored in
+ // .pdb files. Returns true on success.
+ bool PrintFrameDataUsingPDB();
+
+ // Outputs all of the frame information necessary to construct stack
+ // backtraces in the absence of frame pointers. For x64 data stored in
+ // .exe, .dll files. Returns true on success.
+ bool PrintFrameDataUsingEXE();
+
+ // Outputs all of the frame information necessary to construct stack
+ // backtraces in the absence of frame pointers. Returns true on success.
+ bool PrintFrameData();
+
+ // Outputs a single public symbol address and name, if the symbol corresponds
+ // to a code address. Returns true on success. If symbol is does not
+ // correspond to code, returns true without outputting anything.
+ bool PrintCodePublicSymbol(IDiaSymbol *symbol);
+
+ // Outputs a line identifying the PDB file that is being dumped, along with
+ // its uuid and age.
+ bool PrintPDBInfo();
+
+ // Outputs a line identifying the PE file corresponding to the PDB
+ // file that is being dumped, along with its code identifier,
+ // which consists of its timestamp and file size.
+ bool PrintPEInfo();
+
+ // Returns true if this filename has already been seen,
+ // and an ID is stored for it, or false if it has not.
+ bool FileIDIsCached(const wstring &file) {
+ return unique_files_.find(file) != unique_files_.end();
+ }
+
+ // Cache this filename and ID for later reuse.
+ void CacheFileID(const wstring &file, DWORD id) {
+ unique_files_[file] = id;
+ }
+
+ // Store this ID in the cache as a duplicate for this filename.
+ void StoreDuplicateFileID(const wstring &file, DWORD id) {
+ unordered_map<wstring, DWORD>::iterator iter = unique_files_.find(file);
+ if (iter != unique_files_.end()) {
+ // map this id to the previously seen one
+ file_ids_[id] = iter->second;
+ }
+ }
+
+ // Given a file's unique ID, return the ID that should be used to
+ // reference it. There may be multiple files with identical filenames
+ // but different unique IDs. The cache attempts to coalesce these into
+ // one ID per unique filename.
+ DWORD GetRealFileID(DWORD id) {
+ unordered_map<DWORD, DWORD>::iterator iter = file_ids_.find(id);
+ if (iter == file_ids_.end())
+ return id;
+ return iter->second;
+ }
+
+ // Find the PE file corresponding to the loaded PDB file, and
+ // set the code_file_ member. Returns false on failure.
+ bool FindPEFile();
+
+ // Returns the function name for a symbol. If possible, the name is
+ // undecorated. If the symbol's decorated form indicates the size of
+ // parameters on the stack, this information is returned in stack_param_size.
+ // Returns true on success. If the symbol doesn't encode parameter size
+ // information, stack_param_size is set to -1.
+ static bool GetSymbolFunctionName(IDiaSymbol *function, BSTR *name,
+ int *stack_param_size);
+
+ // Returns the number of bytes of stack space used for a function's
+ // parameters. function must have the tag SymTagFunction. In the event of
+ // a failure, returns 0, which is also a valid number of bytes.
+ static int GetFunctionStackParamSize(IDiaSymbol *function);
+
+ // The filename of the PE file corresponding to the currently-open
+ // pdb file.
+ wstring code_file_;
+
+ // The session for the currently-open pdb file.
+ CComPtr<IDiaSession> session_;
+
+ // The current output file for this WriteMap invocation.
+ FILE *output_;
+
+ // There may be many duplicate filenames with different IDs.
+ // This maps from the DIA "unique ID" to a single ID per unique
+ // filename.
+ unordered_map<DWORD, DWORD> file_ids_;
+ // This maps unique filenames to file IDs.
+ unordered_map<wstring, DWORD> unique_files_;
+
+ // This is used for calculating post-transform symbol addresses and lengths.
+ ImageMap image_map_;
+
+ // Disallow copy ctor and operator=
+ PDBSourceLineWriter(const PDBSourceLineWriter&);
+ void operator=(const PDBSourceLineWriter&);
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_WINDOWS_PDB_SOURCE_LINE_WRITER_H_
diff --git a/3rdParty/Breakpad/src/common/windows/string_utils-inl.h b/3rdParty/Breakpad/src/common/windows/string_utils-inl.h
index d281aaa..9b63607 100644
--- a/3rdParty/Breakpad/src/common/windows/string_utils-inl.h
+++ b/3rdParty/Breakpad/src/common/windows/string_utils-inl.h
@@ -30,8 +30,8 @@
// string_utils-inl.h: Safer string manipulation on Windows, supporting
// pre-MSVC8 environments.
-#ifndef COMMON_WINDOWS_STRING_UTILS_INL_H__
-#define COMMON_WINDOWS_STRING_UTILS_INL_H__
+#ifndef COMMON_WINDOWS_STRING_UTILS_INL_H_
+#define COMMON_WINDOWS_STRING_UTILS_INL_H_
#include <stdarg.h>
#include <wchar.h>
@@ -139,4 +139,4 @@ inline void WindowsStringUtils::safe_wcsncpy(wchar_t *destination,
} // namespace google_breakpad
-#endif // COMMON_WINDOWS_STRING_UTILS_INL_H__
+#endif // COMMON_WINDOWS_STRING_UTILS_INL_H_
diff --git a/3rdParty/Breakpad/src/common/windows/string_utils.cc b/3rdParty/Breakpad/src/common/windows/string_utils.cc
new file mode 100644
index 0000000..2728000
--- /dev/null
+++ b/3rdParty/Breakpad/src/common/windows/string_utils.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <cassert>
+#include <vector>
+
+#include "common/windows/string_utils-inl.h"
+
+namespace google_breakpad {
+
+// static
+wstring WindowsStringUtils::GetBaseName(const wstring &filename) {
+ wstring base_name(filename);
+ size_t slash_pos = base_name.find_last_of(L"/\\");
+ if (slash_pos != wstring::npos) {
+ base_name.erase(0, slash_pos + 1);
+ }
+ return base_name;
+}
+
+// static
+bool WindowsStringUtils::safe_mbstowcs(const string &mbs, wstring *wcs) {
+ assert(wcs);
+
+ // First, determine the length of the destination buffer.
+ size_t wcs_length;
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ errno_t err;
+ if ((err = mbstowcs_s(&wcs_length, NULL, 0, mbs.c_str(), _TRUNCATE)) != 0) {
+ return false;
+ }
+ assert(wcs_length > 0);
+#else // _MSC_VER >= 1400
+ if ((wcs_length = mbstowcs(NULL, mbs.c_str(), mbs.length())) == (size_t)-1) {
+ return false;
+ }
+
+ // Leave space for the 0-terminator.
+ ++wcs_length;
+#endif // _MSC_VER >= 1400
+
+ std::vector<wchar_t> wcs_v(wcs_length);
+
+ // Now, convert.
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ if ((err = mbstowcs_s(NULL, &wcs_v[0], wcs_length, mbs.c_str(),
+ _TRUNCATE)) != 0) {
+ return false;
+ }
+#else // _MSC_VER >= 1400
+ if (mbstowcs(&wcs_v[0], mbs.c_str(), mbs.length()) == (size_t)-1) {
+ return false;
+ }
+
+ // Ensure presence of 0-terminator.
+ wcs_v[wcs_length - 1] = '\0';
+#endif // _MSC_VER >= 1400
+
+ *wcs = &wcs_v[0];
+ return true;
+}
+
+// static
+bool WindowsStringUtils::safe_wcstombs(const wstring &wcs, string *mbs) {
+ assert(mbs);
+
+ // First, determine the length of the destination buffer.
+ size_t mbs_length;
+
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ errno_t err;
+ if ((err = wcstombs_s(&mbs_length, NULL, 0, wcs.c_str(), _TRUNCATE)) != 0) {
+ return false;
+ }
+ assert(mbs_length > 0);
+#else // _MSC_VER >= 1400
+ if ((mbs_length = wcstombs(NULL, wcs.c_str(), wcs.length())) == (size_t)-1) {
+ return false;
+ }
+
+ // Leave space for the 0-terminator.
+ ++mbs_length;
+#endif // _MSC_VER >= 1400
+
+ std::vector<char> mbs_v(mbs_length);
+
+ // Now, convert.
+#if _MSC_VER >= 1400 // MSVC 2005/8
+ if ((err = wcstombs_s(NULL, &mbs_v[0], mbs_length, wcs.c_str(),
+ _TRUNCATE)) != 0) {
+ return false;
+ }
+#else // _MSC_VER >= 1400
+ if (wcstombs(&mbs_v[0], wcs.c_str(), wcs.length()) == (size_t)-1) {
+ return false;
+ }
+
+ // Ensure presence of 0-terminator.
+ mbs_v[mbs_length - 1] = '\0';
+#endif // _MSC_VER >= 1400
+
+ *mbs = &mbs_v[0];
+ return true;
+}
+
+} // namespace google_breakpad