blob: e01dc2c0eae51dfd07222c835a9de9da83acba33 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/symbols/module_symbols_impl.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/common/string_util.h"
#include "src/developer/debug/zxdb/symbols/compile_unit.h"
#include "src/developer/debug/zxdb/symbols/dwarf_binary_impl.h"
#include "src/developer/debug/zxdb/symbols/elf_symbol.h"
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/input_location.h"
#include "src/developer/debug/zxdb/symbols/line_details.h"
#include "src/developer/debug/zxdb/symbols/resolve_options.h"
#include "src/developer/debug/zxdb/symbols/symbol_context.h"
#include "src/developer/debug/zxdb/symbols/test_symbol_module.h"
#include "src/developer/debug/zxdb/symbols/type.h"
#include "src/developer/debug/zxdb/symbols/variable.h"
namespace zxdb {
namespace {
class ScopedUnlink {
public:
explicit ScopedUnlink(const char* name) : name_(name) {}
~ScopedUnlink() { EXPECT_EQ(0, unlink(name_)); }
private:
const char* name_;
};
} // namespace
// Trying to load a nonexistent file should error.
TEST(ModuleSymbols, NonExistantFile) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName() + "_NONEXISTANT", "");
// Should fail to load.
EXPECT_FALSE(setup.Init().ok());
}
// Trying to load a random file should error.
TEST(ModuleSymbols, BadFileType) {
char temp_name[] = "/tmp/zxdb_symbol_test.txtXXXXXX";
int fd = mkstemp(temp_name);
ASSERT_LT(0, fd) << "Could not create temporary file: " << temp_name;
// Just use the file name itself as the contents of the file.
ScopedUnlink unlink(temp_name);
EXPECT_LT(0, write(fd, temp_name, strlen(temp_name)));
close(fd);
// The load should fail.
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName() + "_NONEXISTANT", "");
ASSERT_FALSE(setup.Init("", false).ok());
}
TEST(ModuleSymbols, GetMappedLength) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(), "");
ASSERT_TRUE(setup.Init("/build_dir").ok());
// The checked-in test .so's last PROGBITS segment record is:
//
// [15] .got.plt PROGBITS 0000000000002000 00002000
// 0000000000000030 0000000000000000 WA 0 0 8
//
// So 0x3008 offset + 0x30 = 0x2030 ending offset. This will likely change if the checked-in
// test .so is updated. Just verify the results with "readelf -S"
EXPECT_EQ(0x2030u, setup.symbols()->GetMappedLength());
}
TEST(ModuleSymbols, Basic) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(), "");
ASSERT_TRUE(setup.Init("/build_dir").ok());
// Make a symbol context with some load address to ensure that the addresses round-trip properly.
SymbolContext symbol_context(0x18000);
// MyFunction() should have one implementation.
std::vector<Location> addrs = setup.symbols()->ResolveInputLocation(
symbol_context,
InputLocation(Identifier(IdentifierComponent(TestSymbolModule::kMyFunctionName))));
ASSERT_EQ(1u, addrs.size());
// On one occasion Clang generated a symbol file that listed many functions in this file starting
// at offset 0. This obviously causes problems and the test fails below with bafflingly incorrect
// line numbers. The problem went away after forcing recompilation of that file. It might be an
// intermittent Clang bug or some random corruption. If this assert hits, check the function start
// addresses in the DWARF dump, there should be no functions starting at offset 0 in the file.
ASSERT_NE(0u, addrs[0].address());
// That address should resolve back to the function name (don't know the exact file path the
// compiler generated so just check the name).
auto locations =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(addrs[0].address()));
ASSERT_EQ(1u, locations.size());
EXPECT_TRUE(locations[0].is_symbolized());
EXPECT_TRUE(StringEndsWith(locations[0].file_line().file(), "/zxdb_symbol_test.cc"));
EXPECT_EQ(109, locations[0].file_line().line());
EXPECT_EQ("/build_dir", locations[0].file_line().comp_dir());
// The function symbol should have a compilation unit with a C-style language defined and the name
// should contain the file.
ASSERT_TRUE(locations[0].symbol());
fxl::RefPtr<CompileUnit> unit = locations[0].symbol().Get()->GetCompileUnit();
ASSERT_TRUE(unit);
EXPECT_NE(std::string::npos, unit->name().find("zxdb_symbol_test.cc"));
EXPECT_TRUE(DwarfLangIsCFamily(unit->language()));
}
// Tests that querying an address far from the last address in the module won't return anything.
TEST(ModuleSymbols, OffEnd) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(), "");
ASSERT_TRUE(setup.Init("/build_dir").ok());
constexpr uint64_t kLoadAddress = 0x100000;
SymbolContext symbol_context(kLoadAddress);
// Check an address far past the end of the test module.
std::vector<Location> addrs = setup.symbols()->ResolveInputLocation(
symbol_context, InputLocation(kLoadAddress + 0x1000000000));
ASSERT_EQ(1u, addrs.size());
// It should not have a matching symbol (the last ELF symbol in the module shouldn't match it
// just because it's the previous thing).
EXPECT_FALSE(addrs[0].has_symbols());
}
TEST(ModuleSymbols, LineDetailsForAddress) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(), "");
ASSERT_TRUE(setup.Init("/build_dir").ok());
// Make a symbol context with some load address to ensure that the addresses round-trip properly.
SymbolContext symbol_context(0x18000);
// Get the canonical file name to test.
auto file_matches = setup.symbols()->FindFileMatches("line_lookup_symbol_test.cc");
ASSERT_EQ(1u, file_matches.size());
const std::string file_name = file_matches[0];
// The file name should be canonical and lack any redundant "." or "..".
EXPECT_EQ("../../src/developer/debug/zxdb/symbols/test_data/line_lookup_symbol_test.cc",
file_name);
// Get address of line 28 which is a normal line with code on both sides.
int const kLineToQuery = 28;
ResolveOptions options;
options.symbolize = false;
std::vector<Location> addrs;
addrs = setup.symbols()->ResolveInputLocation(
symbol_context, InputLocation(FileLine(file_name, kLineToQuery)), options);
ASSERT_LE(1u, addrs.size());
auto locations =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(addrs[0].address()));
ASSERT_EQ(1u, locations.size());
EXPECT_EQ(kLineToQuery, locations[0].file_line().line());
EXPECT_EQ(file_name, locations[0].file_line().file());
EXPECT_EQ("/build_dir", locations[0].file_line().comp_dir());
// Lookup the line info. Normally we expect one line table entry for this but don't want to assume
// that since the compiler could emit multiple entries for it.
LineDetails line_details =
setup.symbols()->LineDetailsForAddress(symbol_context, addrs[0].address(), false);
EXPECT_EQ(file_name, line_details.file_line().file());
EXPECT_EQ(kLineToQuery, line_details.file_line().line());
EXPECT_EQ("/build_dir", line_details.file_line().comp_dir());
ASSERT_FALSE(line_details.entries().empty());
uint64_t begin_range = line_details.entries().front().range.begin();
uint64_t end_range = line_details.entries().back().range.end();
EXPECT_LT(begin_range, end_range);
// The address before the beginning of the range should be the previous line.
LineDetails prev_details =
setup.symbols()->LineDetailsForAddress(symbol_context, begin_range - 1, false);
EXPECT_EQ(kLineToQuery - 1, prev_details.file_line().line());
EXPECT_EQ(file_name, prev_details.file_line().file());
EXPECT_EQ("/build_dir", prev_details.file_line().comp_dir());
ASSERT_FALSE(prev_details.entries().empty());
EXPECT_EQ(begin_range, prev_details.entries().back().range.end());
// The end of the range (which is non-inclusive) should be the next line.
LineDetails next_details =
setup.symbols()->LineDetailsForAddress(symbol_context, end_range, false);
EXPECT_EQ(kLineToQuery + 1, next_details.file_line().line());
EXPECT_EQ(file_name, next_details.file_line().file());
EXPECT_EQ("/build_dir", next_details.file_line().comp_dir());
ASSERT_FALSE(next_details.entries().empty());
EXPECT_EQ(end_range, next_details.entries().front().range.begin());
}
TEST(ModuleSymbols, ResolveLineInputLocation) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(), "");
ASSERT_TRUE(setup.Init("/build_dir").ok());
// Make a symbol context with some load address to ensure that the addresses round-trip properly.
SymbolContext symbol_context(0x18000);
// Get the canonical file name to test.
auto file_matches = setup.symbols()->FindFileMatches("line_lookup_symbol_test.cc");
ASSERT_EQ(1u, file_matches.size());
const std::string file_name = file_matches[0];
// Basic one, look for line 27 which is a normal statement.
ResolveOptions options;
options.symbolize = false;
std::vector<Location> addrs;
addrs = setup.symbols()->ResolveInputLocation(symbol_context,
InputLocation(FileLine(file_name, 27)), options);
ASSERT_LE(1u, addrs.size());
auto locations =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(addrs[0].address()));
ASSERT_EQ(1u, locations.size());
EXPECT_EQ(27, locations[0].file_line().line());
EXPECT_EQ(file_name, locations[0].file_line().file());
EXPECT_EQ("/build_dir", locations[0].file_line().comp_dir());
// Absolute path should be supported.
locations = setup.symbols()->ResolveInputLocation(
symbol_context, InputLocation(FileLine("/build_dir/" + file_name, 27)), options);
ASSERT_EQ(1u, locations.size());
EXPECT_EQ(addrs[0].address(), locations[0].address());
// Line 26 is a comment line, looking it up should get the following line.
addrs = setup.symbols()->ResolveInputLocation(symbol_context,
InputLocation(FileLine(file_name, 26)), options);
ASSERT_LE(1u, addrs.size());
locations =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(addrs[0].address()));
ASSERT_EQ(1u, locations.size());
EXPECT_EQ(27, locations[0].file_line().line());
EXPECT_EQ(file_name, locations[0].file_line().file());
EXPECT_EQ("/build_dir", locations[0].file_line().comp_dir());
// Line 15 is the beginning of the templatized function. There should be two matches since its
// instantiated twice.
addrs = setup.symbols()->ResolveInputLocation(symbol_context,
InputLocation(FileLine(file_name, 15)), options);
ASSERT_EQ(2u, addrs.size());
locations =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(addrs[0].address()));
ASSERT_EQ(1u, locations.size());
EXPECT_EQ(15, locations[0].file_line().line());
EXPECT_EQ(file_name, locations[0].file_line().file());
EXPECT_EQ("/build_dir", locations[0].file_line().comp_dir());
locations =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(addrs[1].address()));
ASSERT_EQ(1u, locations.size());
EXPECT_EQ(15, locations[0].file_line().line());
EXPECT_EQ(file_name, locations[0].file_line().file());
EXPECT_EQ("/build_dir", locations[0].file_line().comp_dir());
// Line 17 is only present in one of the two template instantiations. We should only find it once
// (see note below about case #2).
addrs = setup.symbols()->ResolveInputLocation(symbol_context,
InputLocation(FileLine(file_name, 17)), options);
ASSERT_TRUE(addrs.size() == 1u || addrs.size() == 2u);
locations =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(addrs[0].address()));
ASSERT_EQ(1u, locations.size());
EXPECT_EQ(17, locations[0].file_line().line());
EXPECT_EQ("/build_dir", locations[0].file_line().comp_dir());
if (addrs.size() == 2u) {
// MSVC in debug mode will emit the full code in both instantiations of the template which is
// valid. To be more robust this test allows that form even though Clang doesn't do this. The
// important thing is that looking up line 17 never gives us line 19 (which is the other
// template instantiation).
locations =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(addrs[1].address()));
EXPECT_EQ(17, locations[0].file_line().line());
}
}
TEST(ModuleSymbols, ResolveGlobalVariable) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(), "");
ASSERT_TRUE(setup.Init().ok());
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
ResolveOptions options;
options.symbolize = true;
std::vector<Location> addrs;
// Look up "kGlobal" which should be a variable of type "int" at some nonzero location.
Identifier global_name = TestSymbolModule::SplitName(TestSymbolModule::kGlobalName);
addrs =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(global_name), options);
ASSERT_LE(1u, addrs.size());
EXPECT_TRUE(addrs[0].symbol());
const Variable* var = addrs[0].symbol().Get()->As<Variable>();
ASSERT_TRUE(var);
EXPECT_EQ(TestSymbolModule::kGlobalName, var->GetFullName());
const Type* var_type = var->type().Get()->As<Type>();
ASSERT_TRUE(var_type);
EXPECT_EQ("int", var_type->GetFullName());
// This variable doesn't have an address because in the current checked-in file, it's expressed as
// a DWARF expression that the SymbolModule doesn't evaluate. In a normal expression, the
// symbol match will trigger the necessary logic in the expression evaluation system. This
// expression might change and that's OK.
EXPECT_EQ(0u, addrs[0].address());
// Look up the class static.
addrs = setup.symbols()->ResolveInputLocation(
symbol_context,
InputLocation(TestSymbolModule::SplitName(TestSymbolModule::kClassStaticName)), options);
ASSERT_LE(1u, addrs.size());
EXPECT_TRUE(addrs[0].symbol());
var = addrs[0].symbol().Get()->As<Variable>();
ASSERT_TRUE(var);
EXPECT_EQ(TestSymbolModule::kClassStaticName, var->GetFullName());
var_type = var->type().Get()->As<Type>();
ASSERT_TRUE(var_type);
EXPECT_EQ("int", var_type->GetFullName());
// As above, there's no address for the current symbol file.
EXPECT_EQ(0u, addrs[0].address());
// Annotate the global variable as a register. This lookup should fail since registers can't be
// looked up in the symbols (this tests that ModuleSymbolsImpl filters out bad special component
// types).
Identifier register_name;
register_name.AppendComponent(
IdentifierComponent(SpecialIdentifier::kRegister, TestSymbolModule::kGlobalName));
addrs =
setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(register_name), options);
ASSERT_TRUE(addrs.empty());
}
TEST(ModuleSymbols, ResolvePLTEntry) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(),
TestSymbolModule::GetStrippedCheckedInTestFileName());
ASSERT_TRUE(setup.Init().ok());
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
ResolveOptions options;
options.symbolize = true;
// Name->PLT symbol.
auto result = setup.symbols()->ResolveInputLocation(
symbol_context,
InputLocation(Identifier(
IdentifierComponent(SpecialIdentifier::kPlt, TestSymbolModule::kPltFunctionName))),
options);
ASSERT_EQ(1u, result.size());
EXPECT_TRUE(result[0].is_valid());
EXPECT_EQ(TestSymbolModule::kPltFunctionOffset, result[0].address());
const ElfSymbol* elf_symbol = result[0].symbol().Get()->As<ElfSymbol>();
ASSERT_TRUE(elf_symbol);
EXPECT_EQ(ElfSymbolType::kPlt, elf_symbol->elf_type());
EXPECT_EQ(TestSymbolModule::kPltFunctionName, elf_symbol->linkage_name());
// Now look up the address and expect to get the symbol back.
result = setup.symbols()->ResolveInputLocation(
symbol_context, InputLocation(TestSymbolModule::kPltFunctionOffset), options);
ASSERT_EQ(1u, result.size());
elf_symbol = result[0].symbol().Get()->As<ElfSymbol>();
ASSERT_TRUE(elf_symbol);
EXPECT_EQ(ElfSymbolType::kPlt, elf_symbol->elf_type());
EXPECT_EQ(TestSymbolModule::kPltFunctionName, elf_symbol->linkage_name());
}
TEST(ModuleSymbols, ResolveMainFunction) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(),
TestSymbolModule::GetStrippedCheckedInTestFileName());
ASSERT_TRUE(setup.Init().ok());
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
// The sample module is a shared library with no main function, so there should be nothing found.
InputLocation input_loc((Identifier(IdentifierComponent(SpecialIdentifier::kMain))));
ResolveOptions options;
auto addrs = setup.symbols()->ResolveInputLocation(symbol_context, input_loc, options);
EXPECT_TRUE(addrs.empty());
// Inject a function named "main" (but not marked in the symbols as the official main function).
// This is kind of a hack. The ModuleSymbolsImpl is using a real symbol file and need to be able
// to generate the function symbol from the SymbolRef for this call to succeed. So we can't just
// inject a fake SymbolRef. Instead, redirect "main" in the index to an existing function
// ("MyFunction").
auto my_function_matches = setup.symbols()->index_.FindExact(
Identifier(IdentifierComponent(TestSymbolModule::kMyFunctionName)));
ASSERT_EQ(1u, my_function_matches.size());
auto main_node = setup.symbols()->index_.root().AddChild(IndexNode::Kind::kFunction, "main");
main_node->AddDie(my_function_matches[0]);
// Query for $main again. Since nothing is marked as the main function, the one named "main"
// should be returned. Since we redirected the index above, this will actually be "MyFunction".
addrs = setup.symbols()->ResolveInputLocation(symbol_context, input_loc, options);
ASSERT_EQ(1u, addrs.size());
EXPECT_EQ(TestSymbolModule::kMyFunctionName, addrs[0].symbol().Get()->GetFullName());
// Now mark a different function as the official main one (kNamespaceFunctionName).
auto anon_function_matches = setup.symbols()->index_.FindExact(
TestSymbolModule::SplitName(TestSymbolModule::kNamespaceFunctionName));
ASSERT_EQ(1u, anon_function_matches.size());
setup.symbols()->index_.main_functions().push_back(anon_function_matches[0]);
// Query again. Now that a function is explicitly marked as the main one,
// only it should be returned.
addrs = setup.symbols()->ResolveInputLocation(symbol_context, input_loc, options);
ASSERT_EQ(1u, addrs.size());
EXPECT_EQ(TestSymbolModule::kNamespaceFunctionName, addrs[0].symbol().Get()->GetFullName());
}
TEST(ModuleSymbols, SkipPrologue) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(),
TestSymbolModule::GetStrippedCheckedInTestFileName());
ASSERT_TRUE(setup.Init().ok());
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
InputLocation input_fn_loc((Identifier(IdentifierComponent(TestSymbolModule::kMyFunctionName))));
// Query the function by name with no prologue skipping.
ResolveOptions no_skip_options;
no_skip_options.symbolize = true;
no_skip_options.skip_function_prologue = false;
auto no_skip_addrs =
setup.symbols()->ResolveInputLocation(symbol_context, input_fn_loc, no_skip_options);
ASSERT_EQ(1u, no_skip_addrs.size());
EXPECT_EQ(TestSymbolModule::kMyFunctionAddress, no_skip_addrs[0].address());
EXPECT_EQ(TestSymbolModule::kMyFunctionName,
no_skip_addrs[0].symbol().Get()->As<Function>()->GetFullName());
// Now with prologue skipping.
ResolveOptions skip_options;
skip_options.symbolize = true;
skip_options.skip_function_prologue = true;
auto skip_addrs =
setup.symbols()->ResolveInputLocation(symbol_context, input_fn_loc, skip_options);
ASSERT_EQ(1u, skip_addrs.size());
EXPECT_EQ(no_skip_addrs[0].address() + TestSymbolModule::kMyFunctionPrologueSize,
skip_addrs[0].address());
EXPECT_EQ(TestSymbolModule::kMyFunctionName,
skip_addrs[0].symbol().Get()->As<Function>()->GetFullName());
// Query by line. No skipping.
InputLocation input_line_loc(FileLine("zxdb_symbol_test.cc", TestSymbolModule::kMyFunctionLine));
no_skip_addrs =
setup.symbols()->ResolveInputLocation(symbol_context, input_line_loc, no_skip_options);
ASSERT_EQ(1u, no_skip_addrs.size());
EXPECT_EQ(TestSymbolModule::kMyFunctionAddress, no_skip_addrs[0].address());
EXPECT_EQ(TestSymbolModule::kMyFunctionName,
no_skip_addrs[0].symbol().Get()->As<Function>()->GetFullName());
// With skipping.
skip_addrs = setup.symbols()->ResolveInputLocation(symbol_context, input_line_loc, skip_options);
ASSERT_EQ(1u, skip_addrs.size());
EXPECT_EQ(TestSymbolModule::kMyFunctionAddress + TestSymbolModule::kMyFunctionPrologueSize,
skip_addrs[0].address());
EXPECT_EQ(TestSymbolModule::kMyFunctionName,
skip_addrs[0].symbol().Get()->As<Function>()->GetFullName());
// Query by address. No skipping.
InputLocation input_addr_loc(TestSymbolModule::kMyFunctionAddress);
no_skip_addrs =
setup.symbols()->ResolveInputLocation(symbol_context, input_addr_loc, no_skip_options);
ASSERT_EQ(1u, no_skip_addrs.size());
EXPECT_EQ(TestSymbolModule::kMyFunctionAddress, no_skip_addrs[0].address());
EXPECT_EQ(TestSymbolModule::kMyFunctionName,
no_skip_addrs[0].symbol().Get()->As<Function>()->GetFullName());
// With skipping.
skip_addrs = setup.symbols()->ResolveInputLocation(symbol_context, input_addr_loc, skip_options);
ASSERT_EQ(1u, skip_addrs.size());
EXPECT_EQ(TestSymbolModule::kMyFunctionAddress + TestSymbolModule::kMyFunctionPrologueSize,
skip_addrs[0].address());
EXPECT_EQ(TestSymbolModule::kMyFunctionName,
skip_addrs[0].symbol().Get()->As<Function>()->GetFullName());
}
TEST(ModuleSymbols, ElfSymbols) {
TestSymbolModule setup(TestSymbolModule::GetCheckedInTestFileName(),
TestSymbolModule::GetStrippedCheckedInTestFileName());
ASSERT_TRUE(setup.Init().ok());
// Give it a non-relative context to make sure that things are relative-ized going in and out.
SymbolContext symbol_context(0x1000000);
// Virtual tables have ELF symbols but not DWARF symbols, so to test that we read the ELF symbols
// properly, look one up.
const char kVirtualDerivedVtableName[] = "_ZTV14VirtualDerived";
const char kVirtualDerivedVtableUnmangledName[] = "vtable for VirtualDerived";
Identifier vtable_identifier(
IdentifierComponent(SpecialIdentifier::kElf, kVirtualDerivedVtableName));
std::vector<Location> result = setup.symbols()->ResolveInputLocation(
symbol_context, InputLocation(vtable_identifier), ResolveOptions());
ASSERT_EQ(1u, result.size());
// It should have found the ELF symbol.
ASSERT_TRUE(result[0].symbol());
auto elf_symbol = result[0].symbol().Get()->As<ElfSymbol>();
ASSERT_TRUE(elf_symbol);
EXPECT_EQ(kVirtualDerivedVtableName, elf_symbol->linkage_name());
EXPECT_EQ(kVirtualDerivedVtableUnmangledName, elf_symbol->GetFullName());
// The returned address should match the symbol info except for relative/absolute.
uint64_t absolute_addr = result[0].address();
EXPECT_EQ(absolute_addr, symbol_context.RelativeToAbsolute(elf_symbol->relative_address()));
// Looking up by that address should give back the name.
result = setup.symbols()->ResolveInputLocation(symbol_context, InputLocation(absolute_addr),
ResolveOptions());
ASSERT_EQ(1u, result.size());
// Symbols, names, and addresses should match as above.
ASSERT_TRUE(result[0].symbol());
elf_symbol = result[0].symbol().Get()->As<ElfSymbol>();
ASSERT_TRUE(elf_symbol);
EXPECT_EQ(kVirtualDerivedVtableName, elf_symbol->linkage_name());
EXPECT_EQ(kVirtualDerivedVtableUnmangledName, elf_symbol->GetFullName());
EXPECT_EQ(result[0].address(), symbol_context.RelativeToAbsolute(elf_symbol->relative_address()));
}
// Loads the stripped binary and ensures that exported symbols can still be found (these symbols
// will be present only in the ELF ".dynsym" table).
TEST(ModuleSymbols, StrippedElfSymbols) {
// Supply only the stripped binary name.
TestSymbolModule setup(TestSymbolModule::GetStrippedCheckedInTestFileName(),
TestSymbolModule::GetStrippedCheckedInTestFileName());
ASSERT_TRUE(setup.Init().ok());
// Give it a non-relative context to make sure that things are relative-ized going in and out.
SymbolContext symbol_context(0x1000000);
const char kSymName[] = "_Z9GetIntPtrv";
std::vector<Location> result = setup.symbols()->ResolveInputLocation(
symbol_context,
InputLocation(Identifier(IdentifierComponent(SpecialIdentifier::kElf, kSymName))),
ResolveOptions());
ASSERT_EQ(1u, result.size());
// It should have found the ELF symbol.
ASSERT_TRUE(result[0].symbol());
auto elf_symbol = result[0].symbol().Get()->As<ElfSymbol>();
ASSERT_TRUE(elf_symbol);
EXPECT_EQ(kSymName, elf_symbol->linkage_name());
EXPECT_EQ("GetIntPtr()", elf_symbol->GetFullName());
}
} // namespace zxdb