/* * 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 #include #include #include #include #include #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.type != expected.type) { ss << " symbols' types do not match: seen `" << (int)symbol.type << "`, expected `" << (int)expected.type << "`.\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& 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, loader::Symbol::SymbolType::Other, "", 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, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, name, 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, 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, loader::Symbol::SymbolType::Other, "symbol", addr}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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].type, symbols[0].name, symbols[0].address + offset}, {symbols[1].binding, symbols[1].type, symbols[1].name, symbols[1].address + offset}, {symbols[2].binding, symbols[2].type, 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, loader::Symbol::SymbolType::Other, "symbol", 0x1310}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol2", 0x2810}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol3", 0x2920}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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].type, symbols[0].name, symbols[0].address & mask}, {symbols[1].binding, symbols[1].type, symbols[1].name, symbols[1].address & mask}, {symbols[2].binding, symbols[2].type, symbols[2].name, symbols[2].address & mask}, {symbols[3].binding, symbols[3].type, 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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol3", 0x30}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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].type, symbols[0].name + "_suffix", symbols[0].address}, {symbols[1].binding, symbols[1].type, symbols[1].name + "_suffix", symbols[1].address}, {symbols[2].binding, symbols[2].type, symbols[2].name + "_suffix", symbols[2].address}, {symbols[3].binding, symbols[3].type, 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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol3", 0x30}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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, symbols[0].type, "NonUniqueName", symbols[0].address}, {symbols[1].binding, symbols[1].type, symbols[1].name, symbols[1].address}, {symbols[3].binding, symbols[3].type, 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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol3", 0x30}, {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, "symbol4", 0x40}, {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol3", 0x30}, {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, "symbol4", 0x40}, {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol3", 0x30}, {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, "symbol4", 0x40}, {loader::Symbol::Binding::Weak, loader::Symbol::SymbolType::Other, "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 the creation of a new filtered table containing only function symbols * of the original table. Also verifies if the original table is kept the same. */ TEST(LoaderSymtabTest, FunctionSymbols) { loader::SymbolTable symtab; loader::Symbol symbols[] = { {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::NoType, "symbol", 0x10}, {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::File, "symbol2", 0x20}, {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Function, "symbol3", 0x30}, {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Object, "symbol4", 0x40}, {loader::Symbol::Binding::Global, loader::Symbol::SymbolType::Function, "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.functionSymbols(); // 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 function symbols ASSERT_TRUE(checkTable(*symtab_new, {symbols[2], symbols[4]})); } /** Test searching for a non-existent address. */ TEST(LoaderSymtabTest, FindNonExistentAddress) { loader::SymbolTable symtab; loader::Symbol symbol = \ {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol2", addr}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "symbol2", 0x20}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "symbol", 0x10}, {loader::Symbol::Binding::Local, loader::Symbol::SymbolType::Other, "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, loader::Symbol::SymbolType::Other, "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