aboutsummaryrefslogtreecommitdiff
path: root/externals/breakpad/src/common/module_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'externals/breakpad/src/common/module_unittest.cc')
-rw-r--r--externals/breakpad/src/common/module_unittest.cc740
1 files changed, 740 insertions, 0 deletions
diff --git a/externals/breakpad/src/common/module_unittest.cc b/externals/breakpad/src/common/module_unittest.cc
new file mode 100644
index 0000000000..c51162e528
--- /dev/null
+++ b/externals/breakpad/src/common/module_unittest.cc
@@ -0,0 +1,740 @@
+// Copyright 2010 Google LLC
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google LLC nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// module_unittest.cc: Unit tests for google_breakpad::Module.
+
+#ifdef HAVE_CONFIG_H
+#include <config.h> // Must come first
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include "breakpad_googletest_includes.h"
+#include "common/module.h"
+#include "common/using_std_string.h"
+
+using google_breakpad::Module;
+using google_breakpad::StringView;
+using std::stringstream;
+using std::vector;
+using testing::ContainerEq;
+
+static Module::Function* generate_duplicate_function(StringView name) {
+ const Module::Address DUP_ADDRESS = 0xd35402aac7a7ad5cULL;
+ const Module::Address DUP_SIZE = 0x200b26e605f99071ULL;
+ const Module::Address DUP_PARAMETER_SIZE = 0xf14ac4fed48c4a99ULL;
+
+ Module::Function* function = new Module::Function(name, DUP_ADDRESS);
+ Module::Range range(DUP_ADDRESS, DUP_SIZE);
+ function->ranges.push_back(range);
+ 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(Module, WriteHeader) {
+ 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(Module, WriteHeaderCodeId) {
+ 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(Module, WriteOneLineFunc) {
+ 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", 0xe165bf8023b9d9abULL);
+ Module::Range range(0xe165bf8023b9d9abULL, 0x1e4bb0eb1cbf5b09ULL);
+ function->ranges.push_back(range);
+ function->parameter_size = 0x772beee89114358aULL;
+ Module::Line line = { 0xe165bf8023b9d9abULL, 0x1e4bb0eb1cbf5b09ULL,
+ 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(Module, WriteRelativeLoadAddress) {
+ 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)", 0xbec774ea5dd935f3ULL);
+ Module::Range range(0xbec774ea5dd935f3ULL, 0x2922088f98d3f6fcULL);
+ function->ranges.push_back(range);
+ function->parameter_size = 0xe5e9aa008bd5f0d0ULL;
+
+ // Some source lines. The module should not sort these.
+ Module::Line line1 = { 0xbec774ea5dd935f3ULL, 0x1c2be6d6c5af2611ULL,
+ file1, 41676901 };
+ Module::Line line2 = { 0xdaf35bc123885c04ULL, 0xcf621b8d324d0ebULL,
+ file2, 67519080 };
+ function->lines.push_back(line2);
+ function->lines.push_back(line1);
+
+ m.AddFunction(function);
+
+ // Some stack information.
+ auto entry = std::make_unique<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(std::move(entry));
+
+ // Set the load address. Doing this after adding all the data to
+ // the module must work fine.
+ m.SetLoadAddress(0x2ab698b0b6407073ULL);
+
+ 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(Module, WriteOmitUnusedFiles) {
+ 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", 0x9b926d464f0b9384ULL);
+ Module::Range range(0x9b926d464f0b9384ULL, 0x4f524a4ba795e6a6ULL);
+ function->ranges.push_back(range);
+ function->parameter_size = 0xbbe8133a6641c9b7ULL;
+
+ // Source files that refer to some files, but not others.
+ Module::Line line1 = { 0xab415089485e1a20ULL, 0x126e3124979291f2ULL,
+ file1, 137850127 };
+ Module::Line line2 = { 0xb2675b5c3c2ed33fULL, 0x1df77f5551dbd68cULL,
+ 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"
+ "ab415089485e1a20 126e3124979291f2 137850127 0\n"
+ "b2675b5c3c2ed33f 1df77f5551dbd68c 28113549 1\n",
+ contents.c_str());
+}
+
+TEST(Module, WriteNoCFI) {
+ 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)", 0xbec774ea5dd935f3ULL);
+ Module::Range range(0xbec774ea5dd935f3ULL, 0x2922088f98d3f6fcULL);
+ function->ranges.push_back(range);
+ function->parameter_size = 0xe5e9aa008bd5f0d0ULL;
+
+ // Some source lines. The module should not sort these.
+ Module::Line line1 = { 0xbec774ea5dd935f3ULL, 0x1c2be6d6c5af2611ULL,
+ file1, 41676901 };
+ function->lines.push_back(line1);
+
+ m.AddFunction(function);
+
+ // Some stack information.
+ auto entry = std::make_unique<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(std::move(entry));
+
+ // Set the load address. Doing this after adding all the data to
+ // the module must work fine.
+ m.SetLoadAddress(0x2ab698b0b6407073ULL);
+
+ m.Write(s, SYMBOLS_AND_FILES | INLINES);
+ 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(Module, ConstructAddFunction) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two functions.
+ Module::Function* function1 = new Module::Function(
+ "_without_form", 0xd35024aa7ca7da5cULL);
+ Module::Range r1(0xd35024aa7ca7da5cULL, 0x200b26e605f99071ULL);
+ function1->ranges.push_back(r1);
+ function1->parameter_size = 0xf14ac4fed48c4a99ULL;
+
+ Module::Function* function2 = new Module::Function(
+ "_and_void", 0x2987743d0b35b13fULL);
+ Module::Range r2(0x2987743d0b35b13fULL, 0xb369db048deb3010ULL);
+ function2->ranges.push_back(r2);
+ function2->parameter_size = 0x938e556cb5a79988ULL;
+
+ // Put them in a vector.
+ vector<Module::Function*> vec;
+ vec.push_back(function1);
+ vec.push_back(function2);
+
+ for (Module::Function* func: vec)
+ m.AddFunction(func);
+
+ 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(Module, WriteOutOfRangeAddresses) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Specify an allowed address range, representing a PT_LOAD segment in a
+ // module.
+ vector<Module::Range> address_ranges = {
+ Module::Range(0x2000ULL, 0x1000ULL),
+ };
+ m.SetAddressRanges(address_ranges);
+
+ // Add three stack frames (one lower, one in, and one higher than the allowed
+ // address range). Only the middle frame should be captured.
+ auto entry1 = std::make_unique<Module::StackFrameEntry>();
+ entry1->address = 0x1000ULL;
+ entry1->size = 0x100ULL;
+ m.AddStackFrameEntry(std::move(entry1));
+ auto entry2 = std::make_unique<Module::StackFrameEntry>();
+ entry2->address = 0x2000ULL;
+ entry2->size = 0x100ULL;
+ m.AddStackFrameEntry(std::move(entry2));
+ auto entry3 = std::make_unique<Module::StackFrameEntry>();
+ entry3->address = 0x3000ULL;
+ entry3->size = 0x100ULL;
+ m.AddStackFrameEntry(std::move(entry3));
+
+ // Add a function outside the allowed range.
+ Module::File* file = m.FindFile("file_name.cc");
+ Module::Function* function = new Module::Function(
+ "function_name", 0x4000ULL);
+ Module::Range range(0x4000ULL, 0x1000ULL);
+ function->ranges.push_back(range);
+ function->parameter_size = 0x100ULL;
+ Module::Line line = { 0x4000ULL, 0x100ULL, file, 67519080 };
+ function->lines.push_back(line);
+ m.AddFunction(function);
+
+ // Add an extern outside the allowed range.
+ auto extern1 = std::make_unique<Module::Extern>(0x5000ULL);
+ extern1->name = "_xyz";
+ m.AddExtern(std::move(extern1));
+
+ m.Write(s, ALL_SYMBOL_DATA);
+
+ EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
+ "STACK CFI INIT 2000 100 \n",
+ s.str().c_str());
+
+ // Cleanup - Prevent Memory Leak errors.
+ delete (function);
+}
+
+TEST(Module, ConstructAddFrames) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // First STACK CFI entry, with no initial rules or deltas.
+ auto entry1 = std::make_unique<Module::StackFrameEntry>();
+ entry1->address = 0xddb5f41285aa7757ULL;
+ entry1->size = 0x1486493370dc5073ULL;
+ m.AddStackFrameEntry(std::move(entry1));
+
+ // Second STACK CFI entry, with initial rules but no deltas.
+ auto entry2 = std::make_unique<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(std::move(entry2));
+
+ // Third STACK CFI entry, with initial rules and deltas.
+ auto entry3 = std::make_unique<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(std::move(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(Module, ConstructUniqueFiles) {
+ 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(Module, ConstructDuplicateFunctions) {
+ 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);
+ // If this succeeds, we'll have a double-free with the `delete` below. Avoid
+ // that.
+ ASSERT_FALSE(m.AddFunction(function2));
+ delete 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(Module, ConstructFunctionsWithSameAddress) {
+ 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());
+}
+
+// If multiple fields are enabled, only one function is included per address.
+// The entry will be tagged with `m` to show that there are multiple symbols
+// at that address.
+// TODO(lgrey): Remove the non-multiple versions of these tests and remove the
+// suffixes from the suffxed ones when removing `enable_multiple_field_`.
+TEST(Module, ConstructFunctionsWithSameAddressMultiple) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID, "", true);
+
+ // Two functions.
+ Module::Function* function1 = generate_duplicate_function("_without_form");
+ Module::Function* function2 = generate_duplicate_function("_and_void");
+
+ m.AddFunction(function1);
+ // If this succeeds, we'll have a double-free with the `delete` below. Avoid
+ // that.
+ ASSERT_FALSE(m.AddFunction(function2));
+ delete function2;
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+ EXPECT_STREQ(
+ "MODULE os-name architecture id-string name with spaces\n"
+ "FUNC m d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
+ " _without_form\n",
+ contents.c_str());
+}
+
+// Externs should be written out as PUBLIC records, sorted by
+// address.
+TEST(Module, ConstructExterns) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two externs.
+ auto extern1 = std::make_unique<Module::Extern>(0xffff);
+ extern1->name = "_abc";
+ auto extern2 = std::make_unique<Module::Extern>(0xaaaa);
+ extern2->name = "_xyz";
+
+ m.AddExtern(std::move(extern1));
+ m.AddExtern(std::move(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(Module, ConstructDuplicateExterns) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two externs.
+ auto extern1 = std::make_unique<Module::Extern>(0xffff);
+ extern1->name = "_xyz";
+ auto extern2 = std::make_unique<Module::Extern>(0xffff);
+ extern2->name = "_abc";
+
+ m.AddExtern(std::move(extern1));
+ m.AddExtern(std::move(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());
+}
+// Externs with the same address have the `m` tag if the multiple field are
+// enabled.
+TEST(Module, ConstructDuplicateExternsMultiple) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID, "", true);
+
+ // Two externs.
+ auto extern1 = std::make_unique<Module::Extern>(0xffff);
+ extern1->name = "_xyz";
+ auto extern2 = std::make_unique<Module::Extern>(0xffff);
+ extern2->name = "_abc";
+
+ m.AddExtern(std::move(extern1));
+ m.AddExtern(std::move(extern2));
+
+ m.Write(s, ALL_SYMBOL_DATA);
+ string contents = s.str();
+
+ EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " MODULE_ID " " MODULE_NAME
+ "\n"
+ "PUBLIC m 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(Module, ConstructFunctionsAndExternsWithSameAddress) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+ // Two externs.
+ auto extern1 = std::make_unique<Module::Extern>(0xabc0);
+ extern1->name = "abc";
+ auto extern2 = std::make_unique<Module::Extern>(0xfff0);
+ extern2->name = "xyz";
+
+ m.AddExtern(std::move(extern1));
+ m.AddExtern(std::move(extern2));
+
+ Module::Function* function = new Module::Function("_xyz", 0xfff0);
+ Module::Range range(0xfff0, 0x10);
+ function->ranges.push_back(range);
+ 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.
+TEST(Module, ConstructFunctionsAndExternsWithSameAddressPreferExternName) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID, "", false, true);
+
+ // Two externs.
+ auto extern1 = std::make_unique<Module::Extern>(0xabc0);
+ extern1->name = "extern1";
+ auto extern2 = std::make_unique<Module::Extern>(0xfff0);
+ extern2->name = "extern2";
+
+ m.AddExtern(std::move(extern1));
+ m.AddExtern(std::move(extern2));
+
+ Module::Function* function = new Module::Function("function2", 0xfff0);
+ Module::Range range(0xfff0, 0x10);
+ function->ranges.push_back(range);
+ 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 extern2\n"
+ "PUBLIC abc0 0 extern1\n",
+ contents.c_str());
+}
+
+// If there exists an extern and a function at the same address, only write
+// out the FUNC entry, and mark it with `m` if the multiple field is enabled.
+TEST(Module, ConstructFunctionsAndExternsWithSameAddressMultiple) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID, "", true);
+
+ // Two externs.
+ auto extern1 = std::make_unique<Module::Extern>(0xabc0);
+ extern1->name = "abc";
+ auto extern2 = std::make_unique<Module::Extern>(0xfff0);
+ extern2->name = "xyz";
+
+ m.AddExtern(std::move(extern1));
+ m.AddExtern(std::move(extern2));
+
+ Module::Function* function = new Module::Function("_xyz", 0xfff0);
+ Module::Range range(0xfff0, 0x10);
+ function->ranges.push_back(range);
+ 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 m 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(Module, ConstructFunctionsAndThumbExternsWithSameAddress) {
+ stringstream s;
+ Module m(MODULE_NAME, MODULE_OS, "arm", MODULE_ID);
+
+ // Two THUMB externs.
+ auto thumb_extern1 = std::make_unique<Module::Extern>(0xabc1);
+ thumb_extern1->name = "thumb_abc";
+ auto thumb_extern2 = std::make_unique<Module::Extern>(0xfff1);
+ thumb_extern2->name = "thumb_xyz";
+
+ auto arm_extern1 = std::make_unique<Module::Extern>(0xcc00);
+ arm_extern1->name = "arm_func";
+
+ m.AddExtern(std::move(thumb_extern1));
+ m.AddExtern(std::move(thumb_extern2));
+ m.AddExtern(std::move(arm_extern1));
+
+ // The corresponding function from the DWARF debug data have the actual
+ // address.
+ Module::Function* function = new Module::Function("_thumb_xyz", 0xfff0);
+ Module::Range range(0xfff0, 0x10);
+ function->ranges.push_back(range);
+ 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());
+}