1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 2 // -*- mode: C++ -*- 3 // 4 // Copyright 2022 Google LLC 5 // 6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the 7 // "License"); you may not use this file except in compliance with the 8 // License. You may obtain a copy of the License at 9 // 10 // https://llvm.org/LICENSE.txt 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 // 18 // Author: Aleksei Vetrov 19 20 #ifndef STG_DWARF_WRAPPERS_H_ 21 #define STG_DWARF_WRAPPERS_H_ 22 23 #include <elf.h> 24 #include <elfutils/libdw.h> 25 #include <elfutils/libdwfl.h> 26 27 #include <cstddef> 28 #include <cstdint> 29 #include <memory> 30 #include <optional> 31 #include <ostream> 32 #include <string> 33 #include <tuple> 34 #include <vector> 35 36 namespace stg { 37 namespace dwarf { 38 39 struct Address { 40 // TODO: use auto operator<=> 41 bool operator<(const Address& other) const { 42 return std::tie(value, is_tls) < std::tie(other.value, other.is_tls); 43 } 44 45 bool operator==(const Address& other) const { 46 return value == other.value && is_tls == other.is_tls; 47 } 48 49 uint64_t value; 50 bool is_tls; 51 }; 52 53 std::ostream& operator<<(std::ostream& os, const Address& address); 54 55 // C++ wrapper over Dwarf_Die, providing interface for its various properties. 56 struct Entry { 57 // All methods in libdw take Dwarf_Die by non-const pointer as libdw caches 58 // in it a link to the associated abbreviation table. Updating this link is 59 // not thread-safe and so we cannot, for example, hold a std::shared_ptr to a 60 // heap-allocated Dwarf_Die. 61 // 62 // The only options left are holding a std::unique_ptr or storing a value. 63 // Unique pointers will add one more level of indirection to a hot path. 64 // So we choose to store Dwarf_Die values. 65 // 66 // Each Entry only contains references to DWARF file memory and is fairly 67 // small (32 bytes), so copies can be easily made if necessary. However, 68 // within one thread it is preferable to pass it by reference. 69 Dwarf_Die die{}; 70 71 // Get list of direct descendants of an entry in the DWARF tree. 72 std::vector<Entry> GetChildren(); 73 74 // All getters are non-const as libdw may need to modify Dwarf_Die. 75 int GetTag(); 76 Dwarf_Off GetOffset(); 77 std::optional<std::string> MaybeGetString(uint32_t attribute); 78 std::optional<std::string> MaybeGetDirectString(uint32_t attribute); 79 std::optional<uint64_t> MaybeGetUnsignedConstant(uint32_t attribute); 80 uint64_t MustGetUnsignedConstant(uint32_t attribute); 81 bool GetFlag(uint32_t attribute); 82 std::optional<Entry> MaybeGetReference(uint32_t attribute); 83 std::optional<Address> MaybeGetAddress(uint32_t attribute); 84 std::optional<uint64_t> MaybeGetMemberByteOffset(); 85 std::optional<uint64_t> MaybeGetVtableOffset(); 86 // Returns value of subrange element count if it is constant or nullopt if it 87 // is not defined or cannot be represented as constant. 88 std::optional<uint64_t> MaybeGetCount(); 89 }; 90 91 // Metadata and top-level entry of a compilation unit. 92 struct CompilationUnit { 93 int version; 94 Entry entry; 95 }; 96 97 // C++ wrapper over libdw (DWARF library). 98 // 99 // Creates a "Dwarf" object from an ELF file or a memory and controls the life 100 // cycle of the created objects. 101 class Handler { 102 public: 103 explicit Handler(const std::string& path); 104 Handler(char* data, size_t size); 105 106 Elf* GetElf(); 107 std::vector<CompilationUnit> GetCompilationUnits(); 108 109 private: 110 struct DwflDeleter { operatorDwflDeleter111 void operator()(Dwfl* dwfl) { 112 dwfl_end(dwfl); 113 } 114 }; 115 116 void InitialiseDwarf(); 117 118 std::unique_ptr<Dwfl, DwflDeleter> dwfl_; 119 // Lifetime of Dwfl_Module and Dwarf is controlled by Dwfl. 120 Dwfl_Module* dwfl_module_ = nullptr; 121 Dwarf* dwarf_ = nullptr; 122 }; 123 124 class Files { 125 public: 126 Files() = default; 127 explicit Files(Entry& compilation_unit); 128 std::optional<std::string> MaybeGetFile(Entry& entry, 129 uint32_t attribute) const; 130 131 private: 132 Dwarf_Files* files_ = nullptr; 133 size_t files_count_ = 0; 134 }; 135 136 } // namespace dwarf 137 } // namespace stg 138 139 #endif // STG_DWARF_WRAPPERS_H_ 140