//===-- DWARFExpressionList.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "lldb/Expression/DWARFExpressionList.h" #include "Plugins/SymbolFile/DWARF/DWARFUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" using namespace lldb; using namespace lldb_private; bool DWARFExpressionList::IsAlwaysValidSingleExpr() const { return GetAlwaysValidExpr() != nullptr; } const DWARFExpression * DWARFExpressionList::GetAlwaysValidExpr() const { if (m_exprs.GetSize() != 1) return nullptr; const auto *expr = m_exprs.GetEntryAtIndex(0); if (expr->base == 0 && expr->size == LLDB_INVALID_ADDRESS) return &expr->data; return nullptr; } bool DWARFExpressionList::AddExpression(addr_t base, addr_t end, DWARFExpression expr) { if (IsAlwaysValidSingleExpr() || base >= end) return false; m_exprs.Append({base, end - base, expr}); return true; } bool DWARFExpressionList::GetExpressionData(DataExtractor &data, lldb::addr_t func_load_addr, lldb::addr_t file_addr) const { if (const DWARFExpression *expr = GetExpressionAtAddress(func_load_addr, file_addr)) return expr->GetExpressionData(data); return false; } bool DWARFExpressionList::ContainsAddress(lldb::addr_t func_load_addr, lldb::addr_t addr) const { if (IsAlwaysValidSingleExpr()) return true; return GetExpressionAtAddress(func_load_addr, addr) != nullptr; } const DWARFExpression * DWARFExpressionList::GetExpressionAtAddress(lldb::addr_t func_load_addr, lldb::addr_t load_addr) const { if (const DWARFExpression *expr = GetAlwaysValidExpr()) return expr; if (func_load_addr == LLDB_INVALID_ADDRESS) func_load_addr = m_func_file_addr; addr_t addr = load_addr - func_load_addr + m_func_file_addr; uint32_t index = m_exprs.FindEntryIndexThatContains(addr); if (index == UINT32_MAX) return nullptr; return &m_exprs.GetEntryAtIndex(index)->data; } DWARFExpression * DWARFExpressionList::GetMutableExpressionAtAddress(lldb::addr_t func_load_addr, lldb::addr_t load_addr) { if (IsAlwaysValidSingleExpr()) return &m_exprs.GetMutableEntryAtIndex(0)->data; if (func_load_addr == LLDB_INVALID_ADDRESS) func_load_addr = m_func_file_addr; addr_t addr = load_addr - func_load_addr + m_func_file_addr; uint32_t index = m_exprs.FindEntryIndexThatContains(addr); if (index == UINT32_MAX) return nullptr; return &m_exprs.GetMutableEntryAtIndex(index)->data; } bool DWARFExpressionList::ContainsThreadLocalStorage() const { // We are assuming for now that any thread local variable will not have a // location list. This has been true for all thread local variables we have // seen so far produced by any compiler. if (!IsAlwaysValidSingleExpr()) return false; const DWARFExpression &expr = m_exprs.GetEntryRef(0).data; return expr.ContainsThreadLocalStorage(m_dwarf_cu); } bool DWARFExpressionList::LinkThreadLocalStorage( lldb::ModuleSP new_module_sp, std::function const &link_address_callback) { // We are assuming for now that any thread local variable will not have a // location list. This has been true for all thread local variables we have // seen so far produced by any compiler. if (!IsAlwaysValidSingleExpr()) return false; DWARFExpression &expr = m_exprs.GetEntryRef(0).data; // If we linked the TLS address correctly, update the module so that when the // expression is evaluated it can resolve the file address to a load address // and read the TLS data if (expr.LinkThreadLocalStorage(m_dwarf_cu, link_address_callback)) m_module_wp = new_module_sp; return true; } bool DWARFExpressionList::MatchesOperand( StackFrame &frame, const Instruction::Operand &operand) const { RegisterContextSP reg_ctx_sp = frame.GetRegisterContext(); if (!reg_ctx_sp) { return false; } const DWARFExpression *expr = nullptr; if (IsAlwaysValidSingleExpr()) expr = &m_exprs.GetEntryAtIndex(0)->data; else { SymbolContext sc = frame.GetSymbolContext(eSymbolContextFunction); if (!sc.function) return false; addr_t load_function_start = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); if (load_function_start == LLDB_INVALID_ADDRESS) return false; addr_t pc = frame.GetFrameCodeAddressForSymbolication().GetFileAddress(); expr = GetExpressionAtAddress(LLDB_INVALID_ADDRESS, pc); } if (!expr) return false; return expr->MatchesOperand(frame, operand); } bool DWARFExpressionList::DumpLocations(Stream *s, lldb::DescriptionLevel level, lldb::addr_t func_load_addr, lldb::addr_t file_addr, ABI *abi) const { llvm::raw_ostream &os = s->AsRawOstream(); llvm::ListSeparator separator; if (const DWARFExpression *expr = GetAlwaysValidExpr()) { expr->DumpLocation(s, level, abi); return true; } for (const Entry &entry : *this) { addr_t load_base = entry.GetRangeBase() + func_load_addr - m_func_file_addr; addr_t load_end = entry.GetRangeEnd() + func_load_addr - m_func_file_addr; if (file_addr != LLDB_INVALID_ADDRESS && (file_addr < load_base || file_addr >= load_end)) continue; const auto &expr = entry.data; DataExtractor data; expr.GetExpressionData(data); uint32_t addr_size = data.GetAddressByteSize(); os << separator; os << "["; os << llvm::format_hex(load_base, 2 + 2 * addr_size); os << ", "; os << llvm::format_hex(load_end, 2 + 2 * addr_size); os << ") -> "; expr.DumpLocation(s, level, abi); if (file_addr != LLDB_INVALID_ADDRESS) break; } return true; } void DWARFExpressionList::GetDescription(Stream *s, lldb::DescriptionLevel level, ABI *abi) const { llvm::raw_ostream &os = s->AsRawOstream(); if (IsAlwaysValidSingleExpr()) { m_exprs.Back()->data.DumpLocation(s, level, abi); return; } os << llvm::format("0x%8.8" PRIx64 ": ", 0); for (const Entry &entry : *this) { const auto &expr = entry.data; DataExtractor data; expr.GetExpressionData(data); uint32_t addr_size = data.GetAddressByteSize(); os << "\n"; os.indent(s->GetIndentLevel() + 2); os << "["; llvm::DWARFFormValue::dumpAddress(os, addr_size, entry.GetRangeBase()); os << ", "; llvm::DWARFFormValue::dumpAddress(os, addr_size, entry.GetRangeEnd()); os << "): "; expr.DumpLocation(s, level, abi); } } llvm::Expected DWARFExpressionList::Evaluate( ExecutionContext *exe_ctx, RegisterContext *reg_ctx, lldb::addr_t func_load_addr, const Value *initial_value_ptr, const Value *object_address_ptr) const { ModuleSP module_sp = m_module_wp.lock(); DataExtractor data; RegisterKind reg_kind; DWARFExpression expr; if (IsAlwaysValidSingleExpr()) { expr = m_exprs.Back()->data; } else { Address pc; StackFrame *frame = nullptr; if (!reg_ctx || !reg_ctx->GetPCForSymbolication(pc)) { if (exe_ctx) frame = exe_ctx->GetFramePtr(); if (!frame) return llvm::createStringError("no frame"); RegisterContextSP reg_ctx_sp = frame->GetRegisterContext(); if (!reg_ctx_sp) return llvm::createStringError("no register context"); reg_ctx_sp->GetPCForSymbolication(pc); } if (!pc.IsValid()) { return llvm::createStringError("Invalid PC in frame."); } addr_t pc_load_addr = pc.GetLoadAddress(exe_ctx->GetTargetPtr()); const DWARFExpression *entry = GetExpressionAtAddress(func_load_addr, pc_load_addr); if (!entry) return llvm::createStringError("variable not available"); expr = *entry; } expr.GetExpressionData(data); reg_kind = expr.GetRegisterKind(); return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, data, m_dwarf_cu, reg_kind, initial_value_ptr, object_address_ptr); }