| /* | |
| * Copyright (c) 2021 Daniel R. Carvalho | |
| * 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 the copyright holders 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 <gmock/gmock.h> | |
| #include <gtest/gtest-spi.h> | |
| #include <gtest/gtest.h> | |
| #include <cassert> | |
| #include <initializer_list> | |
| #include <sstream> | |
| #include "base/gtest/serialization_fixture.hh" | |
| #include "base/loader/symtab.hh" | |
| using namespace gem5; | |
| /** | |
| * Checks if a symbol's contents matches the expected contents to generate | |
| * an error message. On matches an empty string is returned. | |
| * | |
| * @param symbol The symbol to check for a match. | |
| * @param expected The expected symbol value. | |
| * @return The error string, if any. | |
| */ | |
| std::string | |
| getSymbolError(const Loader::Symbol& symbol, const Loader::Symbol& expected) | |
| { | |
| std::stringstream ss; | |
| if (symbol.binding != expected.binding) { | |
| ss << " symbols' bindings do not match: seen `" << | |
| (int)symbol.binding << "`, expected `" << | |
| (int)expected.binding << "`.\n"; | |
| } | |
| if (symbol.name != expected.name) { | |
| ss << " symbols' names do not match: seen `" << symbol.name << | |
| "`, expected `" << expected.name << "`.\n"; | |
| } | |
| if (symbol.address != expected.address) { | |
| ss << " symbols' addresses do not match: seen `" << | |
| symbol.address << "`, expected `" << expected.address << "`.\n"; | |
| } | |
| // No error, symbols match | |
| return ss.str(); | |
| } | |
| /** | |
| * Checks that a symbol's contents matches the expected contents. | |
| * | |
| * @param m_symbol | |
| * @param m_expected | |
| * @param symbol The symbol to check for a match. | |
| * @param expected The expected symbol value. | |
| * @return A GTest's assertion result, with error message on failure. | |
| */ | |
| ::testing::AssertionResult | |
| checkSymbol(const char* m_symbol, const char* m_expected, | |
| const Loader::Symbol& symbol, const Loader::Symbol& expected) | |
| { | |
| const std::string error = getSymbolError(symbol, expected); | |
| if (!error.empty()) { | |
| return ::testing::AssertionFailure() << "Symbols do not match (" << | |
| m_symbol << " != " << m_expected << ")\n" << error; | |
| } | |
| return ::testing::AssertionSuccess(); | |
| } | |
| /** | |
| * Checks that a symbol table contains only the expected symbols. | |
| * | |
| * @param symtab The table to check for matches. | |
| * @param expected The expected table's contents. | |
| * @return A GTest's assertion result, with error message on failure. | |
| */ | |
| ::testing::AssertionResult | |
| checkTable(const Loader::SymbolTable& symtab, | |
| const std::initializer_list<Loader::Symbol>& expected) | |
| { | |
| if (expected.size() != (symtab.end() - symtab.begin())) { | |
| return ::testing::AssertionFailure() << "the number of symbols in " | |
| "the table does not match expectation (seen " << | |
| (symtab.end() - symtab.begin()) << ", expected " << | |
| expected.size(); | |
| } | |
| // @todo We should not assume that the symbols are seen in the given order | |
| auto it = symtab.begin(); | |
| for (const auto& symbol : expected) { | |
| const std::string error = getSymbolError(*(it++), symbol); | |
| if (!error.empty()) { | |
| return ::testing::AssertionFailure() << error; | |
| } | |
| } | |
| return ::testing::AssertionSuccess(); | |
| } | |
| /** Test that the constructor creates an empty table. */ | |
| TEST(LoaderSymtabTest, EmptyConstruction) | |
| { | |
| Loader::SymbolTable symtab; | |
| ASSERT_TRUE(symtab.empty()); | |
| ASSERT_TRUE(checkTable(symtab, {})); | |
| } | |
| /** Test that the insertion of a symbol with no name fails. */ | |
| TEST(LoaderSymtabTest, InsertSymbolNoName) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "", 0x10}; | |
| ASSERT_FALSE(symtab.insert(symbol)); | |
| ASSERT_TRUE(checkTable(symtab, {})); | |
| } | |
| /** Test that the insertion of one symbol in an empty table works. */ | |
| TEST(LoaderSymtabTest, InsertOneSymbol) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10}; | |
| ASSERT_TRUE(symtab.insert(symbol)); | |
| ASSERT_FALSE(symtab.empty()); | |
| ASSERT_TRUE(checkTable(symtab, {symbol})); | |
| } | |
| /** Test that the insertion of a symbol with an existing name fails. */ | |
| TEST(LoaderSymtabTest, InsertSymbolExistingName) | |
| { | |
| Loader::SymbolTable symtab; | |
| const std::string name = "symbol"; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, name, 0x10}, | |
| {Loader::Symbol::Binding::Local, name, 0x20}, | |
| }; | |
| ASSERT_TRUE(symtab.insert(symbols[0])); | |
| ASSERT_FALSE(symtab.insert(symbols[1])); | |
| // Check that the second symbol has not been inserted | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0]})); | |
| } | |
| /** Test that the insertion of a symbol with an existing address works. */ | |
| TEST(LoaderSymtabTest, InsertSymbolExistingAddress) | |
| { | |
| Loader::SymbolTable symtab; | |
| const Addr addr = 0x10; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", addr}, | |
| {Loader::Symbol::Binding::Local, "symbol2", addr}, | |
| }; | |
| ASSERT_TRUE(symtab.insert(symbols[0])); | |
| ASSERT_TRUE(symtab.insert(symbols[1])); | |
| // Check that all symbols are present | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1]})); | |
| } | |
| /** Test that the insertion of one symbol in a non-empty table works. */ | |
| TEST(LoaderSymtabTest, InsertMultipleSymbols) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2]})); | |
| } | |
| /** | |
| * Test that a table with multiple entries becomes empty after being cleared. | |
| */ | |
| TEST(LoaderSymtabTest, ClearMultiple) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| symtab.clear(); | |
| ASSERT_TRUE(symtab.empty()); | |
| ASSERT_TRUE(checkTable(symtab, {})); | |
| } | |
| /** | |
| * Test the creation of a new table with offsets applied to the original | |
| * symbols' addresses. Also verifies that the original table is kept the same. | |
| */ | |
| TEST(LoaderSymtabTest, Offset) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| Addr offset = 0x5; | |
| const auto symtab_new = symtab.offset(offset); | |
| // Check that the original table is not modified | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2]})); | |
| // Check that the new table is offset | |
| Loader::Symbol expected_symbols[] = { | |
| {symbols[0].binding, symbols[0].name, symbols[0].address + offset}, | |
| {symbols[1].binding, symbols[1].name, symbols[1].address + offset}, | |
| {symbols[2].binding, symbols[2].name, symbols[2].address + offset}, | |
| }; | |
| ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0], | |
| expected_symbols[1], expected_symbols[2]})); | |
| } | |
| /** | |
| * Test the creation of a new table with masks applied to the original | |
| * symbols' addresses. Also verifies that the original table is kept the same. | |
| */ | |
| TEST(LoaderSymtabTest, Mask) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x1310}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x2810}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x2920}, | |
| {Loader::Symbol::Binding::Local, "symbol4", 0x3C20}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| EXPECT_TRUE(symtab.insert(symbols[3])); | |
| Addr mask = 0x0110; | |
| const auto symtab_new = symtab.mask(mask); | |
| // Check that the original table is not modified | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2], | |
| symbols[3]})); | |
| // Check that the new table is masked | |
| Loader::Symbol expected_symbols[] = { | |
| {symbols[0].binding, symbols[0].name, symbols[0].address & mask}, | |
| {symbols[1].binding, symbols[1].name, symbols[1].address & mask}, | |
| {symbols[2].binding, symbols[2].name, symbols[2].address & mask}, | |
| {symbols[3].binding, symbols[3].name, symbols[3].address & mask}, | |
| }; | |
| ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0], | |
| expected_symbols[1], expected_symbols[2], expected_symbols[3]})); | |
| } | |
| /** | |
| * Test the creation of a new table with renamed symbols. Also verifies | |
| * that the original table is kept the same. | |
| */ | |
| TEST(LoaderSymtabTest, Rename) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| {Loader::Symbol::Binding::Local, "symbol4", 0x40}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| EXPECT_TRUE(symtab.insert(symbols[3])); | |
| const auto symtab_new = | |
| symtab.rename([](std::string &name) { name = name + "_suffix"; }); | |
| // Check that the original table is not modified | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2], | |
| symbols[3]})); | |
| // Check that the new table's symbols have been renamed | |
| Loader::Symbol expected_symbols[] = { | |
| {symbols[0].binding, symbols[0].name + "_suffix", symbols[0].address}, | |
| {symbols[1].binding, symbols[1].name + "_suffix", symbols[1].address}, | |
| {symbols[2].binding, symbols[2].name + "_suffix", symbols[2].address}, | |
| {symbols[3].binding, symbols[3].name + "_suffix", symbols[3].address}, | |
| }; | |
| ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0], | |
| expected_symbols[1], expected_symbols[2], expected_symbols[3]})); | |
| } | |
| /** | |
| * Tests that renaming symbols respects the rule that symbols in a table | |
| * must have unique names. | |
| */ | |
| TEST(LoaderSymtabTest, RenameNonUnique) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| {Loader::Symbol::Binding::Local, "symbol4", 0x40}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| EXPECT_TRUE(symtab.insert(symbols[3])); | |
| int i = 0; | |
| const auto symtab_new = symtab.rename([&i](std::string &name) | |
| { | |
| if ((i++ % 2) == 0) { | |
| name = "NonUniqueName"; | |
| } | |
| }); | |
| // Check that the original table is not modified | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2], | |
| symbols[3]})); | |
| // Check that the new table's symbols have been renamed, yet it does not | |
| // contain the symbols with duplicated names | |
| Loader::Symbol expected_symbols[] = { | |
| {symbols[0].binding, "NonUniqueName", symbols[0].address}, | |
| {symbols[1].binding, symbols[1].name, symbols[1].address}, | |
| {symbols[3].binding, symbols[3].name, symbols[3].address}, | |
| }; | |
| ASSERT_TRUE(checkTable(*symtab_new, {expected_symbols[0], | |
| expected_symbols[1], expected_symbols[2]})); | |
| } | |
| /** | |
| * Test the creation of a new filtered table containing only the global symbols | |
| * of the original table. Also verifies if the original table is kept the same. | |
| */ | |
| TEST(LoaderSymtabTest, Globals) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Global, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| {Loader::Symbol::Binding::Weak, "symbol4", 0x40}, | |
| {Loader::Symbol::Binding::Weak, "symbol5", 0x50} | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| EXPECT_TRUE(symtab.insert(symbols[3])); | |
| EXPECT_TRUE(symtab.insert(symbols[4])); | |
| const auto symtab_new = symtab.globals(); | |
| // Check that the original table is not modified | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2], | |
| symbols[3], symbols[4]})); | |
| // Check that the new table only contains globals | |
| ASSERT_TRUE(checkTable(*symtab_new, {symbols[1]})); | |
| } | |
| /** | |
| * Test the creation of a new filtered table containing only the local symbols | |
| * of the original table. Also verifies if the original table is kept the same. | |
| */ | |
| TEST(LoaderSymtabTest, Locals) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Global, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| {Loader::Symbol::Binding::Weak, "symbol4", 0x40}, | |
| {Loader::Symbol::Binding::Weak, "symbol5", 0x50} | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| EXPECT_TRUE(symtab.insert(symbols[3])); | |
| EXPECT_TRUE(symtab.insert(symbols[4])); | |
| const auto symtab_new = symtab.locals(); | |
| // Check that the original table is not modified | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2], | |
| symbols[3], symbols[4]})); | |
| // Check that the new table only contains locals | |
| ASSERT_TRUE(checkTable(*symtab_new, {symbols[0], symbols[2]})); | |
| } | |
| /** | |
| * Test the creation of a new filtered table containing only the weak symbols | |
| * of the original table. Also verifies if the original table is kept the same. | |
| */ | |
| TEST(LoaderSymtabTest, Weaks) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Global, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| {Loader::Symbol::Binding::Weak, "symbol4", 0x40}, | |
| {Loader::Symbol::Binding::Weak, "symbol5", 0x50} | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| EXPECT_TRUE(symtab.insert(symbols[3])); | |
| EXPECT_TRUE(symtab.insert(symbols[4])); | |
| const auto symtab_new = symtab.weaks(); | |
| // Check that the original table is not modified | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2], | |
| symbols[3], symbols[4]})); | |
| // Check that the new table only contains weaks | |
| ASSERT_TRUE(checkTable(*symtab_new, {symbols[3], symbols[4]})); | |
| } | |
| /** Test searching for a non-existent address. */ | |
| TEST(LoaderSymtabTest, FindNonExistentAddress) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10}; | |
| EXPECT_TRUE(symtab.insert(symbol)); | |
| ASSERT_EQ(symtab.find(0x0), symtab.end()); | |
| } | |
| /** Test searching for a unique address. */ | |
| TEST(LoaderSymtabTest, FindUniqueAddress) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| const auto it = symtab.find(symbols[2].address); | |
| ASSERT_NE(it, symtab.end()); | |
| ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[2]); | |
| } | |
| /** | |
| * Test that searching for a non-unique address returns the first occurrence. | |
| */ | |
| TEST(LoaderSymtabTest, FindNonUniqueAddress) | |
| { | |
| Loader::SymbolTable symtab; | |
| const Addr addr = 0x20; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", addr}, | |
| {Loader::Symbol::Binding::Local, "symbol3", addr}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| const auto it = symtab.find(symbols[1].address); | |
| ASSERT_NE(it, symtab.end()); | |
| ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[1]); | |
| } | |
| /** Test searching for a non-existent name. */ | |
| TEST(LoaderSymtabTest, FindNonExistentName) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10}; | |
| EXPECT_TRUE(symtab.insert(symbol)); | |
| const auto it = symtab.find("symbol2"); | |
| ASSERT_EQ(it, symtab.end()); | |
| } | |
| /** Test searching for an existing name. */ | |
| TEST(LoaderSymtabTest, FindExistingName) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| const auto it = symtab.find(symbols[1].name); | |
| ASSERT_NE(it, symtab.end()); | |
| ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[1]); | |
| } | |
| /** Test searching for an existent address using findNearest. */ | |
| TEST(LoaderSymtabTest, FindNearestExact) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| const auto it = symtab.findNearest(symbols[1].address); | |
| ASSERT_NE(it, symtab.end()); | |
| ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[1]); | |
| } | |
| /** | |
| * Test that, in a table containing address A, searching for the nearest | |
| * address of an address B where B=A+x returns A. | |
| */ | |
| TEST(LoaderSymtabTest, FindNearestRound) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10}; | |
| EXPECT_TRUE(symtab.insert(symbol)); | |
| const auto it = symtab.findNearest(symbol.address + 0x1); | |
| ASSERT_NE(it, symtab.end()); | |
| ASSERT_PRED_FORMAT2(checkSymbol, *it, symbol); | |
| } | |
| /** | |
| * Test that, in a table containing address A1 and A2, where A1<A2, searching | |
| * for the nearest address of an address B where B=A1+x and B<A2 returns A1, | |
| * and marks A2 as the next address. | |
| */ | |
| TEST(LoaderSymtabTest, FindNearestRoundWithNext) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| Addr next_addr; | |
| const auto it = symtab.findNearest(symbols[0].address + 0x1, next_addr); | |
| ASSERT_NE(it, symtab.end()); | |
| ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[0]); | |
| ASSERT_EQ(next_addr, symbols[1].address); | |
| } | |
| /** | |
| * Test that, in a table containing address A, searching for the nearest | |
| * address of an address B where B=A+x returns A; however, the next address | |
| * is non-existent, so it is marked as not valid. | |
| */ | |
| TEST(LoaderSymtabTest, FindNearestRoundWithNextNonExistent) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10}; | |
| EXPECT_TRUE(symtab.insert(symbol)); | |
| Addr next_addr; | |
| const auto it = symtab.findNearest(symbol.address + 0x1, next_addr); | |
| ASSERT_NE(it, symtab.end()); | |
| ASSERT_PRED_FORMAT2(checkSymbol, *it, symbol); | |
| ASSERT_EQ(next_addr, 0); | |
| } | |
| /** | |
| * Test that searching for the nearest address of an address lower than the | |
| * lowest address fails. | |
| */ | |
| TEST(LoaderSymtabTest, FindNearestNonExistent) | |
| { | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbol = {Loader::Symbol::Binding::Local, "symbol", 0x10}; | |
| EXPECT_TRUE(symtab.insert(symbol)); | |
| const auto it = symtab.findNearest(symbol.address - 0x1); | |
| ASSERT_EQ(it, symtab.end()); | |
| } | |
| /** | |
| * Test that the insertion of a symbol table's symbols in another table works | |
| * when any symbol name conflicts. | |
| */ | |
| TEST(LoaderSymtabTest, InsertTableConflicting) | |
| { | |
| const std::string name = "symbol"; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, name, 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| {Loader::Symbol::Binding::Local, "symbol4", 0x40}, | |
| // Introduce name conflict | |
| {Loader::Symbol::Binding::Local, name, 0x50}, | |
| }; | |
| // Populate table 1 | |
| Loader::SymbolTable symtab; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| // Populate table 2 | |
| Loader::SymbolTable symtab2; | |
| EXPECT_TRUE(symtab2.insert(symbols[3])); | |
| EXPECT_TRUE(symtab2.insert(symbols[4])); | |
| // Do the insertion | |
| ASSERT_FALSE(symtab.insert(symtab2)); | |
| // Check that none of the tables change | |
| ASSERT_TRUE(checkTable(symtab2, {symbols[3], symbols[4]})); | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2]})); | |
| } | |
| /** | |
| * Test that the insertion of a symbol table's symbols in another table works | |
| * when no symbols conflict. | |
| */ | |
| TEST(LoaderSymtabTest, InsertTable) | |
| { | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| {Loader::Symbol::Binding::Local, "symbol4", 0x40}, | |
| {Loader::Symbol::Binding::Local, "symbol5", 0x50}, | |
| }; | |
| // Populate table 1 | |
| Loader::SymbolTable symtab; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| // Populate table 2 | |
| Loader::SymbolTable symtab2; | |
| EXPECT_TRUE(symtab2.insert(symbols[3])); | |
| EXPECT_TRUE(symtab2.insert(symbols[4])); | |
| // Do the insertion | |
| symtab.insert(symtab2); | |
| // Check that symtab2 does not change | |
| ASSERT_TRUE(checkTable(symtab2, {symbols[3], symbols[4]})); | |
| // Check that the symbols from symtab2 have been inserted in symtab | |
| ASSERT_TRUE(checkTable(symtab, {symbols[0], symbols[1], symbols[2], | |
| symbols[3], symbols[4]})); | |
| } | |
| using LoaderSymtabSerializationFixture = SerializationFixture; | |
| /** Test serialization. */ | |
| TEST_F(LoaderSymtabSerializationFixture, Serialization) | |
| { | |
| // Populate the table | |
| Loader::SymbolTable symtab; | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| }; | |
| EXPECT_TRUE(symtab.insert(symbols[0])); | |
| EXPECT_TRUE(symtab.insert(symbols[1])); | |
| EXPECT_TRUE(symtab.insert(symbols[2])); | |
| // Serialization | |
| std::ostringstream cp; | |
| Serializable::ScopedCheckpointSection scs(cp, "Section1"); | |
| symtab.serialize("test", cp); | |
| // Verify the output | |
| ASSERT_THAT(cp.str(), ::testing::StrEq("\n[Section1]\ntest.size=3\n" | |
| "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n" | |
| "test.addr_1=32\ntest.symbol_1=symbol2\ntest.binding_1=1\n" | |
| "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n")); | |
| } | |
| /** Test unserialization. */ | |
| TEST_F(LoaderSymtabSerializationFixture, Unserialization) | |
| { | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Local, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| }; | |
| simulateSerialization("\n[Section1]\ntest.size=3\n" | |
| "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n" | |
| "test.addr_1=32\ntest.symbol_1=symbol2\ntest.binding_1=1\n" | |
| "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n"); | |
| Loader::SymbolTable unserialized_symtab; | |
| CheckpointIn cp(getDirName()); | |
| Serializable::ScopedCheckpointSection scs(cp, "Section1"); | |
| unserialized_symtab.unserialize("test", cp); | |
| // Make sure that the symbols in symtab are present in the | |
| // unserialized table | |
| ASSERT_TRUE(checkTable(unserialized_symtab, {symbols[0], symbols[1], | |
| symbols[2]})); | |
| } | |
| /** | |
| * Test unserialization missing binding. | |
| * @todo Since there is no way to create a checkpoint without binding anymore, | |
| * this functionality should be deprecated at some point. | |
| */ | |
| TEST_F(LoaderSymtabSerializationFixture, UnserializationMissingBinding) | |
| { | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Global, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| }; | |
| simulateSerialization("\n[Section1]\ntest.size=3\n" | |
| "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n" | |
| "test.addr_1=32\ntest.symbol_1=symbol2\n" | |
| "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n"); | |
| Loader::SymbolTable unserialized_symtab; | |
| CheckpointIn cp(getDirName()); | |
| Serializable::ScopedCheckpointSection scs(cp, "Section1"); | |
| unserialized_symtab.unserialize("test", cp); | |
| // Make sure that the symbols in symtab are present in the | |
| // unserialized table | |
| ASSERT_TRUE(checkTable(unserialized_symtab, {symbols[0], symbols[1], | |
| symbols[2]})); | |
| } | |
| /** | |
| * Test unserialization missing binding with a different default value. | |
| * @todo Since there is no way to create a checkpoint without binding anymore, | |
| * this functionality should be deprecated at some point. | |
| */ | |
| TEST_F(LoaderSymtabSerializationFixture, | |
| UnserializationMissingBindingChangeDefault) | |
| { | |
| Loader::Symbol symbols[] = { | |
| {Loader::Symbol::Binding::Local, "symbol", 0x10}, | |
| {Loader::Symbol::Binding::Weak, "symbol2", 0x20}, | |
| {Loader::Symbol::Binding::Local, "symbol3", 0x30}, | |
| }; | |
| simulateSerialization("\n[Section1]\ntest.size=3\n" | |
| "test.addr_0=16\ntest.symbol_0=symbol\ntest.binding_0=1\n" | |
| "test.addr_1=32\ntest.symbol_1=symbol2\n" | |
| "test.addr_2=48\ntest.symbol_2=symbol3\ntest.binding_2=1\n"); | |
| Loader::SymbolTable unserialized_symtab; | |
| CheckpointIn cp(getDirName()); | |
| Serializable::ScopedCheckpointSection scs(cp, "Section1"); | |
| unserialized_symtab.unserialize("test", cp, | |
| Loader::Symbol::Binding::Weak); | |
| // Make sure that the symbols in symtab are present in the | |
| // unserialized table | |
| ASSERT_TRUE(checkTable(unserialized_symtab, {symbols[0], symbols[1], | |
| symbols[2]})); | |
| } |