| // 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. |
| |
| #ifndef SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_MODULE_SYMBOLS_IMPL_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_MODULE_SYMBOLS_IMPL_H_ |
| |
| #include <map> |
| |
| #include "gtest/gtest_prod.h" |
| #include "llvm/BinaryFormat/ELF.h" |
| #include "llvm/DebugInfo/DWARF/DWARFUnit.h" |
| #include "src/developer/debug/zxdb/common/err.h" |
| #include "src/developer/debug/zxdb/symbols/elf_symbol_record.h" |
| #include "src/developer/debug/zxdb/symbols/index.h" |
| #include "src/developer/debug/zxdb/symbols/location.h" |
| #include "src/developer/debug/zxdb/symbols/module_symbols.h" |
| #include "src/lib/fxl/macros.h" |
| #include "src/lib/fxl/memory/weak_ptr.h" |
| |
| namespace llvm { |
| |
| class DWARFCompileUnit; |
| class DWARFContext; |
| class MemoryBuffer; |
| |
| namespace object { |
| class Binary; |
| } // namespace object |
| |
| } // namespace llvm |
| |
| namespace zxdb { |
| |
| class DwarfBinary; |
| class DwarfSymbolFactory; |
| class Variable; |
| |
| // Represents the symbol interface for a module (executable or shared library). See ModuleSymbols. |
| // |
| // This provides a high-level interface on top of the DwarfBinary file (low-level stuff), the Index, |
| // and the SymbolFactory. |
| class ModuleSymbolsImpl final : public ModuleSymbols { |
| public: |
| DwarfBinary* binary() { return binary_.get(); } |
| DwarfSymbolFactory* symbol_factory() { return symbol_factory_.get(); } |
| |
| fxl::WeakPtr<ModuleSymbolsImpl> GetWeakPtr(); |
| |
| // ModuleSymbols implementation. |
| ModuleSymbolStatus GetStatus() const override; |
| std::time_t GetModificationTime() const override; |
| std::string GetBuildDir() const override; |
| uint64_t GetMappedLength() const override; |
| std::vector<Location> ResolveInputLocation( |
| const SymbolContext& symbol_context, const InputLocation& input_location, |
| const ResolveOptions& options = ResolveOptions()) const override; |
| fxl::RefPtr<DwarfUnit> GetDwarfUnit(const SymbolContext& symbol_context, |
| uint64_t absolute_address) const override; |
| LineDetails LineDetailsForAddress(const SymbolContext& symbol_context, uint64_t absolute_address, |
| bool greedy) const override; |
| std::vector<std::string> FindFileMatches(std::string_view name) const override; |
| std::vector<fxl::RefPtr<Function>> GetMainFunctions() const override; |
| const Index& GetIndex() const override; |
| LazySymbol IndexSymbolRefToSymbol(const IndexNode::SymbolRef&) const override; |
| bool HasBinary() const override; |
| |
| private: |
| FRIEND_MAKE_REF_COUNTED(ModuleSymbolsImpl); |
| FRIEND_REF_COUNTED_THREAD_SAFE(ModuleSymbolsImpl); |
| FRIEND_TEST(ModuleSymbols, ResolveMainFunction); |
| |
| // The input binary must be successfully initialized already. |
| // |
| // The build_dir, if not empty, will be used to override the compilation_dir of FileLine objects, |
| // which is useful because in Fuchsia checkout, the compilation_dir will always be ".". |
| // |
| // If create_index is true, an index will be created for fast symbol lookup. |
| // Normal callers will always want to create the index, unless you don't need to query a symbol |
| // from its name, e.g., in some test scenarios or in symbolizer. |
| explicit ModuleSymbolsImpl(std::unique_ptr<DwarfBinary> binary, const std::string& build_dir, |
| bool create_index = true); |
| ~ModuleSymbolsImpl() override; |
| |
| // Helpers for ResolveInputLocation() for the different types of inputs. |
| std::vector<Location> ResolveLineInputLocation(const SymbolContext& symbol_context, |
| const InputLocation& input_location, |
| const ResolveOptions& options) const; |
| std::vector<Location> ResolveSymbolInputLocation(const SymbolContext& symbol_context, |
| const InputLocation& input_location, |
| const ResolveOptions& options) const; |
| std::vector<Location> ResolveAddressInputLocation(const SymbolContext& symbol_context, |
| const InputLocation& input_location, |
| const ResolveOptions& options) const; |
| |
| // Symbolizes the given address if possible. The function can be specified if it's already known |
| // (some code paths have to compute that before calling). Otherwise, the address will be looked up |
| // and the function computed if necessary. |
| // |
| // The DWARF and ELF versions can fail to match a symbol for that category. The general version |
| // will try both and will return a raw address if nothing matches. |
| Location LocationForAddress(const SymbolContext& symbol_context, uint64_t absolute_address, |
| const ResolveOptions& options, const Function* optional_func) const; |
| std::optional<Location> DwarfLocationForAddress(const SymbolContext& symbol_context, |
| uint64_t absolute_address, |
| const ResolveOptions& options, |
| const Function* optional_func) const; |
| std::optional<Location> ElfLocationForAddress(const SymbolContext& symbol_context, |
| uint64_t absolute_address, |
| const ResolveOptions& options) const; |
| |
| // Converts the given global or static variable to a Location. This doesn't work for local |
| // variables which are dynamic and based on the current CPU state and stack. |
| Location LocationForVariable(const SymbolContext& symbol_context, |
| fxl::RefPtr<Variable> variable) const; |
| |
| // Converts a Function object to a found location according to the options and adds it to the |
| // list. May append nothing if there is no code for the function. |
| void AppendLocationForFunction(const SymbolContext& symbol_context, const ResolveOptions& options, |
| const Function* func, std::vector<Location>* result) const; |
| |
| // Looks up an ELF or PLT symbol. The symbol name only matches the mangled names and must not |
| // include the "$elf(...)" or "$plt(...)" annotation. Returns a vector of one entry if found, an |
| // empty vector if not. |
| std::vector<Location> ResolvePltName(const SymbolContext& symbol_context, |
| const std::string& mangled_name) const; |
| std::vector<Location> ResolveElfName(const SymbolContext& symbol_context, |
| const std::string& mangled_name) const; |
| |
| // Resolves the line number information for the given file, which must be an exact match. This is |
| // a helper function for ResolveLineInputLocation(). |
| // |
| // This appends to the given output. |
| void ResolveLineInputLocationForFile(const SymbolContext& symbol_context, |
| const std::string& canonical_file, int line_number, |
| const ResolveOptions& options, |
| std::vector<Location>* output) const; |
| |
| // Constructs an ElfSymbol object for the given record. |
| // |
| // Lookup for an address wants to return a Location identifying that exact location, even if |
| // the address in question isn't at the beginning of the symbol. Such callers can specify the |
| // relative address that the returned Location should have. If nullopt, the address for the |
| // location will be the beginning of the ELF symbol. |
| Location MakeElfSymbolLocation(const SymbolContext& symbol_context, |
| std::optional<uint64_t> relative_address, |
| const ElfSymbolRecord& record) const; |
| |
| // Fills the forward and backward indices for ELF symbols. |
| void FillElfSymbols(); |
| |
| std::unique_ptr<DwarfBinary> binary_; |
| |
| std::string build_dir_; |
| |
| Index index_; |
| |
| // Maps the mangled symbol name to the elf symbol record. There can technically be more than |
| // one entry for a name. |
| // |
| // This structure is also the canonical storage for ElfSymbolRecords used by elf_addresses_. |
| // |
| // We could potentially store this in the Index instead of in this separate location. This will be |
| // required if we need to support unmangled PLT names. A plan: |
| // |
| // - Declare that PLT symbols are encoded as "ns::ClassName::$plt(name)" since special |
| // annotations are per-component. |
| // - Make an "elf" type of IndexNode that the $plt component will match. |
| // - Rename IndexNode::DieRef to IndexNode::SymbolRef and convert the is_declaration flag to an |
| // enum. Add an enum value "ELF symbol". |
| // - Store the ELFSymbolRecords here in a vector that can be indexed into. Use the offset ot the |
| // IndexNode::SymbolRef to mean this index when it's an ELF type of SymbolRef. Hook this up to |
| // IndexDieRefToSymbol. |
| using ElfSymbolMap = std::multimap<std::string, const ElfSymbolRecord>; |
| ElfSymbolMap mangled_elf_symbols_; |
| |
| // All symbols in the mangled_elf_symbols_ map (pointers owned by that structure) sorted by the |
| // relative address. Theoretically there can be more than one symbol for the same address. |
| std::vector<const ElfSymbolRecord*> elf_addresses_; |
| |
| fxl::RefPtr<DwarfSymbolFactory> symbol_factory_; |
| |
| fxl::WeakPtrFactory<ModuleSymbolsImpl> weak_factory_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(ModuleSymbolsImpl); |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_MODULE_SYMBOLS_IMPL_H_ |