| //===-- 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(); |
| } |
| |
| bool DWARFExpressionList::LinkThreadLocalStorage( |
| lldb::ModuleSP new_module_sp, |
| std::function<lldb::addr_t(lldb::addr_t file_addr)> 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(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); |
| } |
| } |
| |
| bool DWARFExpressionList::Evaluate(ExecutionContext *exe_ctx, |
| RegisterContext *reg_ctx, |
| lldb::addr_t func_load_addr, |
| const Value *initial_value_ptr, |
| const Value *object_address_ptr, |
| Value &result, Status *error_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 false; |
| RegisterContextSP reg_ctx_sp = frame->GetRegisterContext(); |
| if (!reg_ctx_sp) |
| return false; |
| reg_ctx_sp->GetPCForSymbolication(pc); |
| } |
| |
| if (!pc.IsValid()) { |
| if (error_ptr) |
| error_ptr->SetErrorString("Invalid PC in frame."); |
| return false; |
| } |
| addr_t pc_load_addr = pc.GetLoadAddress(exe_ctx->GetTargetPtr()); |
| const DWARFExpression *entry = |
| GetExpressionAtAddress(func_load_addr, pc_load_addr); |
| if (!entry) { |
| if (error_ptr) { |
| error_ptr->SetErrorString("variable not available"); |
| } |
| return false; |
| } |
| 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, result, error_ptr); |
| } |