File indexing completed on 2024-05-05 05:44:15
0001 /* 0002 dwarfdiecache.cpp 0003 0004 SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com> 0005 SPDX-FileContributor: Milian Wolff <milian.wolff@kdab.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "dwarfdiecache.h" 0011 0012 #include <dwarf.h> 0013 0014 #include <cxxabi.h> 0015 0016 #include <cstring> 0017 0018 namespace { 0019 enum class WalkResult 0020 { 0021 Recurse, 0022 Skip, 0023 Return 0024 }; 0025 template <typename Callback> 0026 WalkResult walkDieTree(const Callback& callback, Dwarf_Die* die) 0027 { 0028 auto result = callback(die); 0029 if (result != WalkResult::Recurse) 0030 return result; 0031 0032 Dwarf_Die childDie; 0033 if (dwarf_child(die, &childDie) == 0) { 0034 result = walkDieTree(callback, &childDie); 0035 if (result == WalkResult::Return) 0036 return result; 0037 0038 Dwarf_Die siblingDie; 0039 while (dwarf_siblingof(&childDie, &siblingDie) == 0) { 0040 result = walkDieTree(callback, &siblingDie); 0041 if (result == WalkResult::Return) 0042 return result; 0043 childDie = siblingDie; 0044 } 0045 } 0046 return WalkResult::Skip; 0047 } 0048 0049 template <typename Callback> 0050 void walkRanges(const Callback& callback, Dwarf_Die* die) 0051 { 0052 Dwarf_Addr low = 0; 0053 Dwarf_Addr high = 0; 0054 Dwarf_Addr base = 0; 0055 ptrdiff_t rangeOffset = 0; 0056 while ((rangeOffset = dwarf_ranges(die, rangeOffset, &base, &low, &high)) > 0) { 0057 if (!callback(DwarfRange {low, high})) 0058 return; 0059 } 0060 } 0061 0062 // see libdw_visit_scopes.c in elfutils 0063 bool mayHaveScopes(Dwarf_Die* die) 0064 { 0065 switch (dwarf_tag(die)) { 0066 /* DIEs with addresses we can try to match. */ 0067 case DW_TAG_compile_unit: 0068 case DW_TAG_module: 0069 case DW_TAG_lexical_block: 0070 case DW_TAG_with_stmt: 0071 case DW_TAG_catch_block: 0072 case DW_TAG_try_block: 0073 case DW_TAG_entry_point: 0074 case DW_TAG_inlined_subroutine: 0075 case DW_TAG_subprogram: 0076 return true; 0077 0078 /* DIEs without addresses that can own DIEs with addresses. */ 0079 case DW_TAG_namespace: 0080 case DW_TAG_class_type: 0081 case DW_TAG_structure_type: 0082 return true; 0083 0084 /* Other DIEs we have no reason to descend. */ 0085 default: 0086 break; 0087 } 0088 return false; 0089 } 0090 0091 bool dieContainsAddress(Dwarf_Die* die, Dwarf_Addr address) 0092 { 0093 bool contained = false; 0094 walkRanges( 0095 [&contained, address](DwarfRange range) { 0096 if (range.contains(address)) { 0097 contained = true; 0098 return false; 0099 } 0100 return true; 0101 }, 0102 die); 0103 return contained; 0104 } 0105 } 0106 0107 const char* linkageName(Dwarf_Die* die) 0108 { 0109 Dwarf_Attribute attr; 0110 Dwarf_Attribute* result = dwarf_attr_integrate(die, DW_AT_MIPS_linkage_name, &attr); 0111 if (!result) 0112 result = dwarf_attr_integrate(die, DW_AT_linkage_name, &attr); 0113 0114 return result ? dwarf_formstring(result) : nullptr; 0115 } 0116 0117 Dwarf_Die* specificationDie(Dwarf_Die* die, Dwarf_Die* dieMem) 0118 { 0119 Dwarf_Attribute attr; 0120 if (dwarf_attr_integrate(die, DW_AT_specification, &attr)) 0121 return dwarf_formref_die(&attr, dieMem); 0122 return nullptr; 0123 } 0124 0125 /// prepend the names of all scopes that reference the @p die to @p name 0126 void prependScopeNames(std::string& name, Dwarf_Die* die, tsl::robin_map<Dwarf_Off, std::string>& cache) 0127 { 0128 Dwarf_Die dieMem; 0129 Dwarf_Die* scopes = nullptr; 0130 auto nscopes = dwarf_getscopes_die(die, &scopes); 0131 0132 struct ScopesToCache 0133 { 0134 Dwarf_Off offset; 0135 std::size_t trailing; 0136 }; 0137 std::vector<ScopesToCache> cacheOps; 0138 0139 // skip scope for the die itself at the start and the compile unit DIE at end 0140 for (int i = 1; i < nscopes - 1; ++i) { 0141 auto scope = scopes + i; 0142 0143 const auto scopeOffset = dwarf_dieoffset(scope); 0144 0145 auto it = cache.find(scopeOffset); 0146 if (it != cache.end()) { 0147 name.insert(0, "::"); 0148 name.insert(0, it->second); 0149 // we can stop, cached names are always fully qualified 0150 break; 0151 } 0152 0153 if (auto scopeLinkageName = linkageName(scope)) { 0154 // prepend the fully qualified linkage name 0155 name.insert(0, "::"); 0156 cacheOps.push_back({scopeOffset, name.size()}); 0157 // we have to demangle the scope linkage name, otherwise we get a 0158 // mish-mash of mangled and non-mangled names 0159 name.insert(0, demangle(scopeLinkageName)); 0160 // we can stop now, the scope is fully qualified 0161 break; 0162 } 0163 0164 if (auto scopeName = dwarf_diename(scope)) { 0165 // prepend this scope's name, e.g. the class or namespace name 0166 name.insert(0, "::"); 0167 cacheOps.push_back({scopeOffset, name.size()}); 0168 name.insert(0, scopeName); 0169 } 0170 0171 if (auto specification = specificationDie(scope, &dieMem)) { 0172 free(scopes); 0173 scopes = nullptr; 0174 cacheOps.push_back({scopeOffset, name.size()}); 0175 cacheOps.push_back({dwarf_dieoffset(specification), name.size()}); 0176 // follow the scope's specification DIE instead 0177 prependScopeNames(name, specification, cache); 0178 break; 0179 } 0180 } 0181 0182 for (const auto& cacheOp : cacheOps) 0183 cache[cacheOp.offset] = name.substr(0, name.size() - cacheOp.trailing); 0184 0185 free(scopes); 0186 } 0187 0188 bool operator==(const Dwarf_Die& lhs, const Dwarf_Die& rhs) 0189 { 0190 return lhs.addr == rhs.addr && lhs.cu == rhs.cu && lhs.abbrev == rhs.abbrev; 0191 } 0192 0193 std::string qualifiedDieName(Dwarf_Die* die, tsl::robin_map<Dwarf_Off, std::string>& cache) 0194 { 0195 // linkage names are fully qualified, meaning we can stop early then 0196 if (auto name = linkageName(die)) 0197 return name; 0198 0199 // otherwise do a more complex lookup that includes namespaces and other context information 0200 // this is important for inlined subroutines such as lambdas or std:: algorithms 0201 std::string name; 0202 if (auto dieName = dwarf_diename(die)) 0203 name = dieName; 0204 0205 // use the specification DIE which is within the DW_TAG_namespace 0206 Dwarf_Die dieMem; 0207 if (auto specification = specificationDie(die, &dieMem)) 0208 die = specification; 0209 0210 prependScopeNames(name, die, cache); 0211 0212 return name; 0213 } 0214 0215 std::string demangle(const std::string& mangledName) 0216 { 0217 if (mangledName.length() < 3) { 0218 return mangledName; 0219 } else { 0220 static size_t demangleBufferLength = 1024; 0221 static char* demangleBuffer = reinterpret_cast<char*>(malloc(demangleBufferLength)); 0222 0223 // Require GNU v3 ABI by the "_Z" prefix. 0224 if (mangledName[0] == '_' && mangledName[1] == 'Z') { 0225 int status = -1; 0226 char* dsymname = abi::__cxa_demangle(mangledName.data(), demangleBuffer, &demangleBufferLength, &status); 0227 if (status == 0 && dsymname) 0228 return demangleBuffer = dsymname; 0229 } 0230 } 0231 return mangledName; 0232 } 0233 0234 std::string absoluteSourcePath(const char* path, Dwarf_Die* cuDie) 0235 { 0236 if (!path || !cuDie || path[0] == '/') 0237 return path; 0238 0239 Dwarf_Attribute attr; 0240 auto compDir = dwarf_formstring(dwarf_attr(cuDie, DW_AT_comp_dir, &attr)); 0241 if (!compDir) 0242 return path; 0243 0244 std::string ret; 0245 ret.reserve(static_cast<int>(strlen(compDir) + strlen(path) + 1)); 0246 ret.append(compDir); 0247 ret.append("/"); 0248 ret.append(path); 0249 return ret; 0250 } 0251 0252 SourceLocation callSourceLocation(Dwarf_Die* die, Dwarf_Files* files, Dwarf_Die* cuDie) 0253 { 0254 SourceLocation ret; 0255 0256 Dwarf_Attribute attr; 0257 Dwarf_Word val = 0; 0258 0259 const auto hasCallFile = dwarf_formudata(dwarf_attr(die, DW_AT_call_file, &attr), &val) == 0; 0260 if (hasCallFile) { 0261 ret.file = absoluteSourcePath(dwarf_filesrc(files, val, nullptr, nullptr), cuDie); 0262 } 0263 0264 const auto hasCallLine = dwarf_formudata(dwarf_attr(die, DW_AT_call_line, &attr), &val) == 0; 0265 if (hasCallLine) { 0266 ret.line = static_cast<int>(val); 0267 } 0268 0269 return ret; 0270 } 0271 0272 std::vector<Dwarf_Die> findInlineScopes(Dwarf_Die* subprogram, Dwarf_Addr offset) 0273 { 0274 std::vector<Dwarf_Die> scopes; 0275 walkDieTree( 0276 [offset, &scopes](Dwarf_Die* die) { 0277 if (dwarf_tag(die) != DW_TAG_inlined_subroutine) 0278 return WalkResult::Recurse; 0279 if (dieContainsAddress(die, offset)) { 0280 scopes.push_back(*die); 0281 return WalkResult::Recurse; 0282 } 0283 return WalkResult::Skip; 0284 }, 0285 subprogram); 0286 return scopes; 0287 } 0288 0289 SubProgramDie::SubProgramDie(Dwarf_Die die) 0290 : m_ranges {die, {}} 0291 { 0292 walkRanges( 0293 [this](DwarfRange range) { 0294 m_ranges.ranges.push_back(range); 0295 return true; 0296 }, 0297 &die); 0298 } 0299 0300 CuDieRangeMapping::CuDieRangeMapping(Dwarf_Die cudie, Dwarf_Addr bias) 0301 : m_bias {bias} 0302 , m_cuDieRanges {cudie, {}} 0303 { 0304 walkRanges( 0305 [this, bias](DwarfRange range) { 0306 m_cuDieRanges.ranges.push_back({range.low + bias, range.high + bias}); 0307 return true; 0308 }, 0309 &cudie); 0310 } 0311 0312 SubProgramDie* CuDieRangeMapping::findSubprogramDie(Dwarf_Addr offset) 0313 { 0314 if (m_subPrograms.empty()) 0315 addSubprograms(); 0316 0317 auto it = std::find_if(m_subPrograms.begin(), m_subPrograms.end(), 0318 [offset](const SubProgramDie& program) { return program.contains(offset); }); 0319 if (it == m_subPrograms.end()) 0320 return nullptr; 0321 0322 return &(*it); 0323 } 0324 0325 void CuDieRangeMapping::addSubprograms() 0326 { 0327 walkDieTree( 0328 [this](Dwarf_Die* die) { 0329 if (!mayHaveScopes(die)) 0330 return WalkResult::Skip; 0331 0332 if (dwarf_tag(die) == DW_TAG_subprogram) { 0333 SubProgramDie program(*die); 0334 if (!program.isEmpty()) 0335 m_subPrograms.push_back(program); 0336 0337 return WalkResult::Skip; 0338 } 0339 return WalkResult::Recurse; 0340 }, 0341 cudie()); 0342 } 0343 0344 const std::string& CuDieRangeMapping::dieName(Dwarf_Die* die) 0345 { 0346 const auto offset = dwarf_dieoffset(die); 0347 auto it = m_dieNameCache.find(offset); 0348 if (it == m_dieNameCache.end()) 0349 it = m_dieNameCache.insert({offset, demangle(qualifiedDieName(die, m_dieNameCache))}).first; 0350 0351 return it->second; 0352 } 0353 0354 DwarfDieCache::DwarfDieCache(Dwfl_Module* mod) 0355 { 0356 if (!mod) 0357 return; 0358 0359 Dwarf_Die* die = nullptr; 0360 Dwarf_Addr bias = 0; 0361 while ((die = dwfl_module_nextcu(mod, die, &bias))) { 0362 CuDieRangeMapping cuDieMapping(*die, bias); 0363 if (!cuDieMapping.isEmpty()) 0364 m_cuDieRanges.push_back(cuDieMapping); 0365 } 0366 } 0367 0368 CuDieRangeMapping* DwarfDieCache::findCuDie(Dwarf_Addr addr) 0369 { 0370 auto it = std::find_if(m_cuDieRanges.begin(), m_cuDieRanges.end(), 0371 [addr](const CuDieRangeMapping& cuDieMapping) { return cuDieMapping.contains(addr); }); 0372 if (it == m_cuDieRanges.end()) 0373 return nullptr; 0374 0375 return &(*it); 0376 }