Page MenuHomePhabricator

No OneTemporary

File Metadata

Created
Mon, Nov 18, 9:08 AM
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/lldb/include/lldb/Symbol/Function.h b/lldb/include/lldb/Symbol/Function.h
index b0a7a0d..ba4839e 100644
--- a/lldb/include/lldb/Symbol/Function.h
+++ b/lldb/include/lldb/Symbol/Function.h
@@ -1,581 +1,601 @@
//===-- Function.h ----------------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_Function_h_
#define liblldb_Function_h_
#include "lldb/Core/AddressRange.h"
#include "lldb/Core/Mangled.h"
#include "lldb/Expression/DWARFExpression.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/Declaration.h"
#include "lldb/Utility/UserID.h"
#include "llvm/ADT/ArrayRef.h"
namespace lldb_private {
/// \class FunctionInfo Function.h "lldb/Symbol/Function.h"
/// A class that contains generic function information.
///
/// This provides generic function information that gets reused between inline
/// functions and function types.
class FunctionInfo {
public:
/// Construct with the function method name and optional declaration
/// information.
///
/// \param[in] name
/// A C string name for the method name for this function. This
/// value should not be the mangled named, but the simple method
/// name.
///
/// \param[in] decl_ptr
/// Optional declaration information that describes where the
/// function was declared. This can be NULL.
FunctionInfo(const char *name, const Declaration *decl_ptr);
/// Construct with the function method name and optional declaration
/// information.
///
/// \param[in] name
/// A name for the method name for this function. This value
/// should not be the mangled named, but the simple method name.
///
/// \param[in] decl_ptr
/// Optional declaration information that describes where the
/// function was declared. This can be NULL.
FunctionInfo(ConstString name, const Declaration *decl_ptr);
/// Destructor.
///
/// The destructor is virtual since classes inherit from this class.
virtual ~FunctionInfo();
/// Compare two function information objects.
///
/// First compares the method names, and if equal, then compares the
/// declaration information.
///
/// \param[in] lhs
/// The Left Hand Side const FunctionInfo object reference.
///
/// \param[in] rhs
/// The Right Hand Side const FunctionInfo object reference.
///
/// \return
/// \li -1 if lhs < rhs
/// \li 0 if lhs == rhs
/// \li 1 if lhs > rhs
static int Compare(const FunctionInfo &lhs, const FunctionInfo &rhs);
/// Dump a description of this object to a Stream.
///
/// Dump a description of the contents of this object to the supplied stream
/// \a s.
///
/// \param[in] s
/// The stream to which to dump the object description.
void Dump(Stream *s, bool show_fullpaths) const;
/// Get accessor for the declaration information.
///
/// \return
/// A reference to the declaration object.
Declaration &GetDeclaration();
/// Get const accessor for the declaration information.
///
/// \return
/// A const reference to the declaration object.
const Declaration &GetDeclaration() const;
/// Get accessor for the method name.
///
/// \return
/// A const reference to the method name object.
ConstString GetName() const;
/// Get the memory cost of this object.
///
/// \return
/// The number of bytes that this object occupies in memory.
/// The returned value does not include the bytes for any
/// shared string values.
///
/// \see ConstString::StaticMemorySize ()
virtual size_t MemorySize() const;
protected:
// Member variables.
ConstString m_name; ///< Function method name (not a mangled name).
Declaration m_declaration; ///< Information describing where this function
///information was defined.
};
/// \class InlineFunctionInfo Function.h "lldb/Symbol/Function.h"
/// A class that describes information for an inlined function.
class InlineFunctionInfo : public FunctionInfo {
public:
/// Construct with the function method name, mangled name, and optional
/// declaration information.
///
/// \param[in] name
/// A C string name for the method name for this function. This
/// value should not be the mangled named, but the simple method
/// name.
///
/// \param[in] mangled
/// A C string name for the mangled name for this function. This
/// value can be NULL if there is no mangled information.
///
/// \param[in] decl_ptr
/// Optional declaration information that describes where the
/// function was declared. This can be NULL.
///
/// \param[in] call_decl_ptr
/// Optional calling location declaration information that
/// describes from where this inlined function was called.
InlineFunctionInfo(const char *name, const char *mangled,
const Declaration *decl_ptr,
const Declaration *call_decl_ptr);
/// Construct with the function method name, mangled name, and optional
/// declaration information.
///
/// \param[in] name
/// A name for the method name for this function. This value
/// should not be the mangled named, but the simple method name.
///
/// \param[in] mangled
/// A name for the mangled name for this function. This value
/// can be empty if there is no mangled information.
///
/// \param[in] decl_ptr
/// Optional declaration information that describes where the
/// function was declared. This can be NULL.
///
/// \param[in] call_decl_ptr
/// Optional calling location declaration information that
/// describes from where this inlined function was called.
InlineFunctionInfo(ConstString name, const Mangled &mangled,
const Declaration *decl_ptr,
const Declaration *call_decl_ptr);
/// Destructor.
~InlineFunctionInfo() override;
/// Compare two inlined function information objects.
///
/// First compares the FunctionInfo objects, and if equal, compares the
/// mangled names.
///
/// \param[in] lhs
/// The Left Hand Side const InlineFunctionInfo object
/// reference.
///
/// \param[in] rhs
/// The Right Hand Side const InlineFunctionInfo object
/// reference.
///
/// \return
/// \li -1 if lhs < rhs
/// \li 0 if lhs == rhs
/// \li 1 if lhs > rhs
int Compare(const InlineFunctionInfo &lhs, const InlineFunctionInfo &rhs);
/// Dump a description of this object to a Stream.
///
/// Dump a description of the contents of this object to the supplied stream
/// \a s.
///
/// \param[in] s
/// The stream to which to dump the object description.
void Dump(Stream *s, bool show_fullpaths) const;
void DumpStopContext(Stream *s, lldb::LanguageType language) const;
ConstString GetName(lldb::LanguageType language) const;
ConstString GetDisplayName(lldb::LanguageType language) const;
/// Get accessor for the call site declaration information.
///
/// \return
/// A reference to the declaration object.
Declaration &GetCallSite();
/// Get const accessor for the call site declaration information.
///
/// \return
/// A const reference to the declaration object.
const Declaration &GetCallSite() const;
/// Get accessor for the mangled name object.
///
/// \return
/// A reference to the mangled name object.
Mangled &GetMangled();
/// Get const accessor for the mangled name object.
///
/// \return
/// A const reference to the mangled name object.
const Mangled &GetMangled() const;
/// Get the memory cost of this object.
///
/// \return
/// The number of bytes that this object occupies in memory.
/// The returned value does not include the bytes for any
/// shared string values.
///
/// \see ConstString::StaticMemorySize ()
size_t MemorySize() const override;
private:
// Member variables.
Mangled m_mangled; ///< Mangled inlined function name (can be empty if there
///is no mangled information).
Declaration m_call_decl;
};
class Function;
+/// \class CallSiteParameter Function.h "lldb/Symbol/Function.h"
+///
+/// Represent the locations of a parameter at a call site, both in the caller
+/// and in the callee.
+struct CallSiteParameter {
+ DWARFExpression LocationInCallee;
+ DWARFExpression LocationInCaller;
+};
+
+/// A vector of \c CallSiteParameter.
+using CallSiteParameterArray = llvm::SmallVector<CallSiteParameter, 0>;
+
/// \class CallEdge Function.h "lldb/Symbol/Function.h"
///
/// Represent a call made within a Function. This can be used to find a path
-/// in the call graph between two functions.
+/// in the call graph between two functions, or to evaluate DW_OP_entry_value.
class CallEdge {
public:
/// Construct a call edge using a symbol name to identify the calling
/// function, and a return PC within the calling function to identify a
/// specific call site.
///
/// TODO: A symbol name may not be globally unique. To disambiguate ODR
/// conflicts, it's necessary to determine the \c Target a call edge is
/// associated with before resolving it.
- CallEdge(const char *symbol_name, lldb::addr_t return_pc);
+ CallEdge(const char *symbol_name, lldb::addr_t return_pc,
+ CallSiteParameterArray parameters);
CallEdge(CallEdge &&) = default;
CallEdge &operator=(CallEdge &&) = default;
/// Get the callee's definition.
///
/// Note that this might lazily invoke the DWARF parser.
Function *GetCallee(ModuleList &images);
/// Get the load PC address of the instruction which executes after the call
/// returns. Returns LLDB_INVALID_ADDRESS iff this is a tail call. \p caller
/// is the Function containing this call, and \p target is the Target which
/// made the call.
lldb::addr_t GetReturnPCAddress(Function &caller, Target &target) const;
/// Like \ref GetReturnPCAddress, but returns an unslid function-local PC
/// offset.
lldb::addr_t GetUnresolvedReturnPCAddress() const { return return_pc; }
+ /// Get the call site parameters available at this call edge.
+ llvm::ArrayRef<CallSiteParameter> GetCallSiteParameters() const;
+
private:
void ParseSymbolFileAndResolve(ModuleList &images);
/// Either the callee's mangled name or its definition, discriminated by
/// \ref resolved.
union {
const char *symbol_name;
Function *def;
} lazy_callee;
/// An invalid address if this is a tail call. Otherwise, the function-local
/// PC offset. Adding this PC offset to the function's base load address
/// gives the return PC for the call.
lldb::addr_t return_pc;
+ CallSiteParameterArray parameters;
+
/// Whether or not an attempt was made to find the callee's definition.
bool resolved;
DISALLOW_COPY_AND_ASSIGN(CallEdge);
};
/// \class Function Function.h "lldb/Symbol/Function.h"
/// A class that describes a function.
///
/// Functions belong to CompileUnit objects (Function::m_comp_unit), have
/// unique user IDs (Function::UserID), know how to reconstruct their symbol
/// context (Function::SymbolContextScope), have a specific function type
/// (Function::m_type_uid), have a simple method name (FunctionInfo::m_name),
/// be declared at a specific location (FunctionInfo::m_declaration), possibly
/// have mangled names (Function::m_mangled), an optional return type
/// (Function::m_type), and contains lexical blocks (Function::m_blocks).
///
/// The function information is split into a few pieces:
/// \li The concrete instance information
/// \li The abstract information
///
/// The abstract information is found in the function type (Type) that
/// describes a function information, return type and parameter types.
///
/// The concrete information is the address range information and specific
/// locations for an instance of this function.
class Function : public UserID, public SymbolContextScope {
public:
/// Construct with a compile unit, function UID, function type UID, optional
/// mangled name, function type, and a section offset based address range.
///
/// \param[in] comp_unit
/// The compile unit to which this function belongs.
///
/// \param[in] func_uid
/// The UID for this function. This value is provided by the
/// SymbolFile plug-in and can be any value that allows
/// the plug-in to quickly find and parse more detailed
/// information when and if more information is needed.
///
/// \param[in] func_type_uid
/// The type UID for the function Type to allow for lazy type
/// parsing from the debug information.
///
/// \param[in] mangled
/// The optional mangled name for this function. If empty, there
/// is no mangled information.
///
/// \param[in] func_type
/// The optional function type. If NULL, the function type will
/// be parsed on demand when accessed using the
/// Function::GetType() function by asking the SymbolFile
/// plug-in to get the type for \a func_type_uid.
///
/// \param[in] range
/// The section offset based address for this function.
Function(CompileUnit *comp_unit, lldb::user_id_t func_uid,
lldb::user_id_t func_type_uid, const Mangled &mangled,
Type *func_type, const AddressRange &range);
/// Destructor.
~Function() override;
/// \copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*)
///
/// \see SymbolContextScope
void CalculateSymbolContext(SymbolContext *sc) override;
lldb::ModuleSP CalculateSymbolContextModule() override;
CompileUnit *CalculateSymbolContextCompileUnit() override;
Function *CalculateSymbolContextFunction() override;
const AddressRange &GetAddressRange() { return m_range; }
lldb::LanguageType GetLanguage() const;
/// Find the file and line number of the source location of the start of the
/// function. This will use the declaration if present and fall back on the
/// line table if that fails. So there may NOT be a line table entry for
/// this source file/line combo.
///
/// \param[out] source_file
/// The source file.
///
/// \param[out] line_no
/// The line number.
void GetStartLineSourceInfo(FileSpec &source_file, uint32_t &line_no);
/// Find the file and line number of the source location of the end of the
/// function.
///
///
/// \param[out] source_file
/// The source file.
///
/// \param[out] line_no
/// The line number.
void GetEndLineSourceInfo(FileSpec &source_file, uint32_t &line_no);
/// Get the outgoing call edges from this function, sorted by their return
/// PC addresses (in increasing order).
llvm::MutableArrayRef<CallEdge> GetCallEdges();
/// Get the outgoing tail-calling edges from this function. If none exist,
/// return None.
llvm::MutableArrayRef<CallEdge> GetTailCallingEdges();
/// Get the outgoing call edge from this function which has the given return
/// address \p return_pc, or return nullptr. Note that this will not return a
/// tail-calling edge.
CallEdge *GetCallEdgeForReturnAddress(lldb::addr_t return_pc, Target &target);
/// Get accessor for the block list.
///
/// \return
/// The block list object that describes all lexical blocks
/// in the function.
///
/// \see BlockList
Block &GetBlock(bool can_create);
/// Get accessor for the compile unit that owns this function.
///
/// \return
/// A compile unit object pointer.
CompileUnit *GetCompileUnit();
/// Get const accessor for the compile unit that owns this function.
///
/// \return
/// A const compile unit object pointer.
const CompileUnit *GetCompileUnit() const;
void GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target);
/// Get accessor for the frame base location.
///
/// \return
/// A location expression that describes the function frame
/// base.
DWARFExpression &GetFrameBaseExpression() { return m_frame_base; }
/// Get const accessor for the frame base location.
///
/// \return
/// A const compile unit object pointer.
const DWARFExpression &GetFrameBaseExpression() const { return m_frame_base; }
ConstString GetName() const;
ConstString GetNameNoArguments() const;
ConstString GetDisplayName() const;
const Mangled &GetMangled() const { return m_mangled; }
/// Get the DeclContext for this function, if available.
///
/// \return
/// The DeclContext, or NULL if none exists.
CompilerDeclContext GetDeclContext();
/// Get accessor for the type that describes the function return value type,
/// and parameter types.
///
/// \return
/// A type object pointer.
Type *GetType();
/// Get const accessor for the type that describes the function return value
/// type, and parameter types.
///
/// \return
/// A const type object pointer.
const Type *GetType() const;
CompilerType GetCompilerType();
/// Get the size of the prologue instructions for this function. The
/// "prologue" instructions include any instructions given line number 0
/// immediately following the prologue end.
///
/// \return
/// The size of the prologue.
uint32_t GetPrologueByteSize();
/// Dump a description of this object to a Stream.
///
/// Dump a description of the contents of this object to the supplied stream
/// \a s.
///
/// \param[in] s
/// The stream to which to dump the object description.
///
/// \param[in] show_context
/// If \b true, variables will dump their symbol context
/// information.
void Dump(Stream *s, bool show_context) const;
/// \copydoc SymbolContextScope::DumpSymbolContext(Stream*)
///
/// \see SymbolContextScope
void DumpSymbolContext(Stream *s) override;
/// Get the memory cost of this object.
///
/// \return
/// The number of bytes that this object occupies in memory.
/// The returned value does not include the bytes for any
/// shared string values.
///
/// \see ConstString::StaticMemorySize ()
size_t MemorySize() const;
/// Get whether compiler optimizations were enabled for this function
///
/// The debug information may provide information about whether this
/// function was compiled with optimization or not. In this case,
/// "optimized" means that the debug experience may be difficult for the
/// user to understand. Variables may not be available when the developer
/// would expect them, stepping through the source lines in the function may
/// appear strange, etc.
///
/// \return
/// Returns 'true' if this function was compiled with
/// optimization. 'false' indicates that either the optimization
/// is unknown, or this function was built without optimization.
bool GetIsOptimized();
/// Get whether this function represents a 'top-level' function
///
/// The concept of a top-level function is language-specific, mostly meant
/// to represent the notion of scripting-style code that has global
/// visibility of the variables/symbols/functions/... defined within the
/// containing file/module
///
/// If stopped in a top-level function, LLDB will expose global variables
/// as-if locals in the 'frame variable' command
///
/// \return
/// Returns 'true' if this function is a top-level function,
/// 'false' otherwise.
bool IsTopLevelFunction();
lldb::DisassemblerSP GetInstructions(const ExecutionContext &exe_ctx,
const char *flavor,
bool prefer_file_cache);
bool GetDisassembly(const ExecutionContext &exe_ctx, const char *flavor,
bool prefer_file_cache, Stream &strm);
protected:
enum {
flagsCalculatedPrologueSize =
(1 << 0) ///< Have we already tried to calculate the prologue size?
};
// Member variables.
CompileUnit *m_comp_unit; ///< The compile unit that owns this function.
lldb::user_id_t
m_type_uid; ///< The user ID of for the prototype Type for this function.
Type *m_type; ///< The function prototype type for this function that include
///the function info (FunctionInfo), return type and parameters.
Mangled m_mangled; ///< The mangled function name if any, if empty, there is
///no mangled information.
Block m_block; ///< All lexical blocks contained in this function.
AddressRange m_range; ///< The function address range that covers the widest
///range needed to contain all blocks
DWARFExpression m_frame_base; ///< The frame base expression for variables
///that are relative to the frame pointer.
Flags m_flags;
uint32_t
m_prologue_byte_size; ///< Compute the prologue size once and cache it
+ // TODO: Use a layer of indirection to point to call edges, to save space
+ // when call info hasn't been parsed.
bool m_call_edges_resolved = false; ///< Whether call site info has been
/// parsed.
std::vector<CallEdge> m_call_edges; ///< Outgoing call edges.
private:
DISALLOW_COPY_AND_ASSIGN(Function);
};
} // namespace lldb_private
#endif // liblldb_Function_h_
diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py
index 17b97d5..5d9838f 100644
--- a/lldb/packages/Python/lldbsuite/test/decorators.py
+++ b/lldb/packages/Python/lldbsuite/test/decorators.py
@@ -1,833 +1,843 @@
from __future__ import absolute_import
from __future__ import print_function
# System modules
from distutils.version import LooseVersion
from functools import wraps
import os
import platform
import re
import sys
import tempfile
import subprocess
# Third-party modules
import six
import unittest2
# LLDB modules
import lldb
from . import configuration
from . import test_categories
from . import lldbtest_config
from lldbsuite.test_event.event_builder import EventBuilder
from lldbsuite.support import funcutils
from lldbsuite.test import lldbplatform
from lldbsuite.test import lldbplatformutil
class DecorateMode:
Skip, Xfail = range(2)
# You can use no_match to reverse the test of the conditional that is used to match keyword
# arguments in the skip / xfail decorators. If oslist=["windows", "linux"] skips windows
# and linux, oslist=no_match(["windows", "linux"]) skips *unless* windows
# or linux.
class no_match:
def __init__(self, item):
self.item = item
def _check_expected_version(comparison, expected, actual):
def fn_leq(x, y): return x <= y
def fn_less(x, y): return x < y
def fn_geq(x, y): return x >= y
def fn_greater(x, y): return x > y
def fn_eq(x, y): return x == y
def fn_neq(x, y): return x != y
op_lookup = {
"==": fn_eq,
"=": fn_eq,
"!=": fn_neq,
"<>": fn_neq,
">": fn_greater,
"<": fn_less,
">=": fn_geq,
"<=": fn_leq
}
expected_str = '.'.join([str(x) for x in expected])
actual_str = '.'.join([str(x) for x in actual])
return op_lookup[comparison](
LooseVersion(actual_str),
LooseVersion(expected_str))
_re_pattern_type = type(re.compile(''))
def _match_decorator_property(expected, actual):
if actual is None or expected is None:
return True
if isinstance(expected, no_match):
return not _match_decorator_property(expected.item, actual)
elif isinstance(expected, (_re_pattern_type,) + six.string_types):
return re.search(expected, actual) is not None
elif hasattr(expected, "__iter__"):
return any([x is not None and _match_decorator_property(x, actual)
for x in expected])
else:
return expected == actual
def expectedFailure(expected_fn, bugnumber=None):
def expectedFailure_impl(func):
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
raise Exception(
"Decorator can only be used to decorate a test method")
@wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
if funcutils.requires_self(expected_fn):
xfail_reason = expected_fn(self)
else:
xfail_reason = expected_fn()
if xfail_reason is not None:
if configuration.results_formatter_object is not None:
# Mark this test as expected to fail.
configuration.results_formatter_object.handle_event(
EventBuilder.event_for_mark_test_expected_failure(self))
xfail_func = unittest2.expectedFailure(func)
xfail_func(*args, **kwargs)
else:
func(*args, **kwargs)
return wrapper
# Some decorators can be called both with no arguments (e.g. @expectedFailureWindows)
# or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])). When called
# the first way, the first argument will be the actual function because decorators are
# weird like that. So this is basically a check that says "which syntax was the original
# function decorated with?"
if six.callable(bugnumber):
return expectedFailure_impl(bugnumber)
else:
return expectedFailure_impl
def skipTestIfFn(expected_fn, bugnumber=None):
def skipTestIfFn_impl(func):
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
raise Exception(
"@skipTestIfFn can only be used to decorate a test method")
@wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
if funcutils.requires_self(expected_fn):
reason = expected_fn(self)
else:
reason = expected_fn()
if reason is not None:
self.skipTest(reason)
else:
func(*args, **kwargs)
return wrapper
# Some decorators can be called both with no arguments (e.g. @expectedFailureWindows)
# or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])). When called
# the first way, the first argument will be the actual function because decorators are
# weird like that. So this is basically a check that says "how was the
# decorator used"
if six.callable(bugnumber):
return skipTestIfFn_impl(bugnumber)
else:
return skipTestIfFn_impl
def _decorateTest(mode,
bugnumber=None, oslist=None, hostoslist=None,
compiler=None, compiler_version=None,
archs=None, triple=None,
debug_info=None,
swig_version=None, py_version=None,
macos_version=None,
remote=None, dwarf_version=None):
def fn(self):
skip_for_os = _match_decorator_property(
lldbplatform.translate(oslist), self.getPlatform())
skip_for_hostos = _match_decorator_property(
lldbplatform.translate(hostoslist),
lldbplatformutil.getHostPlatform())
skip_for_compiler = _match_decorator_property(
compiler, self.getCompiler()) and self.expectedCompilerVersion(compiler_version)
skip_for_arch = _match_decorator_property(
archs, self.getArchitecture())
skip_for_debug_info = _match_decorator_property(
debug_info, self.getDebugInfo())
skip_for_triple = _match_decorator_property(
triple, lldb.DBG.GetSelectedPlatform().GetTriple())
skip_for_remote = _match_decorator_property(
remote, lldb.remote_platform is not None)
skip_for_swig_version = (
swig_version is None) or (
not hasattr(
lldb,
'swig_version')) or (
_check_expected_version(
swig_version[0],
swig_version[1],
lldb.swig_version))
skip_for_py_version = (
py_version is None) or _check_expected_version(
py_version[0], py_version[1], sys.version_info)
skip_for_macos_version = (macos_version is None) or (
(platform.mac_ver()[0] != "") and (_check_expected_version(
macos_version[0],
macos_version[1],
platform.mac_ver()[0])))
skip_for_dwarf_version = (dwarf_version is None) or (
_check_expected_version(dwarf_version[0], dwarf_version[1],
self.getDwarfVersion()))
# For the test to be skipped, all specified (e.g. not None) parameters must be True.
# An unspecified parameter means "any", so those are marked skip by default. And we skip
# the final test if all conditions are True.
conditions = [(oslist, skip_for_os, "target o/s"),
(hostoslist, skip_for_hostos, "host o/s"),
(compiler, skip_for_compiler, "compiler or version"),
(archs, skip_for_arch, "architecture"),
(debug_info, skip_for_debug_info, "debug info format"),
(triple, skip_for_triple, "target triple"),
(swig_version, skip_for_swig_version, "swig version"),
(py_version, skip_for_py_version, "python version"),
(macos_version, skip_for_macos_version, "macOS version"),
(remote, skip_for_remote, "platform locality (remote/local)"),
(dwarf_version, skip_for_dwarf_version, "dwarf version")]
reasons = []
final_skip_result = True
for this_condition in conditions:
final_skip_result = final_skip_result and this_condition[1]
if this_condition[0] is not None and this_condition[1]:
reasons.append(this_condition[2])
reason_str = None
if final_skip_result:
mode_str = {
DecorateMode.Skip: "skipping",
DecorateMode.Xfail: "xfailing"}[mode]
if len(reasons) > 0:
reason_str = ",".join(reasons)
reason_str = "{} due to the following parameter(s): {}".format(
mode_str, reason_str)
else:
reason_str = "{} unconditionally"
if bugnumber is not None and not six.callable(bugnumber):
reason_str = reason_str + " [" + str(bugnumber) + "]"
return reason_str
if mode == DecorateMode.Skip:
return skipTestIfFn(fn, bugnumber)
elif mode == DecorateMode.Xfail:
return expectedFailure(fn, bugnumber)
else:
return None
# provide a function to xfail on defined oslist, compiler version, and archs
# if none is specified for any argument, that argument won't be checked and thus means for all
# for example,
# @expectedFailureAll, xfail for all platform/compiler/arch,
# @expectedFailureAll(compiler='gcc'), xfail for gcc on all platform/architecture
# @expectedFailureAll(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), xfail for gcc>=4.9 on linux with i386
def expectedFailureAll(bugnumber=None,
oslist=None, hostoslist=None,
compiler=None, compiler_version=None,
archs=None, triple=None,
debug_info=None,
swig_version=None, py_version=None,
macos_version=None,
remote=None, dwarf_version=None):
return _decorateTest(DecorateMode.Xfail,
bugnumber=bugnumber,
oslist=oslist, hostoslist=hostoslist,
compiler=compiler, compiler_version=compiler_version,
archs=archs, triple=triple,
debug_info=debug_info,
swig_version=swig_version, py_version=py_version,
macos_version=None,
remote=remote,dwarf_version=dwarf_version)
# provide a function to skip on defined oslist, compiler version, and archs
# if none is specified for any argument, that argument won't be checked and thus means for all
# for example,
# @skipIf, skip for all platform/compiler/arch,
# @skipIf(compiler='gcc'), skip for gcc on all platform/architecture
# @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386
def skipIf(bugnumber=None,
oslist=None, hostoslist=None,
compiler=None, compiler_version=None,
archs=None, triple=None,
debug_info=None,
swig_version=None, py_version=None,
macos_version=None,
remote=None, dwarf_version=None):
return _decorateTest(DecorateMode.Skip,
bugnumber=bugnumber,
oslist=oslist, hostoslist=hostoslist,
compiler=compiler, compiler_version=compiler_version,
archs=archs, triple=triple,
debug_info=debug_info,
swig_version=swig_version, py_version=py_version,
macos_version=macos_version,
remote=remote, dwarf_version=dwarf_version)
def _skip_for_android(reason, api_levels, archs):
def impl(obj):
result = lldbplatformutil.match_android_device(
obj.getArchitecture(), valid_archs=archs, valid_api_levels=api_levels)
return reason if result else None
return impl
def add_test_categories(cat):
"""Add test categories to a TestCase method"""
cat = test_categories.validate(cat, True)
def impl(func):
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
raise Exception(
"@add_test_categories can only be used to decorate a test method")
try:
if hasattr(func, "categories"):
cat.extend(func.categories)
setattr(func, "categories", cat)
except AttributeError:
raise Exception('Cannot assign categories to inline tests.')
return func
return impl
def benchmarks_test(func):
"""Decorate the item as a benchmarks test."""
def should_skip_benchmarks_test():
return "benchmarks test"
# Mark this function as such to separate them from the regular tests.
result = skipTestIfFn(should_skip_benchmarks_test)(func)
result.__benchmarks_test__ = True
return result
def no_debug_info_test(func):
"""Decorate the item as a test what don't use any debug info. If this annotation is specified
then the test runner won't generate a separate test for each debug info format. """
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
raise Exception(
"@no_debug_info_test can only be used to decorate a test method")
@wraps(func)
def wrapper(self, *args, **kwargs):
return func(self, *args, **kwargs)
# Mark this function as such to separate them from the regular tests.
wrapper.__no_debug_info_test__ = True
return wrapper
def apple_simulator_test(platform):
"""
Decorate the test as a test requiring a simulator for a specific platform.
Consider that a simulator is available if you have the corresponding SDK installed.
The SDK identifiers for simulators are iphonesimulator, appletvsimulator, watchsimulator
"""
def should_skip_simulator_test():
if lldbplatformutil.getHostPlatform() != 'darwin':
return "simulator tests are run only on darwin hosts"
try:
DEVNULL = open(os.devnull, 'w')
output = subprocess.check_output(["xcodebuild", "-showsdks"], stderr=DEVNULL).decode("utf-8")
if re.search('%ssimulator' % platform, output):
return None
else:
return "%s simulator is not supported on this system." % platform
except subprocess.CalledProcessError:
return "%s is not supported on this system (xcodebuild failed)." % feature
return skipTestIfFn(should_skip_simulator_test)
def debugserver_test(func):
"""Decorate the item as a debugserver test."""
def should_skip_debugserver_test():
return "debugserver tests" if configuration.dont_do_debugserver_test else None
return skipTestIfFn(should_skip_debugserver_test)(func)
def llgs_test(func):
"""Decorate the item as a lldb-server test."""
def should_skip_llgs_tests():
return "llgs tests" if configuration.dont_do_llgs_test else None
return skipTestIfFn(should_skip_llgs_tests)(func)
def not_remote_testsuite_ready(func):
"""Decorate the item as a test which is not ready yet for remote testsuite."""
def is_remote():
return "Not ready for remote testsuite" if lldb.remote_platform else None
return skipTestIfFn(is_remote)(func)
def expectedFailureOS(
oslist,
bugnumber=None,
compilers=None,
debug_info=None,
archs=None):
return expectedFailureAll(
oslist=oslist,
bugnumber=bugnumber,
compiler=compilers,
archs=archs,
debug_info=debug_info)
def expectedFailureDarwin(bugnumber=None, compilers=None, debug_info=None):
# For legacy reasons, we support both "darwin" and "macosx" as OS X
# triples.
return expectedFailureOS(
lldbplatform.darwin_all,
bugnumber,
compilers,
debug_info=debug_info)
def expectedFailureAndroid(bugnumber=None, api_levels=None, archs=None):
""" Mark a test as xfail for Android.
Arguments:
bugnumber - The LLVM pr associated with the problem.
api_levels - A sequence of numbers specifying the Android API levels
for which a test is expected to fail. None means all API level.
arch - A sequence of architecture names specifying the architectures
for which a test is expected to fail. None means all architectures.
"""
return expectedFailure(
_skip_for_android(
"xfailing on android",
api_levels,
archs),
bugnumber)
def expectedFailureNetBSD(bugnumber=None):
return expectedFailureOS(
['netbsd'],
bugnumber)
# Flakey tests get two chances to run. If they fail the first time round, the result formatter
# makes sure it is run one more time.
def expectedFlakey(expected_fn, bugnumber=None):
def expectedFailure_impl(func):
@wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
if expected_fn(self):
# Send event marking test as explicitly eligible for rerunning.
if configuration.results_formatter_object is not None:
# Mark this test as rerunnable.
configuration.results_formatter_object.handle_event(
EventBuilder.event_for_mark_test_rerun_eligible(self))
func(*args, **kwargs)
return wrapper
# Some decorators can be called both with no arguments (e.g. @expectedFailureWindows)
# or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])). When called
# the first way, the first argument will be the actual function because decorators are
# weird like that. So this is basically a check that says "which syntax was the original
# function decorated with?"
if six.callable(bugnumber):
return expectedFailure_impl(bugnumber)
else:
return expectedFailure_impl
def expectedFlakeyOS(oslist, bugnumber=None, compilers=None):
def fn(self):
return (self.getPlatform() in oslist and
self.expectedCompiler(compilers))
return expectedFlakey(fn, bugnumber)
def expectedFlakeyDarwin(bugnumber=None, compilers=None):
# For legacy reasons, we support both "darwin" and "macosx" as OS X
# triples.
return expectedFlakeyOS(
lldbplatformutil.getDarwinOSTriples(),
bugnumber,
compilers)
def expectedFlakeyFreeBSD(bugnumber=None, compilers=None):
return expectedFlakeyOS(['freebsd'], bugnumber, compilers)
def expectedFlakeyLinux(bugnumber=None, compilers=None):
return expectedFlakeyOS(['linux'], bugnumber, compilers)
def expectedFlakeyNetBSD(bugnumber=None, compilers=None):
return expectedFlakeyOS(['netbsd'], bugnumber, compilers)
def expectedFlakeyAndroid(bugnumber=None, api_levels=None, archs=None):
return expectedFlakey(
_skip_for_android(
"flakey on android",
api_levels,
archs),
bugnumber)
def skipIfOutOfTreeDebugserver(func):
"""Decorate the item to skip tests if using an out-of-tree debugserver."""
def is_out_of_tree_debugserver():
return "out-of-tree debugserver" if lldbtest_config.out_of_tree_debugserver else None
return skipTestIfFn(is_out_of_tree_debugserver)(func)
def skipIfRemote(func):
"""Decorate the item to skip tests if testing remotely."""
def is_remote():
return "skip on remote platform" if lldb.remote_platform else None
return skipTestIfFn(is_remote)(func)
def skipIfNoSBHeaders(func):
"""Decorate the item to mark tests that should be skipped when LLDB is built with no SB API headers."""
def are_sb_headers_missing():
if lldbplatformutil.getHostPlatform() == 'darwin':
header = os.path.join(
os.environ["LLDB_LIB_DIR"],
'LLDB.framework',
'Versions',
'Current',
'Headers',
'LLDB.h')
if os.path.exists(header):
return None
header = os.path.join(
os.environ["LLDB_SRC"],
"include",
"lldb",
"API",
"LLDB.h")
if not os.path.exists(header):
return "skip because LLDB.h header not found"
return None
return skipTestIfFn(are_sb_headers_missing)(func)
def skipIfiOSSimulator(func):
"""Decorate the item to skip tests that should be skipped on the iOS Simulator."""
def is_ios_simulator():
return "skip on the iOS Simulator" if configuration.lldb_platform_name == 'ios-simulator' else None
return skipTestIfFn(is_ios_simulator)(func)
def skipIfiOS(func):
return skipIfPlatform(["ios"])(func)
def skipIftvOS(func):
return skipIfPlatform(["tvos"])(func)
def skipIfwatchOS(func):
return skipIfPlatform(["watchos"])(func)
def skipIfbridgeOS(func):
return skipIfPlatform(["bridgeos"])(func)
def skipIfDarwinEmbedded(func):
"""Decorate the item to skip tests that should be skipped on Darwin armv7/arm64 targets."""
return skipIfPlatform(
lldbplatform.translate(
lldbplatform.darwin_embedded))(func)
def skipIfFreeBSD(func):
"""Decorate the item to skip tests that should be skipped on FreeBSD."""
return skipIfPlatform(["freebsd"])(func)
def skipIfNetBSD(func):
"""Decorate the item to skip tests that should be skipped on NetBSD."""
return skipIfPlatform(["netbsd"])(func)
def skipIfDarwin(func):
"""Decorate the item to skip tests that should be skipped on Darwin."""
return skipIfPlatform(
lldbplatform.translate(
lldbplatform.darwin_all))(func)
def skipIfLinux(func):
"""Decorate the item to skip tests that should be skipped on Linux."""
return skipIfPlatform(["linux"])(func)
def skipIfWindows(func):
"""Decorate the item to skip tests that should be skipped on Windows."""
return skipIfPlatform(["windows"])(func)
def skipUnlessWindows(func):
"""Decorate the item to skip tests that should be skipped on any non-Windows platform."""
return skipUnlessPlatform(["windows"])(func)
def skipUnlessDarwin(func):
"""Decorate the item to skip tests that should be skipped on any non Darwin platform."""
return skipUnlessPlatform(lldbplatformutil.getDarwinOSTriples())(func)
def skipUnlessTargetAndroid(func):
return unittest2.skipUnless(lldbplatformutil.target_is_android(),
"requires target to be Android")(func)
def skipIfHostIncompatibleWithRemote(func):
"""Decorate the item to skip tests if binaries built on this host are incompatible."""
def is_host_incompatible_with_remote(self):
host_arch = self.getLldbArchitecture()
host_platform = lldbplatformutil.getHostPlatform()
target_arch = self.getArchitecture()
target_platform = 'darwin' if self.platformIsDarwin() else self.getPlatform()
if not (target_arch == 'x86_64' and host_arch ==
'i386') and host_arch != target_arch:
return "skipping because target %s is not compatible with host architecture %s" % (
target_arch, host_arch)
if target_platform != host_platform:
return "skipping because target is %s but host is %s" % (
target_platform, host_platform)
if lldbplatformutil.match_android_device(target_arch):
return "skipping because target is android"
return None
return skipTestIfFn(is_host_incompatible_with_remote)(func)
def skipIfPlatform(oslist):
"""Decorate the item to skip tests if running on one of the listed platforms."""
# This decorator cannot be ported to `skipIf` yet because it is used on entire
# classes, which `skipIf` explicitly forbids.
return unittest2.skipIf(lldbplatformutil.getPlatform() in oslist,
"skip on %s" % (", ".join(oslist)))
def skipUnlessPlatform(oslist):
"""Decorate the item to skip tests unless running on one of the listed platforms."""
# This decorator cannot be ported to `skipIf` yet because it is used on entire
# classes, which `skipIf` explicitly forbids.
return unittest2.skipUnless(lldbplatformutil.getPlatform() in oslist,
"requires one of %s" % (", ".join(oslist)))
+def skipUnlessArch(arch):
+ """Decorate the item to skip tests unless running on the specified architecture."""
+
+ def arch_doesnt_match(self):
+ target_arch = self.getArchitecture()
+ if arch != target_arch:
+ return "Test only runs on " + arch + ", but target arch is " + target_arch
+ return None
+
+ return skipTestIfFn(arch_doesnt_match)
def skipIfTargetAndroid(bugnumber=None, api_levels=None, archs=None):
"""Decorator to skip tests when the target is Android.
Arguments:
api_levels - The API levels for which the test should be skipped. If
it is None, then the test will be skipped for all API levels.
arch - A sequence of architecture names specifying the architectures
for which a test is skipped. None means all architectures.
"""
return skipTestIfFn(
_skip_for_android(
"skipping for android",
api_levels,
archs),
bugnumber)
def skipUnlessSupportedTypeAttribute(attr):
"""Decorate the item to skip test unless Clang supports type __attribute__(attr)."""
def compiler_doesnt_support_struct_attribute(self):
compiler_path = self.getCompiler()
f = tempfile.NamedTemporaryFile()
cmd = [self.getCompiler(), "-x", "c++", "-c", "-o", f.name, "-"]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
stdout, stderr = p.communicate('struct __attribute__((%s)) Test {};'%attr)
if attr in stderr:
return "Compiler does not support attribute %s"%(attr)
return None
return skipTestIfFn(compiler_doesnt_support_struct_attribute)
def skipUnlessHasCallSiteInfo(func):
"""Decorate the function to skip testing unless call site info from clang is available."""
def is_compiler_clang_with_call_site_info(self):
compiler_path = self.getCompiler()
compiler = os.path.basename(compiler_path)
if not compiler.startswith("clang"):
return "Test requires clang as compiler"
f = tempfile.NamedTemporaryFile()
cmd = "echo 'int main() {}' | " \
- "%s -g -glldb -O1 -S -emit-llvm -x c -o %s -" % (compiler_path, f.name)
+ "%s -g -glldb -O1 -Xclang -femit-debug-entry-values -S -emit-llvm -x c -o %s -" % (compiler_path, f.name)
if os.popen(cmd).close() is not None:
return "Compiler can't compile with call site info enabled"
with open(f.name, 'r') as ir_output_file:
buf = ir_output_file.read()
if 'DIFlagAllCallsDescribed' not in buf:
return "Compiler did not introduce DIFlagAllCallsDescribed IR flag"
return None
return skipTestIfFn(is_compiler_clang_with_call_site_info)(func)
def skipUnlessThreadSanitizer(func):
"""Decorate the item to skip test unless Clang -fsanitize=thread is supported."""
def is_compiler_clang_with_thread_sanitizer(self):
compiler_path = self.getCompiler()
compiler = os.path.basename(compiler_path)
if not compiler.startswith("clang"):
return "Test requires clang as compiler"
if lldbplatformutil.getPlatform() == 'windows':
return "TSAN tests not compatible with 'windows'"
# rdar://28659145 - TSAN tests don't look like they're supported on i386
if self.getArchitecture() == 'i386' and platform.system() == 'Darwin':
return "TSAN tests not compatible with i386 targets"
f = tempfile.NamedTemporaryFile()
cmd = "echo 'int main() {}' | %s -x c -o %s -" % (compiler_path, f.name)
if os.popen(cmd).close() is not None:
return None # The compiler cannot compile at all, let's *not* skip the test
cmd = "echo 'int main() {}' | %s -fsanitize=thread -x c -o %s -" % (compiler_path, f.name)
if os.popen(cmd).close() is not None:
return "Compiler cannot compile with -fsanitize=thread"
return None
return skipTestIfFn(is_compiler_clang_with_thread_sanitizer)(func)
def skipUnlessUndefinedBehaviorSanitizer(func):
"""Decorate the item to skip test unless -fsanitize=undefined is supported."""
def is_compiler_clang_with_ubsan(self):
# Write out a temp file which exhibits UB.
inputf = tempfile.NamedTemporaryFile(suffix='.c', mode='w')
inputf.write('int main() { int x = 0; return x / x; }\n')
inputf.flush()
# We need to write out the object into a named temp file for inspection.
outputf = tempfile.NamedTemporaryFile()
# Try to compile with ubsan turned on.
cmd = '%s -fsanitize=undefined %s -o %s' % (self.getCompiler(), inputf.name, outputf.name)
if os.popen(cmd).close() is not None:
return "Compiler cannot compile with -fsanitize=undefined"
# Check that we actually see ubsan instrumentation in the binary.
cmd = 'nm %s' % outputf.name
with os.popen(cmd) as nm_output:
if '___ubsan_handle_divrem_overflow' not in nm_output.read():
return "Division by zero instrumentation is missing"
# Find the ubsan dylib.
# FIXME: This check should go away once compiler-rt gains support for __ubsan_on_report.
cmd = '%s -fsanitize=undefined -x c - -o - -### 2>&1' % self.getCompiler()
with os.popen(cmd) as cc_output:
driver_jobs = cc_output.read()
m = re.search(r'"([^"]+libclang_rt.ubsan_osx_dynamic.dylib)"', driver_jobs)
if not m:
return "Could not find the ubsan dylib used by the driver"
ubsan_dylib = m.group(1)
# Check that the ubsan dylib has special monitor hooks.
cmd = 'nm -gU %s' % ubsan_dylib
with os.popen(cmd) as nm_output:
syms = nm_output.read()
if '___ubsan_on_report' not in syms:
return "Missing ___ubsan_on_report"
if '___ubsan_get_current_report_data' not in syms:
return "Missing ___ubsan_get_current_report_data"
# OK, this dylib + compiler works for us.
return None
return skipTestIfFn(is_compiler_clang_with_ubsan)(func)
def skipUnlessAddressSanitizer(func):
"""Decorate the item to skip test unless Clang -fsanitize=thread is supported."""
def is_compiler_with_address_sanitizer(self):
compiler_path = self.getCompiler()
compiler = os.path.basename(compiler_path)
f = tempfile.NamedTemporaryFile()
if lldbplatformutil.getPlatform() == 'windows':
return "ASAN tests not compatible with 'windows'"
cmd = "echo 'int main() {}' | %s -x c -o %s -" % (compiler_path, f.name)
if os.popen(cmd).close() is not None:
return None # The compiler cannot compile at all, let's *not* skip the test
cmd = "echo 'int main() {}' | %s -fsanitize=address -x c -o %s -" % (compiler_path, f.name)
if os.popen(cmd).close() is not None:
return "Compiler cannot compile with -fsanitize=address"
return None
return skipTestIfFn(is_compiler_with_address_sanitizer)(func)
def _get_bool_config_skip_if_decorator(key):
config = lldb.SBDebugger.GetBuildConfiguration()
value_node = config.GetValueForKey(key)
fail_value = True # More likely to notice if something goes wrong
have = value_node.GetValueForKey("value").GetBooleanValue(fail_value)
return unittest2.skipIf(not have, "requires " + key)
def skipIfCursesSupportMissing(func):
return _get_bool_config_skip_if_decorator("curses")(func)
def skipIfXmlSupportMissing(func):
return _get_bool_config_skip_if_decorator("xml")(func)
def skipIfLLVMTargetMissing(target):
config = lldb.SBDebugger.GetBuildConfiguration()
targets = config.GetValueForKey("targets").GetValueForKey("value")
found = False
for i in range(targets.GetSize()):
if targets.GetItemAtIndex(i).GetStringValue(99) == target:
found = True
break
return unittest2.skipIf(not found, "requires " + target)
# Call sysctl on darwin to see if a specified hardware feature is available on this machine.
def skipUnlessFeature(feature):
def is_feature_enabled(self):
if platform.system() == 'Darwin':
try:
DEVNULL = open(os.devnull, 'w')
output = subprocess.check_output(["/usr/sbin/sysctl", feature], stderr=DEVNULL).decode("utf-8")
# If 'feature: 1' was output, then this feature is available and
# the test should not be skipped.
if re.match('%s: 1\s*' % feature, output):
return None
else:
return "%s is not supported on this system." % feature
except subprocess.CalledProcessError:
return "%s is not supported on this system." % feature
return skipTestIfFn(is_feature_enabled)
def skipIfAsan(func):
"""Skip this test if the environment is set up to run LLDB itself under ASAN."""
def is_asan():
if ('ASAN_OPTIONS' in os.environ):
return "ASAN unsupported"
return None
return skipTestIfFn(is_asan)(func)
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile
new file mode 100644
index 0000000..0c26fc7
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/Makefile
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -O1 -glldb -Xclang -femit-debug-entry-values
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py
new file mode 100644
index 0000000..5b5099b
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py
@@ -0,0 +1,8 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+ [decorators.skipUnlessDarwin,
+ decorators.skipUnlessArch('x86_64'),
+ decorators.skipUnlessHasCallSiteInfo,
+ decorators.skipIf(dwarf_version=['<', '4'])])
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp
new file mode 100644
index 0000000..6efd570
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp
@@ -0,0 +1,169 @@
+// Note: This test requires the SysV AMD64 ABI to be in use, and requires
+// compiler support for DWARF entry values.
+
+// Inhibit dead-arg-elim by using 'x'.
+template<typename T> __attribute__((noinline)) void use(T x) {
+ asm volatile (""
+ /* Outputs */ :
+ /* Inputs */ : "g"(x)
+ /* Clobbers */ :
+ );
+}
+
+// Destroy %rsi in the current frame.
+#define DESTROY_RSI \
+ asm volatile ("xorq %%rsi, %%rsi" \
+ /* Outputs */ : \
+ /* Inputs */ : \
+ /* Clobbers */ : "rsi" \
+ );
+
+struct S1 {
+ int field1 = 123;
+ int *field2 = &field1;
+};
+
+__attribute__((noinline))
+void func1(int &sink, int x) {
+ use(x);
+
+ // Destroy 'x' in the current frame.
+ DESTROY_RSI;
+
+ //% self.filecheck("image lookup -va $pc", "main.cpp", "-check-prefix=FUNC1-DESC")
+ // FUNC1-DESC: name = "x", type = "int", location = DW_OP_entry_value( rsi)
+
+ ++sink;
+}
+
+__attribute__((noinline))
+void func2(int &sink, int x) {
+ use(x);
+
+ // Destroy 'x' in the current frame.
+ DESTROY_RSI;
+
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC2-EXPR")
+ // FUNC2-EXPR: (int) ${{.*}} = 123
+
+ ++sink;
+}
+
+__attribute__((noinline))
+void func3(int &sink, int *p) {
+ use(p);
+
+ // Destroy 'p' in the current frame.
+ DESTROY_RSI;
+
+ //% self.filecheck("expr *p", "main.cpp", "-check-prefix=FUNC3-EXPR")
+ // FUNC3-EXPR: (int) ${{.*}} = 123
+
+ ++sink;
+}
+
+__attribute__((noinline))
+void func4_amb(int &sink, int x) {
+ use(x);
+
+ // Destroy 'x' in the current frame.
+ DESTROY_RSI;
+
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC4-EXPR", expect_cmd_failure=True)
+ // FUNC4-EXPR: couldn't get the value of variable x: Could not evaluate DW_OP_entry_value.
+
+ ++sink;
+}
+
+__attribute__((noinline))
+void func5_amb() {}
+
+__attribute__((noinline))
+void func6(int &sink, int x) {
+ if (sink > 0)
+ func4_amb(sink, x); /* tail (taken) */
+ else
+ func5_amb(); /* tail */
+}
+
+__attribute__((noinline))
+void func7(int &sink, int x) {
+ //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC7-BT")
+ // FUNC7-BT: func7
+ // FUNC7-BT-NEXT: [inlined] func8_inlined
+ // FUNC7-BT-NEXT: [inlined] func9_inlined
+ // FUNC7-BT-NEXT: func10
+ use(x);
+
+ // Destroy 'x' in the current frame.
+ DESTROY_RSI;
+
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC7-EXPR")
+ // FUNC7-EXPR: (int) ${{.*}} = 123
+
+ ++sink;
+}
+
+__attribute__((always_inline))
+void func8_inlined(int &sink, int x) {
+ func7(sink, x);
+}
+
+__attribute__((always_inline))
+void func9_inlined(int &sink, int x) {
+ func8_inlined(sink, x);
+}
+
+__attribute__((noinline, disable_tail_calls))
+void func10(int &sink, int x) {
+ func9_inlined(sink, x);
+}
+
+__attribute__((noinline))
+void func11_tailcalled(int &sink, int x) {
+ //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC11-BT")
+ // FUNC11-BT: func11_tailcalled{{.*}}
+ // FUNC11-BT-NEXT: func12{{.*}} [artificial]
+ use(x);
+
+ // Destroy 'x' in the current frame.
+ DESTROY_RSI;
+
+ //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC11-EXPR")
+ // FUNC11-EXPR: (int) ${{.*}} = 123
+
+ ++sink;
+}
+
+__attribute__((noinline))
+void func12(int &sink, int x) {
+ func11_tailcalled(sink, x);
+}
+
+__attribute__((disable_tail_calls))
+int main() {
+ int sink = 0;
+ S1 s1;
+
+ // Test location dumping for DW_OP_entry_value.
+ func1(sink, 123);
+
+ // Test evaluation of "DW_OP_constu" in the parent frame.
+ func2(sink, 123);
+
+ // Test evaluation of "DW_OP_fbreg -24, DW_OP_deref" in the parent frame.
+ func3(sink, s1.field2);
+
+ // The sequences `main -> func4 -> func{5,6}_amb -> sink` are both plausible.
+ // Test that lldb doesn't attempt to guess which one occurred: entry value
+ // evaluation should fail.
+ func6(sink, 123);
+
+ // Test that evaluation can "see through" inlining.
+ func10(sink, 123);
+
+ // Test that evaluation can "see through" tail calls.
+ func12(sink, 123);
+
+ return 0;
+}
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index c11800d..48bfa72 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -1,3182 +1,3443 @@
//===-- DWARFExpression.cpp -------------------------------------*- C++ -*-===//
//
// 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/DWARFExpression.h"
#include <inttypes.h>
#include <vector>
#include "lldb/Core/Module.h"
#include "lldb/Core/Value.h"
#include "lldb/Core/dwarf.h"
#include "lldb/Utility/DataEncoder.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
#include "lldb/Utility/Scalar.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/VMRange.h"
#include "lldb/Host/Host.h"
#include "lldb/Utility/Endian.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/StackID.h"
+#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
using namespace lldb;
using namespace lldb_private;
static lldb::addr_t
ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu,
uint32_t index) {
uint32_t index_size = dwarf_cu->GetAddressByteSize();
dw_offset_t addr_base = dwarf_cu->GetAddrBase();
lldb::offset_t offset = addr_base + index * index_size;
return dwarf_cu->GetSymbolFileDWARF()
.GetDWARFContext()
.getOrLoadAddrData()
.GetMaxU64(&offset, index_size);
}
// DWARFExpression constructor
DWARFExpression::DWARFExpression()
: m_module_wp(), m_data(), m_dwarf_cu(nullptr),
m_reg_kind(eRegisterKindDWARF), m_loclist_slide(LLDB_INVALID_ADDRESS) {}
DWARFExpression::DWARFExpression(lldb::ModuleSP module_sp,
const DataExtractor &data,
const DWARFUnit *dwarf_cu)
: m_module_wp(), m_data(data), m_dwarf_cu(dwarf_cu),
m_reg_kind(eRegisterKindDWARF), m_loclist_slide(LLDB_INVALID_ADDRESS) {
if (module_sp)
m_module_wp = module_sp;
}
// Destructor
DWARFExpression::~DWARFExpression() {}
bool DWARFExpression::IsValid() const { return m_data.GetByteSize() > 0; }
void DWARFExpression::UpdateValue(uint64_t const_value,
lldb::offset_t const_value_byte_size,
uint8_t addr_byte_size) {
if (!const_value_byte_size)
return;
m_data.SetData(
DataBufferSP(new DataBufferHeap(&const_value, const_value_byte_size)));
m_data.SetByteOrder(endian::InlHostByteOrder());
m_data.SetAddressByteSize(addr_byte_size);
}
void DWARFExpression::DumpLocation(Stream *s, lldb::offset_t offset,
lldb::offset_t length,
lldb::DescriptionLevel level,
ABI *abi) const {
if (!m_data.ValidOffsetForDataOfSize(offset, length))
return;
const lldb::offset_t start_offset = offset;
const lldb::offset_t end_offset = offset + length;
+
+ // An operation within a DWARF expression may contain a sub-expression. The
+ // motivating example for this is DW_OP_entry_value. Keep track of where each
+ // each sub-expression ends.
+ std::vector<lldb::offset_t> ends_of_subexprs;
+
+ // "Finish" (i.e. print the closing right-parens) for sub-expressions up to
+ // the specified \p op_offset.
+ auto finish_subexpressions_to = [&](const lldb::offset_t op_offset) {
+ while (!ends_of_subexprs.empty() && op_offset >= ends_of_subexprs.back()) {
+ ends_of_subexprs.pop_back();
+ s->Printf(")");
+ if (!ends_of_subexprs.empty())
+ s->Printf(" ");
+ }
+ };
+
while (m_data.ValidOffset(offset) && offset < end_offset) {
const lldb::offset_t op_offset = offset;
const uint8_t op = m_data.GetU8(&offset);
+ finish_subexpressions_to(op_offset);
switch (level) {
default:
break;
case lldb::eDescriptionLevelBrief:
if (op_offset > start_offset)
s->PutChar(' ');
break;
case lldb::eDescriptionLevelFull:
case lldb::eDescriptionLevelVerbose:
if (op_offset > start_offset)
s->EOL();
s->Indent();
if (level == lldb::eDescriptionLevelFull)
break;
// Fall through for verbose and print offset and DW_OP prefix..
s->Printf("0x%8.8" PRIx64 ": %s", op_offset,
op >= DW_OP_APPLE_uninit ? "DW_OP_APPLE_" : "DW_OP_");
break;
}
switch (op) {
case DW_OP_addr:
*s << "DW_OP_addr(" << m_data.GetAddress(&offset) << ") ";
break; // 0x03 1 address
case DW_OP_deref:
*s << "DW_OP_deref";
break; // 0x06
case DW_OP_const1u:
s->Printf("DW_OP_const1u(0x%2.2x)", m_data.GetU8(&offset));
break; // 0x08 1 1-byte constant
case DW_OP_const1s:
s->Printf("DW_OP_const1s(0x%2.2x)", m_data.GetU8(&offset));
break; // 0x09 1 1-byte constant
case DW_OP_const2u:
s->Printf("DW_OP_const2u(0x%4.4x)", m_data.GetU16(&offset));
break; // 0x0a 1 2-byte constant
case DW_OP_const2s:
s->Printf("DW_OP_const2s(0x%4.4x)", m_data.GetU16(&offset));
break; // 0x0b 1 2-byte constant
case DW_OP_const4u:
s->Printf("DW_OP_const4u(0x%8.8x)", m_data.GetU32(&offset));
break; // 0x0c 1 4-byte constant
case DW_OP_const4s:
s->Printf("DW_OP_const4s(0x%8.8x)", m_data.GetU32(&offset));
break; // 0x0d 1 4-byte constant
case DW_OP_const8u:
s->Printf("DW_OP_const8u(0x%16.16" PRIx64 ")", m_data.GetU64(&offset));
break; // 0x0e 1 8-byte constant
case DW_OP_const8s:
s->Printf("DW_OP_const8s(0x%16.16" PRIx64 ")", m_data.GetU64(&offset));
break; // 0x0f 1 8-byte constant
case DW_OP_constu:
s->Printf("DW_OP_constu(0x%" PRIx64 ")", m_data.GetULEB128(&offset));
break; // 0x10 1 ULEB128 constant
case DW_OP_consts:
s->Printf("DW_OP_consts(0x%" PRId64 ")", m_data.GetSLEB128(&offset));
break; // 0x11 1 SLEB128 constant
case DW_OP_dup:
s->PutCString("DW_OP_dup");
break; // 0x12
case DW_OP_drop:
s->PutCString("DW_OP_drop");
break; // 0x13
case DW_OP_over:
s->PutCString("DW_OP_over");
break; // 0x14
case DW_OP_pick:
s->Printf("DW_OP_pick(0x%2.2x)", m_data.GetU8(&offset));
break; // 0x15 1 1-byte stack index
case DW_OP_swap:
s->PutCString("DW_OP_swap");
break; // 0x16
case DW_OP_rot:
s->PutCString("DW_OP_rot");
break; // 0x17
case DW_OP_xderef:
s->PutCString("DW_OP_xderef");
break; // 0x18
case DW_OP_abs:
s->PutCString("DW_OP_abs");
break; // 0x19
case DW_OP_and:
s->PutCString("DW_OP_and");
break; // 0x1a
case DW_OP_div:
s->PutCString("DW_OP_div");
break; // 0x1b
case DW_OP_minus:
s->PutCString("DW_OP_minus");
break; // 0x1c
case DW_OP_mod:
s->PutCString("DW_OP_mod");
break; // 0x1d
case DW_OP_mul:
s->PutCString("DW_OP_mul");
break; // 0x1e
case DW_OP_neg:
s->PutCString("DW_OP_neg");
break; // 0x1f
case DW_OP_not:
s->PutCString("DW_OP_not");
break; // 0x20
case DW_OP_or:
s->PutCString("DW_OP_or");
break; // 0x21
case DW_OP_plus:
s->PutCString("DW_OP_plus");
break; // 0x22
case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend
s->Printf("DW_OP_plus_uconst(0x%" PRIx64 ")",
m_data.GetULEB128(&offset));
break;
case DW_OP_shl:
s->PutCString("DW_OP_shl");
break; // 0x24
case DW_OP_shr:
s->PutCString("DW_OP_shr");
break; // 0x25
case DW_OP_shra:
s->PutCString("DW_OP_shra");
break; // 0x26
case DW_OP_xor:
s->PutCString("DW_OP_xor");
break; // 0x27
case DW_OP_skip:
s->Printf("DW_OP_skip(0x%4.4x)", m_data.GetU16(&offset));
break; // 0x2f 1 signed 2-byte constant
case DW_OP_bra:
s->Printf("DW_OP_bra(0x%4.4x)", m_data.GetU16(&offset));
break; // 0x28 1 signed 2-byte constant
case DW_OP_eq:
s->PutCString("DW_OP_eq");
break; // 0x29
case DW_OP_ge:
s->PutCString("DW_OP_ge");
break; // 0x2a
case DW_OP_gt:
s->PutCString("DW_OP_gt");
break; // 0x2b
case DW_OP_le:
s->PutCString("DW_OP_le");
break; // 0x2c
case DW_OP_lt:
s->PutCString("DW_OP_lt");
break; // 0x2d
case DW_OP_ne:
s->PutCString("DW_OP_ne");
break; // 0x2e
case DW_OP_lit0: // 0x30
case DW_OP_lit1: // 0x31
case DW_OP_lit2: // 0x32
case DW_OP_lit3: // 0x33
case DW_OP_lit4: // 0x34
case DW_OP_lit5: // 0x35
case DW_OP_lit6: // 0x36
case DW_OP_lit7: // 0x37
case DW_OP_lit8: // 0x38
case DW_OP_lit9: // 0x39
case DW_OP_lit10: // 0x3A
case DW_OP_lit11: // 0x3B
case DW_OP_lit12: // 0x3C
case DW_OP_lit13: // 0x3D
case DW_OP_lit14: // 0x3E
case DW_OP_lit15: // 0x3F
case DW_OP_lit16: // 0x40
case DW_OP_lit17: // 0x41
case DW_OP_lit18: // 0x42
case DW_OP_lit19: // 0x43
case DW_OP_lit20: // 0x44
case DW_OP_lit21: // 0x45
case DW_OP_lit22: // 0x46
case DW_OP_lit23: // 0x47
case DW_OP_lit24: // 0x48
case DW_OP_lit25: // 0x49
case DW_OP_lit26: // 0x4A
case DW_OP_lit27: // 0x4B
case DW_OP_lit28: // 0x4C
case DW_OP_lit29: // 0x4D
case DW_OP_lit30: // 0x4E
case DW_OP_lit31:
s->Printf("DW_OP_lit%i", op - DW_OP_lit0);
break; // 0x4f
case DW_OP_reg0: // 0x50
case DW_OP_reg1: // 0x51
case DW_OP_reg2: // 0x52
case DW_OP_reg3: // 0x53
case DW_OP_reg4: // 0x54
case DW_OP_reg5: // 0x55
case DW_OP_reg6: // 0x56
case DW_OP_reg7: // 0x57
case DW_OP_reg8: // 0x58
case DW_OP_reg9: // 0x59
case DW_OP_reg10: // 0x5A
case DW_OP_reg11: // 0x5B
case DW_OP_reg12: // 0x5C
case DW_OP_reg13: // 0x5D
case DW_OP_reg14: // 0x5E
case DW_OP_reg15: // 0x5F
case DW_OP_reg16: // 0x60
case DW_OP_reg17: // 0x61
case DW_OP_reg18: // 0x62
case DW_OP_reg19: // 0x63
case DW_OP_reg20: // 0x64
case DW_OP_reg21: // 0x65
case DW_OP_reg22: // 0x66
case DW_OP_reg23: // 0x67
case DW_OP_reg24: // 0x68
case DW_OP_reg25: // 0x69
case DW_OP_reg26: // 0x6A
case DW_OP_reg27: // 0x6B
case DW_OP_reg28: // 0x6C
case DW_OP_reg29: // 0x6D
case DW_OP_reg30: // 0x6E
case DW_OP_reg31: // 0x6F
{
uint32_t reg_num = op - DW_OP_reg0;
if (abi) {
RegisterInfo reg_info;
if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
if (reg_info.name) {
s->PutCString(reg_info.name);
break;
} else if (reg_info.alt_name) {
s->PutCString(reg_info.alt_name);
break;
}
}
}
s->Printf("DW_OP_reg%u", reg_num);
break;
} break;
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31: {
uint32_t reg_num = op - DW_OP_breg0;
int64_t reg_offset = m_data.GetSLEB128(&offset);
if (abi) {
RegisterInfo reg_info;
if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
if (reg_info.name) {
s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset);
break;
} else if (reg_info.alt_name) {
s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset);
break;
}
}
}
s->Printf("DW_OP_breg%i(0x%" PRIx64 ")", reg_num, reg_offset);
} break;
case DW_OP_regx: // 0x90 1 ULEB128 register
{
uint32_t reg_num = m_data.GetULEB128(&offset);
if (abi) {
RegisterInfo reg_info;
if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
if (reg_info.name) {
s->PutCString(reg_info.name);
break;
} else if (reg_info.alt_name) {
s->PutCString(reg_info.alt_name);
break;
}
}
}
s->Printf("DW_OP_regx(%" PRIu32 ")", reg_num);
break;
} break;
case DW_OP_fbreg: // 0x91 1 SLEB128 offset
s->Printf("DW_OP_fbreg(%" PRIi64 ")", m_data.GetSLEB128(&offset));
break;
case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset
{
uint32_t reg_num = m_data.GetULEB128(&offset);
int64_t reg_offset = m_data.GetSLEB128(&offset);
if (abi) {
RegisterInfo reg_info;
if (abi->GetRegisterInfoByKind(m_reg_kind, reg_num, reg_info)) {
if (reg_info.name) {
s->Printf("[%s%+" PRIi64 "]", reg_info.name, reg_offset);
break;
} else if (reg_info.alt_name) {
s->Printf("[%s%+" PRIi64 "]", reg_info.alt_name, reg_offset);
break;
}
}
}
s->Printf("DW_OP_bregx(reg=%" PRIu32 ",offset=%" PRIi64 ")", reg_num,
reg_offset);
} break;
case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed
s->Printf("DW_OP_piece(0x%" PRIx64 ")", m_data.GetULEB128(&offset));
break;
case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved
s->Printf("DW_OP_deref_size(0x%2.2x)", m_data.GetU8(&offset));
break;
case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved
s->Printf("DW_OP_xderef_size(0x%2.2x)", m_data.GetU8(&offset));
break;
case DW_OP_nop:
s->PutCString("DW_OP_nop");
break; // 0x96
case DW_OP_push_object_address:
s->PutCString("DW_OP_push_object_address");
break; // 0x97 DWARF3
case DW_OP_call2: // 0x98 DWARF3 1 2-byte offset of DIE
s->Printf("DW_OP_call2(0x%4.4x)", m_data.GetU16(&offset));
break;
case DW_OP_call4: // 0x99 DWARF3 1 4-byte offset of DIE
s->Printf("DW_OP_call4(0x%8.8x)", m_data.GetU32(&offset));
break;
case DW_OP_call_ref: // 0x9a DWARF3 1 4- or 8-byte offset of DIE
s->Printf("DW_OP_call_ref(0x%8.8" PRIx64 ")", m_data.GetAddress(&offset));
break;
case DW_OP_form_tls_address:
s->PutCString("DW_OP_form_tls_address"); // 0x9b
break;
case DW_OP_GNU_addr_index: // 0xfb
s->Printf("DW_OP_GNU_addr_index(0x%" PRIx64 ")",
m_data.GetULEB128(&offset));
break;
case DW_OP_addrx:
s->Printf("DW_OP_addrx(0x%" PRIx64 ")",
m_data.GetULEB128(&offset));
break;
case DW_OP_GNU_const_index: // 0xfc
s->Printf("DW_OP_GNU_const_index(0x%" PRIx64 ")",
m_data.GetULEB128(&offset));
break;
case DW_OP_GNU_push_tls_address:
s->PutCString("DW_OP_GNU_push_tls_address"); // 0xe0
break;
case DW_OP_APPLE_uninit:
s->PutCString("DW_OP_APPLE_uninit"); // 0xF0
break;
+ case DW_OP_entry_value: {
+ uint32_t subexpr_len = m_data.GetULEB128(&offset);
+ s->PutCString("DW_OP_entry_value(");
+ ends_of_subexprs.push_back(offset + subexpr_len);
+ break;
+ }
}
}
+
+ finish_subexpressions_to(end_offset);
}
void DWARFExpression::SetLocationListSlide(addr_t slide) {
m_loclist_slide = slide;
}
int DWARFExpression::GetRegisterKind() { return m_reg_kind; }
void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) {
m_reg_kind = reg_kind;
}
bool DWARFExpression::IsLocationList() const {
return m_loclist_slide != LLDB_INVALID_ADDRESS;
}
void DWARFExpression::GetDescription(Stream *s, lldb::DescriptionLevel level,
addr_t location_list_base_addr,
ABI *abi) const {
if (IsLocationList()) {
// We have a location list
lldb::offset_t offset = 0;
uint32_t count = 0;
addr_t curr_base_addr = location_list_base_addr;
while (m_data.ValidOffset(offset)) {
addr_t begin_addr_offset = LLDB_INVALID_ADDRESS;
addr_t end_addr_offset = LLDB_INVALID_ADDRESS;
if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset,
begin_addr_offset, end_addr_offset))
break;
if (begin_addr_offset == 0 && end_addr_offset == 0)
break;
if (begin_addr_offset < end_addr_offset) {
if (count > 0)
s->PutCString(", ");
VMRange addr_range(curr_base_addr + begin_addr_offset,
curr_base_addr + end_addr_offset);
addr_range.Dump(s, 0, 8);
s->PutChar('{');
lldb::offset_t location_length = m_data.GetU16(&offset);
DumpLocation(s, offset, location_length, level, abi);
s->PutChar('}');
offset += location_length;
} else {
if ((m_data.GetAddressByteSize() == 4 &&
(begin_addr_offset == UINT32_MAX)) ||
(m_data.GetAddressByteSize() == 8 &&
(begin_addr_offset == UINT64_MAX))) {
curr_base_addr = end_addr_offset + location_list_base_addr;
// We have a new base address
if (count > 0)
s->PutCString(", ");
*s << "base_addr = " << end_addr_offset;
}
}
count++;
}
} else {
// We have a normal location that contains DW_OP location opcodes
DumpLocation(s, 0, m_data.GetByteSize(), level, abi);
}
}
static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
lldb::RegisterKind reg_kind,
uint32_t reg_num, Status *error_ptr,
Value &value) {
if (reg_ctx == nullptr) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat("No register context in frame.\n");
} else {
uint32_t native_reg =
reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
if (native_reg == LLDB_INVALID_REGNUM) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat("Unable to convert register "
"kind=%u reg_num=%u to a native "
"register number.\n",
reg_kind, reg_num);
} else {
const RegisterInfo *reg_info =
reg_ctx->GetRegisterInfoAtIndex(native_reg);
RegisterValue reg_value;
if (reg_ctx->ReadRegister(reg_info, reg_value)) {
if (reg_value.GetScalarValue(value.GetScalar())) {
value.SetValueType(Value::eValueTypeScalar);
value.SetContext(Value::eContextTypeRegisterInfo,
const_cast<RegisterInfo *>(reg_info));
if (error_ptr)
error_ptr->Clear();
return true;
} else {
// If we get this error, then we need to implement a value buffer in
// the dwarf expression evaluation function...
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"register %s can't be converted to a scalar value",
reg_info->name);
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat("register %s is not available",
reg_info->name);
}
}
}
return false;
}
+/// Return the length in bytes of the set of operands for \p op. No guarantees
+/// are made on the state of \p data after this call.
static offset_t GetOpcodeDataSize(const DataExtractor &data,
const lldb::offset_t data_offset,
const uint8_t op) {
lldb::offset_t offset = data_offset;
switch (op) {
case DW_OP_addr:
case DW_OP_call_ref: // 0x9a 1 address sized offset of DIE (DWARF3)
return data.GetAddressByteSize();
// Opcodes with no arguments
case DW_OP_deref: // 0x06
case DW_OP_dup: // 0x12
case DW_OP_drop: // 0x13
case DW_OP_over: // 0x14
case DW_OP_swap: // 0x16
case DW_OP_rot: // 0x17
case DW_OP_xderef: // 0x18
case DW_OP_abs: // 0x19
case DW_OP_and: // 0x1a
case DW_OP_div: // 0x1b
case DW_OP_minus: // 0x1c
case DW_OP_mod: // 0x1d
case DW_OP_mul: // 0x1e
case DW_OP_neg: // 0x1f
case DW_OP_not: // 0x20
case DW_OP_or: // 0x21
case DW_OP_plus: // 0x22
case DW_OP_shl: // 0x24
case DW_OP_shr: // 0x25
case DW_OP_shra: // 0x26
case DW_OP_xor: // 0x27
case DW_OP_eq: // 0x29
case DW_OP_ge: // 0x2a
case DW_OP_gt: // 0x2b
case DW_OP_le: // 0x2c
case DW_OP_lt: // 0x2d
case DW_OP_ne: // 0x2e
case DW_OP_lit0: // 0x30
case DW_OP_lit1: // 0x31
case DW_OP_lit2: // 0x32
case DW_OP_lit3: // 0x33
case DW_OP_lit4: // 0x34
case DW_OP_lit5: // 0x35
case DW_OP_lit6: // 0x36
case DW_OP_lit7: // 0x37
case DW_OP_lit8: // 0x38
case DW_OP_lit9: // 0x39
case DW_OP_lit10: // 0x3A
case DW_OP_lit11: // 0x3B
case DW_OP_lit12: // 0x3C
case DW_OP_lit13: // 0x3D
case DW_OP_lit14: // 0x3E
case DW_OP_lit15: // 0x3F
case DW_OP_lit16: // 0x40
case DW_OP_lit17: // 0x41
case DW_OP_lit18: // 0x42
case DW_OP_lit19: // 0x43
case DW_OP_lit20: // 0x44
case DW_OP_lit21: // 0x45
case DW_OP_lit22: // 0x46
case DW_OP_lit23: // 0x47
case DW_OP_lit24: // 0x48
case DW_OP_lit25: // 0x49
case DW_OP_lit26: // 0x4A
case DW_OP_lit27: // 0x4B
case DW_OP_lit28: // 0x4C
case DW_OP_lit29: // 0x4D
case DW_OP_lit30: // 0x4E
case DW_OP_lit31: // 0x4f
case DW_OP_reg0: // 0x50
case DW_OP_reg1: // 0x51
case DW_OP_reg2: // 0x52
case DW_OP_reg3: // 0x53
case DW_OP_reg4: // 0x54
case DW_OP_reg5: // 0x55
case DW_OP_reg6: // 0x56
case DW_OP_reg7: // 0x57
case DW_OP_reg8: // 0x58
case DW_OP_reg9: // 0x59
case DW_OP_reg10: // 0x5A
case DW_OP_reg11: // 0x5B
case DW_OP_reg12: // 0x5C
case DW_OP_reg13: // 0x5D
case DW_OP_reg14: // 0x5E
case DW_OP_reg15: // 0x5F
case DW_OP_reg16: // 0x60
case DW_OP_reg17: // 0x61
case DW_OP_reg18: // 0x62
case DW_OP_reg19: // 0x63
case DW_OP_reg20: // 0x64
case DW_OP_reg21: // 0x65
case DW_OP_reg22: // 0x66
case DW_OP_reg23: // 0x67
case DW_OP_reg24: // 0x68
case DW_OP_reg25: // 0x69
case DW_OP_reg26: // 0x6A
case DW_OP_reg27: // 0x6B
case DW_OP_reg28: // 0x6C
case DW_OP_reg29: // 0x6D
case DW_OP_reg30: // 0x6E
case DW_OP_reg31: // 0x6F
case DW_OP_nop: // 0x96
case DW_OP_push_object_address: // 0x97 DWARF3
case DW_OP_form_tls_address: // 0x9b DWARF3
case DW_OP_call_frame_cfa: // 0x9c DWARF3
case DW_OP_stack_value: // 0x9f DWARF4
case DW_OP_GNU_push_tls_address: // 0xe0 GNU extension
return 0;
// Opcodes with a single 1 byte arguments
case DW_OP_const1u: // 0x08 1 1-byte constant
case DW_OP_const1s: // 0x09 1 1-byte constant
case DW_OP_pick: // 0x15 1 1-byte stack index
case DW_OP_deref_size: // 0x94 1 1-byte size of data retrieved
case DW_OP_xderef_size: // 0x95 1 1-byte size of data retrieved
return 1;
// Opcodes with a single 2 byte arguments
case DW_OP_const2u: // 0x0a 1 2-byte constant
case DW_OP_const2s: // 0x0b 1 2-byte constant
case DW_OP_skip: // 0x2f 1 signed 2-byte constant
case DW_OP_bra: // 0x28 1 signed 2-byte constant
case DW_OP_call2: // 0x98 1 2-byte offset of DIE (DWARF3)
return 2;
// Opcodes with a single 4 byte arguments
case DW_OP_const4u: // 0x0c 1 4-byte constant
case DW_OP_const4s: // 0x0d 1 4-byte constant
case DW_OP_call4: // 0x99 1 4-byte offset of DIE (DWARF3)
return 4;
// Opcodes with a single 8 byte arguments
case DW_OP_const8u: // 0x0e 1 8-byte constant
case DW_OP_const8s: // 0x0f 1 8-byte constant
return 8;
// All opcodes that have a single ULEB (signed or unsigned) argument
case DW_OP_addrx: // 0xa1 1 ULEB128 index
case DW_OP_constu: // 0x10 1 ULEB128 constant
case DW_OP_consts: // 0x11 1 SLEB128 constant
case DW_OP_plus_uconst: // 0x23 1 ULEB128 addend
case DW_OP_breg0: // 0x70 1 ULEB128 register
case DW_OP_breg1: // 0x71 1 ULEB128 register
case DW_OP_breg2: // 0x72 1 ULEB128 register
case DW_OP_breg3: // 0x73 1 ULEB128 register
case DW_OP_breg4: // 0x74 1 ULEB128 register
case DW_OP_breg5: // 0x75 1 ULEB128 register
case DW_OP_breg6: // 0x76 1 ULEB128 register
case DW_OP_breg7: // 0x77 1 ULEB128 register
case DW_OP_breg8: // 0x78 1 ULEB128 register
case DW_OP_breg9: // 0x79 1 ULEB128 register
case DW_OP_breg10: // 0x7a 1 ULEB128 register
case DW_OP_breg11: // 0x7b 1 ULEB128 register
case DW_OP_breg12: // 0x7c 1 ULEB128 register
case DW_OP_breg13: // 0x7d 1 ULEB128 register
case DW_OP_breg14: // 0x7e 1 ULEB128 register
case DW_OP_breg15: // 0x7f 1 ULEB128 register
case DW_OP_breg16: // 0x80 1 ULEB128 register
case DW_OP_breg17: // 0x81 1 ULEB128 register
case DW_OP_breg18: // 0x82 1 ULEB128 register
case DW_OP_breg19: // 0x83 1 ULEB128 register
case DW_OP_breg20: // 0x84 1 ULEB128 register
case DW_OP_breg21: // 0x85 1 ULEB128 register
case DW_OP_breg22: // 0x86 1 ULEB128 register
case DW_OP_breg23: // 0x87 1 ULEB128 register
case DW_OP_breg24: // 0x88 1 ULEB128 register
case DW_OP_breg25: // 0x89 1 ULEB128 register
case DW_OP_breg26: // 0x8a 1 ULEB128 register
case DW_OP_breg27: // 0x8b 1 ULEB128 register
case DW_OP_breg28: // 0x8c 1 ULEB128 register
case DW_OP_breg29: // 0x8d 1 ULEB128 register
case DW_OP_breg30: // 0x8e 1 ULEB128 register
case DW_OP_breg31: // 0x8f 1 ULEB128 register
case DW_OP_regx: // 0x90 1 ULEB128 register
case DW_OP_fbreg: // 0x91 1 SLEB128 offset
case DW_OP_piece: // 0x93 1 ULEB128 size of piece addressed
case DW_OP_GNU_addr_index: // 0xfb 1 ULEB128 index
case DW_OP_GNU_const_index: // 0xfc 1 ULEB128 index
data.Skip_LEB128(&offset);
return offset - data_offset;
// All opcodes that have a 2 ULEB (signed or unsigned) arguments
case DW_OP_bregx: // 0x92 2 ULEB128 register followed by SLEB128 offset
case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3);
data.Skip_LEB128(&offset);
data.Skip_LEB128(&offset);
return offset - data_offset;
case DW_OP_implicit_value: // 0x9e ULEB128 size followed by block of that size
// (DWARF4)
{
uint64_t block_len = data.Skip_LEB128(&offset);
offset += block_len;
return offset - data_offset;
}
+ case DW_OP_entry_value: // 0xa3 ULEB128 size + variable-length block
+ {
+ uint64_t subexpr_len = data.GetULEB128(&offset);
+ return (offset - data_offset) + subexpr_len;
+ }
+
default:
break;
}
return LLDB_INVALID_OFFSET;
}
lldb::addr_t DWARFExpression::GetLocation_DW_OP_addr(uint32_t op_addr_idx,
bool &error) const {
error = false;
if (IsLocationList())
return LLDB_INVALID_ADDRESS;
lldb::offset_t offset = 0;
uint32_t curr_op_addr_idx = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_addr) {
const lldb::addr_t op_file_addr = m_data.GetAddress(&offset);
if (curr_op_addr_idx == op_addr_idx)
return op_file_addr;
else
++curr_op_addr_idx;
} else if (op == DW_OP_GNU_addr_index || op == DW_OP_addrx) {
uint64_t index = m_data.GetULEB128(&offset);
if (curr_op_addr_idx == op_addr_idx) {
if (!m_dwarf_cu) {
error = true;
break;
}
return ReadAddressFromDebugAddrSection(m_dwarf_cu, index);
} else
++curr_op_addr_idx;
} else {
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
if (op_arg_size == LLDB_INVALID_OFFSET) {
error = true;
break;
}
offset += op_arg_size;
}
}
return LLDB_INVALID_ADDRESS;
}
bool DWARFExpression::Update_DW_OP_addr(lldb::addr_t file_addr) {
if (IsLocationList())
return false;
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_addr) {
const uint32_t addr_byte_size = m_data.GetAddressByteSize();
// We have to make a copy of the data as we don't know if this data is
// from a read only memory mapped buffer, so we duplicate all of the data
// first, then modify it, and if all goes well, we then replace the data
// for this expression
// So first we copy the data into a heap buffer
std::unique_ptr<DataBufferHeap> head_data_up(
new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize()));
// Make en encoder so we can write the address into the buffer using the
// correct byte order (endianness)
DataEncoder encoder(head_data_up->GetBytes(), head_data_up->GetByteSize(),
m_data.GetByteOrder(), addr_byte_size);
// Replace the address in the new buffer
if (encoder.PutMaxU64(offset, addr_byte_size, file_addr) == UINT32_MAX)
return false;
// All went well, so now we can reset the data using a shared pointer to
// the heap data so "m_data" will now correctly manage the heap data.
m_data.SetData(DataBufferSP(head_data_up.release()));
return true;
} else {
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
if (op_arg_size == LLDB_INVALID_OFFSET)
break;
offset += op_arg_size;
}
}
return false;
}
bool DWARFExpression::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 (IsLocationList())
return false;
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
if (op == DW_OP_form_tls_address || op == DW_OP_GNU_push_tls_address)
return true;
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
if (op_arg_size == LLDB_INVALID_OFFSET)
return false;
else
offset += op_arg_size;
}
return false;
}
bool DWARFExpression::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 (IsLocationList())
return false;
const uint32_t addr_byte_size = m_data.GetAddressByteSize();
// We have to make a copy of the data as we don't know if this data is from a
// read only memory mapped buffer, so we duplicate all of the data first,
// then modify it, and if all goes well, we then replace the data for this
// expression
// So first we copy the data into a heap buffer
std::shared_ptr<DataBufferHeap> heap_data_sp(
new DataBufferHeap(m_data.GetDataStart(), m_data.GetByteSize()));
// Make en encoder so we can write the address into the buffer using the
// correct byte order (endianness)
DataEncoder encoder(heap_data_sp->GetBytes(), heap_data_sp->GetByteSize(),
m_data.GetByteOrder(), addr_byte_size);
lldb::offset_t offset = 0;
lldb::offset_t const_offset = 0;
lldb::addr_t const_value = 0;
size_t const_byte_size = 0;
while (m_data.ValidOffset(offset)) {
const uint8_t op = m_data.GetU8(&offset);
bool decoded_data = false;
switch (op) {
case DW_OP_const4u:
// Remember the const offset in case we later have a
// DW_OP_form_tls_address or DW_OP_GNU_push_tls_address
const_offset = offset;
const_value = m_data.GetU32(&offset);
decoded_data = true;
const_byte_size = 4;
break;
case DW_OP_const8u:
// Remember the const offset in case we later have a
// DW_OP_form_tls_address or DW_OP_GNU_push_tls_address
const_offset = offset;
const_value = m_data.GetU64(&offset);
decoded_data = true;
const_byte_size = 8;
break;
case DW_OP_form_tls_address:
case DW_OP_GNU_push_tls_address:
// DW_OP_form_tls_address and DW_OP_GNU_push_tls_address must be preceded
// by a file address on the stack. We assume that DW_OP_const4u or
// DW_OP_const8u is used for these values, and we check that the last
// opcode we got before either of these was DW_OP_const4u or
// DW_OP_const8u. If so, then we can link the value accodingly. For
// Darwin, the value in the DW_OP_const4u or DW_OP_const8u is the file
// address of a structure that contains a function pointer, the pthread
// key and the offset into the data pointed to by the pthread key. So we
// must link this address and also set the module of this expression to
// the new_module_sp so we can resolve the file address correctly
if (const_byte_size > 0) {
lldb::addr_t linked_file_addr = link_address_callback(const_value);
if (linked_file_addr == LLDB_INVALID_ADDRESS)
return false;
// Replace the address in the new buffer
if (encoder.PutMaxU64(const_offset, const_byte_size,
linked_file_addr) == UINT32_MAX)
return false;
}
break;
default:
const_offset = 0;
const_value = 0;
const_byte_size = 0;
break;
}
if (!decoded_data) {
const offset_t op_arg_size = GetOpcodeDataSize(m_data, offset, op);
if (op_arg_size == LLDB_INVALID_OFFSET)
return false;
else
offset += op_arg_size;
}
}
// 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
m_module_wp = new_module_sp;
m_data.SetData(heap_data_sp);
return true;
}
bool DWARFExpression::LocationListContainsAddress(
lldb::addr_t loclist_base_addr, lldb::addr_t addr) const {
if (addr == LLDB_INVALID_ADDRESS)
return false;
if (IsLocationList()) {
lldb::offset_t offset = 0;
if (loclist_base_addr == LLDB_INVALID_ADDRESS)
return false;
while (m_data.ValidOffset(offset)) {
// We need to figure out what the value is for the location.
addr_t lo_pc = LLDB_INVALID_ADDRESS;
addr_t hi_pc = LLDB_INVALID_ADDRESS;
if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc,
hi_pc))
break;
if (lo_pc == 0 && hi_pc == 0)
break;
lo_pc += loclist_base_addr - m_loclist_slide;
hi_pc += loclist_base_addr - m_loclist_slide;
if (lo_pc <= addr && addr < hi_pc)
return true;
offset += m_data.GetU16(&offset);
}
}
return false;
}
bool DWARFExpression::GetLocation(addr_t base_addr, addr_t pc,
lldb::offset_t &offset,
lldb::offset_t &length) {
offset = 0;
if (!IsLocationList()) {
length = m_data.GetByteSize();
return true;
}
if (base_addr != LLDB_INVALID_ADDRESS && pc != LLDB_INVALID_ADDRESS) {
addr_t curr_base_addr = base_addr;
while (m_data.ValidOffset(offset)) {
// We need to figure out what the value is for the location.
addr_t lo_pc = LLDB_INVALID_ADDRESS;
addr_t hi_pc = LLDB_INVALID_ADDRESS;
if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset, lo_pc,
hi_pc))
break;
if (lo_pc == 0 && hi_pc == 0)
break;
lo_pc += curr_base_addr - m_loclist_slide;
hi_pc += curr_base_addr - m_loclist_slide;
length = m_data.GetU16(&offset);
if (length > 0 && lo_pc <= pc && pc < hi_pc)
return true;
offset += length;
}
}
offset = LLDB_INVALID_OFFSET;
length = 0;
return false;
}
bool DWARFExpression::DumpLocationForAddress(Stream *s,
lldb::DescriptionLevel level,
addr_t base_addr, addr_t address,
ABI *abi) {
lldb::offset_t offset = 0;
lldb::offset_t length = 0;
if (GetLocation(base_addr, address, offset, length)) {
if (length > 0) {
DumpLocation(s, offset, length, level, abi);
return true;
}
}
return false;
}
+static bool Evaluate_DW_OP_entry_value(std::vector<Value> &stack,
+ ExecutionContext *exe_ctx,
+ RegisterContext *reg_ctx,
+ const DataExtractor &opcodes,
+ lldb::offset_t &opcode_offset,
+ Status *error_ptr, Log *log) {
+ // DW_OP_entry_value(sub-expr) describes the location a variable had upon
+ // function entry: this variable location is presumed to be optimized out at
+ // the current PC value. The caller of the function may have call site
+ // information that describes an alternate location for the variable (e.g. a
+ // constant literal, or a spilled stack value) in the parent frame.
+ //
+ // Example (this is pseudo-code & pseudo-DWARF, but hopefully illustrative):
+ //
+ // void child(int &sink, int x) {
+ // ...
+ // /* "x" gets optimized out. */
+ //
+ // /* The location of "x" here is: DW_OP_entry_value($reg2). */
+ // ++sink;
+ // }
+ //
+ // void parent() {
+ // int sink;
+ //
+ // /*
+ // * The callsite information emitted here is:
+ // *
+ // * DW_TAG_call_site
+ // * DW_AT_return_pc ... (for "child(sink, 123);")
+ // * DW_TAG_call_site_parameter (for "sink")
+ // * DW_AT_location ($reg1)
+ // * DW_AT_call_value ($SP - 8)
+ // * DW_TAG_call_site_parameter (for "x")
+ // * DW_AT_location ($reg2)
+ // * DW_AT_call_value ($literal 123)
+ // *
+ // * DW_TAG_call_site
+ // * DW_AT_return_pc ... (for "child(sink, 456);")
+ // * ...
+ // */
+ // child(sink, 123);
+ // child(sink, 456);
+ // }
+ //
+ // When the program stops at "++sink" within `child`, the debugger determines
+ // the call site by analyzing the return address. Once the call site is found,
+ // the debugger determines which parameter is referenced by DW_OP_entry_value
+ // and evaluates the corresponding location for that parameter in `parent`.
+
+ // 1. Find the function which pushed the current frame onto the stack.
+ if ((!exe_ctx || !exe_ctx->HasTargetScope()) || !reg_ctx) {
+ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no exe/reg context");
+ return false;
+ }
+
+ StackFrame *current_frame = exe_ctx->GetFramePtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (!current_frame || !thread) {
+ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current frame/thread");
+ return false;
+ }
+
+ Target &target = exe_ctx->GetTargetRef();
+ StackFrameSP parent_frame = nullptr;
+ addr_t return_pc = LLDB_INVALID_ADDRESS;
+ uint32_t current_frame_idx = current_frame->GetFrameIndex();
+ uint32_t num_frames = thread->GetStackFrameCount();
+ for (uint32_t parent_frame_idx = current_frame_idx + 1;
+ parent_frame_idx < num_frames; ++parent_frame_idx) {
+ parent_frame = thread->GetStackFrameAtIndex(parent_frame_idx);
+ // Require a valid sequence of frames.
+ if (!parent_frame)
+ break;
+
+ // Record the first valid return address, even if this is an inlined frame,
+ // in order to look up the associated call edge in the first non-inlined
+ // parent frame.
+ if (return_pc == LLDB_INVALID_ADDRESS) {
+ return_pc = parent_frame->GetFrameCodeAddress().GetLoadAddress(&target);
+ LLDB_LOG(log,
+ "Evaluate_DW_OP_entry_value: immediate ancestor with pc = {0:x}",
+ return_pc);
+ }
+
+ // If we've found an inlined frame, skip it (these have no call site
+ // parameters).
+ if (parent_frame->IsInlined())
+ continue;
+
+ // We've found the first non-inlined parent frame.
+ break;
+ }
+ if (!parent_frame || !parent_frame->GetRegisterContext()) {
+ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent frame with reg ctx");
+ return false;
+ }
+
+ Function *parent_func =
+ parent_frame->GetSymbolContext(eSymbolContextFunction).function;
+ if (!parent_func) {
+ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent function");
+ return false;
+ }
+
+ // 2. Find the call edge in the parent function responsible for creating the
+ // current activation.
+ Function *current_func =
+ current_frame->GetSymbolContext(eSymbolContextFunction).function;
+ if (!current_func) {
+ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current function");
+ return false;
+ }
+
+ CallEdge *call_edge = nullptr;
+ ModuleList &modlist = target.GetImages();
+ if (!parent_frame->IsArtificial()) {
+ // If the parent frame is not artificial, the current activation may be
+ // produced by an ambiguous tail call. In this case, refuse to proceed.
+ call_edge = parent_func->GetCallEdgeForReturnAddress(return_pc, target);
+ if (!call_edge) {
+ LLDB_LOG(log,
+ "Evaluate_DW_OP_entry_value: no call edge for retn-pc = {0:x} "
+ "in parent frame {1}",
+ return_pc, parent_func->GetName());
+ return false;
+ }
+ Function *callee_func = call_edge->GetCallee(modlist);
+ if (callee_func != current_func) {
+ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: ambiguous call sequence, "
+ "can't find real parent frame");
+ return false;
+ }
+ } else {
+ // The StackFrameList solver machinery has deduced that an unambiguous tail
+ // call sequence that produced the current activation. The first edge in
+ // the parent that points to the current function must be valid.
+ for (CallEdge &edge : parent_func->GetTailCallingEdges()) {
+ if (edge.GetCallee(modlist) == current_func) {
+ call_edge = &edge;
+ break;
+ }
+ }
+ }
+ if (!call_edge) {
+ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no unambiguous edge from parent "
+ "to current function");
+ return false;
+ }
+
+ // 3. Attempt to locate the DW_OP_entry_value expression in the set of
+ // available call site parameters. If found, evaluate the corresponding
+ // parameter in the context of the parent frame.
+ const uint32_t subexpr_len = opcodes.GetULEB128(&opcode_offset);
+ const void *subexpr_data = opcodes.GetData(&opcode_offset, subexpr_len);
+ if (!subexpr_data) {
+ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: subexpr could not be read");
+ return false;
+ }
+
+ const CallSiteParameter *matched_param = nullptr;
+ for (const CallSiteParameter &param : call_edge->GetCallSiteParameters()) {
+ DataExtractor param_subexpr_extractor;
+ if (!param.LocationInCallee.GetExpressionData(param_subexpr_extractor))
+ continue;
+ lldb::offset_t param_subexpr_offset = 0;
+ const void *param_subexpr_data =
+ param_subexpr_extractor.GetData(&param_subexpr_offset, subexpr_len);
+ if (!param_subexpr_data ||
+ param_subexpr_extractor.BytesLeft(param_subexpr_offset) != 0)
+ continue;
+
+ // At this point, the DW_OP_entry_value sub-expression and the callee-side
+ // expression in the call site parameter are known to have the same length.
+ // Check whether they are equal.
+ //
+ // Note that an equality check is sufficient: the contents of the
+ // DW_OP_entry_value subexpression are only used to identify the right call
+ // site parameter in the parent, and do not require any special handling.
+ if (memcmp(subexpr_data, param_subexpr_data, subexpr_len) == 0) {
+ matched_param = &param;
+ break;
+ }
+ }
+ if (!matched_param) {
+ LLDB_LOG(log,
+ "Evaluate_DW_OP_entry_value: no matching call site param found");
+ return false;
+ }
+
+ // TODO: Add support for DW_OP_push_object_address within a DW_OP_entry_value
+ // subexpresion whenever llvm does.
+ Value result;
+ ExecutionContext parent_exe_ctx = *exe_ctx;
+ parent_exe_ctx.SetFrameSP(parent_frame);
+ const DWARFExpression &param_expr = matched_param->LocationInCaller;
+ if (!param_expr.Evaluate(&parent_exe_ctx,
+ parent_frame->GetRegisterContext().get(),
+ /*loclist_base_addr=*/LLDB_INVALID_ADDRESS,
+ /*initial_value_ptr=*/nullptr,
+ /*object_address_ptr=*/nullptr, result, error_ptr)) {
+ LLDB_LOG(log,
+ "Evaluate_DW_OP_entry_value: call site param evaluation failed");
+ return false;
+ }
+
+ stack.push_back(result);
+ return true;
+}
+
bool DWARFExpression::Evaluate(ExecutionContextScope *exe_scope,
lldb::addr_t loclist_base_load_addr,
const Value *initial_value_ptr,
const Value *object_address_ptr, Value &result,
Status *error_ptr) const {
ExecutionContext exe_ctx(exe_scope);
return Evaluate(&exe_ctx, nullptr, loclist_base_load_addr, initial_value_ptr,
object_address_ptr, result, error_ptr);
}
bool DWARFExpression::Evaluate(ExecutionContext *exe_ctx,
RegisterContext *reg_ctx,
lldb::addr_t loclist_base_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();
if (IsLocationList()) {
lldb::offset_t offset = 0;
addr_t pc;
StackFrame *frame = nullptr;
if (reg_ctx)
pc = reg_ctx->GetPC();
else {
frame = exe_ctx->GetFramePtr();
if (!frame)
return false;
RegisterContextSP reg_ctx_sp = frame->GetRegisterContext();
if (!reg_ctx_sp)
return false;
pc = reg_ctx_sp->GetPC();
}
if (loclist_base_load_addr != LLDB_INVALID_ADDRESS) {
if (pc == LLDB_INVALID_ADDRESS) {
if (error_ptr)
error_ptr->SetErrorString("Invalid PC in frame.");
return false;
}
addr_t curr_loclist_base_load_addr = loclist_base_load_addr;
while (m_data.ValidOffset(offset)) {
// We need to figure out what the value is for the location.
addr_t lo_pc = LLDB_INVALID_ADDRESS;
addr_t hi_pc = LLDB_INVALID_ADDRESS;
if (!AddressRangeForLocationListEntry(m_dwarf_cu, m_data, &offset,
lo_pc, hi_pc))
break;
if (lo_pc == 0 && hi_pc == 0)
break;
lo_pc += curr_loclist_base_load_addr - m_loclist_slide;
hi_pc += curr_loclist_base_load_addr - m_loclist_slide;
uint16_t length = m_data.GetU16(&offset);
if (length > 0 && lo_pc <= pc && pc < hi_pc) {
return DWARFExpression::Evaluate(
exe_ctx, reg_ctx, module_sp,
DataExtractor(m_data, offset, length), m_dwarf_cu, m_reg_kind,
initial_value_ptr, object_address_ptr, result, error_ptr);
}
offset += length;
}
}
if (error_ptr)
error_ptr->SetErrorString("variable not available");
return false;
}
// Not a location list, just a single expression.
return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, m_data,
m_dwarf_cu, m_reg_kind, initial_value_ptr,
object_address_ptr, result, error_ptr);
}
bool DWARFExpression::Evaluate(
ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
lldb::ModuleSP module_sp, const DataExtractor &opcodes,
const DWARFUnit *dwarf_cu, const lldb::RegisterKind reg_kind,
const Value *initial_value_ptr, const Value *object_address_ptr,
Value &result, Status *error_ptr) {
if (opcodes.GetByteSize() == 0) {
if (error_ptr)
error_ptr->SetErrorString(
"no location, value may have been optimized out");
return false;
}
std::vector<Value> stack;
Process *process = nullptr;
StackFrame *frame = nullptr;
if (exe_ctx) {
process = exe_ctx->GetProcessPtr();
frame = exe_ctx->GetFramePtr();
}
if (reg_ctx == nullptr && frame)
reg_ctx = frame->GetRegisterContext().get();
if (initial_value_ptr)
stack.push_back(*initial_value_ptr);
lldb::offset_t offset = 0;
Value tmp;
uint32_t reg_num;
/// Insertion point for evaluating multi-piece expression.
uint64_t op_piece_offset = 0;
Value pieces; // Used for DW_OP_piece
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
while (opcodes.ValidOffset(offset)) {
const lldb::offset_t op_offset = offset;
const uint8_t op = opcodes.GetU8(&offset);
if (log && log->GetVerbose()) {
size_t count = stack.size();
LLDB_LOGF(log, "Stack before operation has %" PRIu64 " values:",
(uint64_t)count);
for (size_t i = 0; i < count; ++i) {
StreamString new_value;
new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
stack[i].Dump(&new_value);
LLDB_LOGF(log, " %s", new_value.GetData());
}
LLDB_LOGF(log, "0x%8.8" PRIx64 ": %s", op_offset,
DW_OP_value_to_name(op));
}
switch (op) {
// The DW_OP_addr operation has a single operand that encodes a machine
// address and whose size is the size of an address on the target machine.
case DW_OP_addr:
stack.push_back(Scalar(opcodes.GetAddress(&offset)));
stack.back().SetValueType(Value::eValueTypeFileAddress);
// Convert the file address to a load address, so subsequent
// DWARF operators can operate on it.
if (frame)
stack.back().ConvertToLoadAddress(module_sp.get(),
frame->CalculateTarget().get());
break;
// The DW_OP_addr_sect_offset4 is used for any location expressions in
// shared libraries that have a location like:
// DW_OP_addr(0x1000)
// If this address resides in a shared library, then this virtual address
// won't make sense when it is evaluated in the context of a running
// process where shared libraries have been slid. To account for this, this
// new address type where we can store the section pointer and a 4 byte
// offset.
// case DW_OP_addr_sect_offset4:
// {
// result_type = eResultTypeFileAddress;
// lldb::Section *sect = (lldb::Section
// *)opcodes.GetMaxU64(&offset, sizeof(void *));
// lldb::addr_t sect_offset = opcodes.GetU32(&offset);
//
// Address so_addr (sect, sect_offset);
// lldb::addr_t load_addr = so_addr.GetLoadAddress();
// if (load_addr != LLDB_INVALID_ADDRESS)
// {
// // We successfully resolve a file address to a load
// // address.
// stack.push_back(load_addr);
// break;
// }
// else
// {
// // We were able
// if (error_ptr)
// error_ptr->SetErrorStringWithFormat ("Section %s in
// %s is not currently loaded.\n",
// sect->GetName().AsCString(),
// sect->GetModule()->GetFileSpec().GetFilename().AsCString());
// return false;
// }
// }
// break;
// OPCODE: DW_OP_deref
// OPERANDS: none
// DESCRIPTION: Pops the top stack entry and treats it as an address.
// The value retrieved from that address is pushed. The size of the data
// retrieved from the dereferenced address is the size of an address on the
// target machine.
case DW_OP_deref: {
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString("Expression stack empty for DW_OP_deref.");
return false;
}
Value::ValueType value_type = stack.back().GetValueType();
switch (value_type) {
case Value::eValueTypeHostAddress: {
void *src = (void *)stack.back().GetScalar().ULongLong();
intptr_t ptr;
::memcpy(&ptr, src, sizeof(void *));
stack.back().GetScalar() = ptr;
stack.back().ClearContext();
} break;
case Value::eValueTypeFileAddress: {
auto file_addr = stack.back().GetScalar().ULongLong(
LLDB_INVALID_ADDRESS);
if (!module_sp) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"need module to resolve file address for DW_OP_deref");
return false;
}
Address so_addr;
if (!module_sp->ResolveFileAddress(file_addr, so_addr)) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"failed to resolve file address in module");
return false;
}
addr_t load_Addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr());
if (load_Addr == LLDB_INVALID_ADDRESS) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"failed to resolve load address");
return false;
}
stack.back().GetScalar() = load_Addr;
stack.back().SetValueType(Value::eValueTypeLoadAddress);
// Fall through to load address code below...
} LLVM_FALLTHROUGH;
case Value::eValueTypeLoadAddress:
if (exe_ctx) {
if (process) {
lldb::addr_t pointer_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
Status error;
lldb::addr_t pointer_value =
process->ReadPointerFromMemory(pointer_addr, error);
if (pointer_value != LLDB_INVALID_ADDRESS) {
stack.back().GetScalar() = pointer_value;
stack.back().ClearContext();
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"Failed to dereference pointer from 0x%" PRIx64
" for DW_OP_deref: %s\n",
pointer_addr, error.AsCString());
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL process for DW_OP_deref.\n");
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL execution context for DW_OP_deref.\n");
return false;
}
break;
default:
break;
}
} break;
// OPCODE: DW_OP_deref_size
// OPERANDS: 1
// 1 - uint8_t that specifies the size of the data to dereference.
// DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top
// stack entry and treats it as an address. The value retrieved from that
// address is pushed. In the DW_OP_deref_size operation, however, the size
// in bytes of the data retrieved from the dereferenced address is
// specified by the single operand. This operand is a 1-byte unsigned
// integral constant whose value may not be larger than the size of an
// address on the target machine. The data retrieved is zero extended to
// the size of an address on the target machine before being pushed on the
// expression stack.
case DW_OP_deref_size: {
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack empty for DW_OP_deref_size.");
return false;
}
uint8_t size = opcodes.GetU8(&offset);
Value::ValueType value_type = stack.back().GetValueType();
switch (value_type) {
case Value::eValueTypeHostAddress: {
void *src = (void *)stack.back().GetScalar().ULongLong();
intptr_t ptr;
::memcpy(&ptr, src, sizeof(void *));
// I can't decide whether the size operand should apply to the bytes in
// their
// lldb-host endianness or the target endianness.. I doubt this'll ever
// come up but I'll opt for assuming big endian regardless.
switch (size) {
case 1:
ptr = ptr & 0xff;
break;
case 2:
ptr = ptr & 0xffff;
break;
case 3:
ptr = ptr & 0xffffff;
break;
case 4:
ptr = ptr & 0xffffffff;
break;
// the casts are added to work around the case where intptr_t is a 32
// bit quantity;
// presumably we won't hit the 5..7 cases if (void*) is 32-bits in this
// program.
case 5:
ptr = (intptr_t)ptr & 0xffffffffffULL;
break;
case 6:
ptr = (intptr_t)ptr & 0xffffffffffffULL;
break;
case 7:
ptr = (intptr_t)ptr & 0xffffffffffffffULL;
break;
default:
break;
}
stack.back().GetScalar() = ptr;
stack.back().ClearContext();
} break;
case Value::eValueTypeLoadAddress:
if (exe_ctx) {
if (process) {
lldb::addr_t pointer_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
uint8_t addr_bytes[sizeof(lldb::addr_t)];
Status error;
if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) ==
size) {
DataExtractor addr_data(addr_bytes, sizeof(addr_bytes),
process->GetByteOrder(), size);
lldb::offset_t addr_data_offset = 0;
switch (size) {
case 1:
stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset);
break;
case 2:
stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset);
break;
case 4:
stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset);
break;
case 8:
stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset);
break;
default:
stack.back().GetScalar() =
addr_data.GetPointer(&addr_data_offset);
}
stack.back().ClearContext();
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"Failed to dereference pointer from 0x%" PRIx64
" for DW_OP_deref: %s\n",
pointer_addr, error.AsCString());
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL process for DW_OP_deref.\n");
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL execution context for DW_OP_deref.\n");
return false;
}
break;
default:
break;
}
} break;
// OPCODE: DW_OP_xderef_size
// OPERANDS: 1
// 1 - uint8_t that specifies the size of the data to dereference.
// DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at
// the top of the stack is treated as an address. The second stack entry is
// treated as an "address space identifier" for those architectures that
// support multiple address spaces. The top two stack elements are popped,
// a data item is retrieved through an implementation-defined address
// calculation and pushed as the new stack top. In the DW_OP_xderef_size
// operation, however, the size in bytes of the data retrieved from the
// dereferenced address is specified by the single operand. This operand is
// a 1-byte unsigned integral constant whose value may not be larger than
// the size of an address on the target machine. The data retrieved is zero
// extended to the size of an address on the target machine before being
// pushed on the expression stack.
case DW_OP_xderef_size:
if (error_ptr)
error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size.");
return false;
// OPCODE: DW_OP_xderef
// OPERANDS: none
// DESCRIPTION: Provides an extended dereference mechanism. The entry at
// the top of the stack is treated as an address. The second stack entry is
// treated as an "address space identifier" for those architectures that
// support multiple address spaces. The top two stack elements are popped,
// a data item is retrieved through an implementation-defined address
// calculation and pushed as the new stack top. The size of the data
// retrieved from the dereferenced address is the size of an address on the
// target machine.
case DW_OP_xderef:
if (error_ptr)
error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef.");
return false;
// All DW_OP_constXXX opcodes have a single operand as noted below:
//
// Opcode Operand 1
// DW_OP_const1u 1-byte unsigned integer constant DW_OP_const1s
// 1-byte signed integer constant DW_OP_const2u 2-byte unsigned integer
// constant DW_OP_const2s 2-byte signed integer constant DW_OP_const4u
// 4-byte unsigned integer constant DW_OP_const4s 4-byte signed integer
// constant DW_OP_const8u 8-byte unsigned integer constant DW_OP_const8s
// 8-byte signed integer constant DW_OP_constu unsigned LEB128 integer
// constant DW_OP_consts signed LEB128 integer constant
case DW_OP_const1u:
stack.push_back(Scalar((uint8_t)opcodes.GetU8(&offset)));
break;
case DW_OP_const1s:
stack.push_back(Scalar((int8_t)opcodes.GetU8(&offset)));
break;
case DW_OP_const2u:
stack.push_back(Scalar((uint16_t)opcodes.GetU16(&offset)));
break;
case DW_OP_const2s:
stack.push_back(Scalar((int16_t)opcodes.GetU16(&offset)));
break;
case DW_OP_const4u:
stack.push_back(Scalar((uint32_t)opcodes.GetU32(&offset)));
break;
case DW_OP_const4s:
stack.push_back(Scalar((int32_t)opcodes.GetU32(&offset)));
break;
case DW_OP_const8u:
stack.push_back(Scalar((uint64_t)opcodes.GetU64(&offset)));
break;
case DW_OP_const8s:
stack.push_back(Scalar((int64_t)opcodes.GetU64(&offset)));
break;
case DW_OP_constu:
stack.push_back(Scalar(opcodes.GetULEB128(&offset)));
break;
case DW_OP_consts:
stack.push_back(Scalar(opcodes.GetSLEB128(&offset)));
break;
// OPCODE: DW_OP_dup
// OPERANDS: none
// DESCRIPTION: duplicates the value at the top of the stack
case DW_OP_dup:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString("Expression stack empty for DW_OP_dup.");
return false;
} else
stack.push_back(stack.back());
break;
// OPCODE: DW_OP_drop
// OPERANDS: none
// DESCRIPTION: pops the value at the top of the stack
case DW_OP_drop:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString("Expression stack empty for DW_OP_drop.");
return false;
} else
stack.pop_back();
break;
// OPCODE: DW_OP_over
// OPERANDS: none
// DESCRIPTION: Duplicates the entry currently second in the stack at
// the top of the stack.
case DW_OP_over:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_over.");
return false;
} else
stack.push_back(stack[stack.size() - 2]);
break;
// OPCODE: DW_OP_pick
// OPERANDS: uint8_t index into the current stack
// DESCRIPTION: The stack entry with the specified index (0 through 255,
// inclusive) is pushed on the stack
case DW_OP_pick: {
uint8_t pick_idx = opcodes.GetU8(&offset);
if (pick_idx < stack.size())
stack.push_back(stack[stack.size() - 1 - pick_idx]);
else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"Index %u out of range for DW_OP_pick.\n", pick_idx);
return false;
}
} break;
// OPCODE: DW_OP_swap
// OPERANDS: none
// DESCRIPTION: swaps the top two stack entries. The entry at the top
// of the stack becomes the second stack entry, and the second entry
// becomes the top of the stack
case DW_OP_swap:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_swap.");
return false;
} else {
tmp = stack.back();
stack.back() = stack[stack.size() - 2];
stack[stack.size() - 2] = tmp;
}
break;
// OPCODE: DW_OP_rot
// OPERANDS: none
// DESCRIPTION: Rotates the first three stack entries. The entry at
// the top of the stack becomes the third stack entry, the second entry
// becomes the top of the stack, and the third entry becomes the second
// entry.
case DW_OP_rot:
if (stack.size() < 3) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 3 items for DW_OP_rot.");
return false;
} else {
size_t last_idx = stack.size() - 1;
Value old_top = stack[last_idx];
stack[last_idx] = stack[last_idx - 1];
stack[last_idx - 1] = stack[last_idx - 2];
stack[last_idx - 2] = old_top;
}
break;
// OPCODE: DW_OP_abs
// OPERANDS: none
// DESCRIPTION: pops the top stack entry, interprets it as a signed
// value and pushes its absolute value. If the absolute value can not be
// represented, the result is undefined.
case DW_OP_abs:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_abs.");
return false;
} else if (!stack.back().ResolveValue(exe_ctx).AbsoluteValue()) {
if (error_ptr)
error_ptr->SetErrorString(
"Failed to take the absolute value of the first stack item.");
return false;
}
break;
// OPCODE: DW_OP_and
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, performs a bitwise and
// operation on the two, and pushes the result.
case DW_OP_and:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_and.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_div
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, divides the former second
// entry by the former top of the stack using signed division, and pushes
// the result.
case DW_OP_div:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_div.");
return false;
} else {
tmp = stack.back();
if (tmp.ResolveValue(exe_ctx).IsZero()) {
if (error_ptr)
error_ptr->SetErrorString("Divide by zero.");
return false;
} else {
stack.pop_back();
stack.back() =
stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx);
if (!stack.back().ResolveValue(exe_ctx).IsValid()) {
if (error_ptr)
error_ptr->SetErrorString("Divide failed.");
return false;
}
}
}
break;
// OPCODE: DW_OP_minus
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, subtracts the former top
// of the stack from the former second entry, and pushes the result.
case DW_OP_minus:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_minus.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_mod
// OPERANDS: none
// DESCRIPTION: pops the top two stack values and pushes the result of
// the calculation: former second stack entry modulo the former top of the
// stack.
case DW_OP_mod:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_mod.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_mul
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, multiplies them
// together, and pushes the result.
case DW_OP_mul:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_mul.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_neg
// OPERANDS: none
// DESCRIPTION: pops the top stack entry, and pushes its negation.
case DW_OP_neg:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_neg.");
return false;
} else {
if (!stack.back().ResolveValue(exe_ctx).UnaryNegate()) {
if (error_ptr)
error_ptr->SetErrorString("Unary negate failed.");
return false;
}
}
break;
// OPCODE: DW_OP_not
// OPERANDS: none
// DESCRIPTION: pops the top stack entry, and pushes its bitwise
// complement
case DW_OP_not:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_not.");
return false;
} else {
if (!stack.back().ResolveValue(exe_ctx).OnesComplement()) {
if (error_ptr)
error_ptr->SetErrorString("Logical NOT failed.");
return false;
}
}
break;
// OPCODE: DW_OP_or
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, performs a bitwise or
// operation on the two, and pushes the result.
case DW_OP_or:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_or.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_plus
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, adds them together, and
// pushes the result.
case DW_OP_plus:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_plus.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().GetScalar() += tmp.GetScalar();
}
break;
// OPCODE: DW_OP_plus_uconst
// OPERANDS: none
// DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128
// constant operand and pushes the result.
case DW_OP_plus_uconst:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_plus_uconst.");
return false;
} else {
const uint64_t uconst_value = opcodes.GetULEB128(&offset);
// Implicit conversion from a UINT to a Scalar...
stack.back().GetScalar() += uconst_value;
if (!stack.back().GetScalar().IsValid()) {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_plus_uconst failed.");
return false;
}
}
break;
// OPCODE: DW_OP_shl
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, shifts the former
// second entry left by the number of bits specified by the former top of
// the stack, and pushes the result.
case DW_OP_shl:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_shl.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_shr
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, shifts the former second
// entry right logically (filling with zero bits) by the number of bits
// specified by the former top of the stack, and pushes the result.
case DW_OP_shr:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_shr.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical(
tmp.ResolveValue(exe_ctx))) {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_shr failed.");
return false;
}
}
break;
// OPCODE: DW_OP_shra
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, shifts the former second
// entry right arithmetically (divide the magnitude by 2, keep the same
// sign for the result) by the number of bits specified by the former top
// of the stack, and pushes the result.
case DW_OP_shra:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_shra.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_xor
// OPERANDS: none
// DESCRIPTION: pops the top two stack entries, performs the bitwise
// exclusive-or operation on the two, and pushes the result.
case DW_OP_xor:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_xor.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_skip
// OPERANDS: int16_t
// DESCRIPTION: An unconditional branch. Its single operand is a 2-byte
// signed integer constant. The 2-byte constant is the number of bytes of
// the DWARF expression to skip forward or backward from the current
// operation, beginning after the 2-byte constant.
case DW_OP_skip: {
int16_t skip_offset = (int16_t)opcodes.GetU16(&offset);
lldb::offset_t new_offset = offset + skip_offset;
if (opcodes.ValidOffset(new_offset))
offset = new_offset;
else {
if (error_ptr)
error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip.");
return false;
}
} break;
// OPCODE: DW_OP_bra
// OPERANDS: int16_t
// DESCRIPTION: A conditional branch. Its single operand is a 2-byte
// signed integer constant. This operation pops the top of stack. If the
// value popped is not the constant 0, the 2-byte constant operand is the
// number of bytes of the DWARF expression to skip forward or backward from
// the current operation, beginning after the 2-byte constant.
case DW_OP_bra:
if (stack.empty()) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_bra.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
int16_t bra_offset = (int16_t)opcodes.GetU16(&offset);
Scalar zero(0);
if (tmp.ResolveValue(exe_ctx) != zero) {
lldb::offset_t new_offset = offset + bra_offset;
if (opcodes.ValidOffset(new_offset))
offset = new_offset;
else {
if (error_ptr)
error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra.");
return false;
}
}
}
break;
// OPCODE: DW_OP_eq
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// equals (==) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
case DW_OP_eq:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_eq.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_ge
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// greater than or equal to (>=) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
case DW_OP_ge:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_ge.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_gt
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// greater than (>) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
case DW_OP_gt:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_gt.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_le
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// less than or equal to (<=) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
case DW_OP_le:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_le.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_lt
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// less than (<) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
case DW_OP_lt:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_lt.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_ne
// OPERANDS: none
// DESCRIPTION: pops the top two stack values, compares using the
// not equal (!=) operator.
// STACK RESULT: push the constant value 1 onto the stack if the result
// of the operation is true or the constant value 0 if the result of the
// operation is false.
case DW_OP_ne:
if (stack.size() < 2) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 2 items for DW_OP_ne.");
return false;
} else {
tmp = stack.back();
stack.pop_back();
stack.back().ResolveValue(exe_ctx) =
stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx);
}
break;
// OPCODE: DW_OP_litn
// OPERANDS: none
// DESCRIPTION: encode the unsigned literal values from 0 through 31.
// STACK RESULT: push the unsigned literal constant value onto the top
// of the stack.
case DW_OP_lit0:
case DW_OP_lit1:
case DW_OP_lit2:
case DW_OP_lit3:
case DW_OP_lit4:
case DW_OP_lit5:
case DW_OP_lit6:
case DW_OP_lit7:
case DW_OP_lit8:
case DW_OP_lit9:
case DW_OP_lit10:
case DW_OP_lit11:
case DW_OP_lit12:
case DW_OP_lit13:
case DW_OP_lit14:
case DW_OP_lit15:
case DW_OP_lit16:
case DW_OP_lit17:
case DW_OP_lit18:
case DW_OP_lit19:
case DW_OP_lit20:
case DW_OP_lit21:
case DW_OP_lit22:
case DW_OP_lit23:
case DW_OP_lit24:
case DW_OP_lit25:
case DW_OP_lit26:
case DW_OP_lit27:
case DW_OP_lit28:
case DW_OP_lit29:
case DW_OP_lit30:
case DW_OP_lit31:
stack.push_back(Scalar((uint64_t)(op - DW_OP_lit0)));
break;
// OPCODE: DW_OP_regN
// OPERANDS: none
// DESCRIPTION: Push the value in register n on the top of the stack.
case DW_OP_reg0:
case DW_OP_reg1:
case DW_OP_reg2:
case DW_OP_reg3:
case DW_OP_reg4:
case DW_OP_reg5:
case DW_OP_reg6:
case DW_OP_reg7:
case DW_OP_reg8:
case DW_OP_reg9:
case DW_OP_reg10:
case DW_OP_reg11:
case DW_OP_reg12:
case DW_OP_reg13:
case DW_OP_reg14:
case DW_OP_reg15:
case DW_OP_reg16:
case DW_OP_reg17:
case DW_OP_reg18:
case DW_OP_reg19:
case DW_OP_reg20:
case DW_OP_reg21:
case DW_OP_reg22:
case DW_OP_reg23:
case DW_OP_reg24:
case DW_OP_reg25:
case DW_OP_reg26:
case DW_OP_reg27:
case DW_OP_reg28:
case DW_OP_reg29:
case DW_OP_reg30:
case DW_OP_reg31: {
reg_num = op - DW_OP_reg0;
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
stack.push_back(tmp);
else
return false;
} break;
// OPCODE: DW_OP_regx
// OPERANDS:
// ULEB128 literal operand that encodes the register.
// DESCRIPTION: Push the value in register on the top of the stack.
case DW_OP_regx: {
reg_num = opcodes.GetULEB128(&offset);
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
stack.push_back(tmp);
else
return false;
} break;
// OPCODE: DW_OP_bregN
// OPERANDS:
// SLEB128 offset from register N
// DESCRIPTION: Value is in memory at the address specified by register
// N plus an offset.
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31: {
reg_num = op - DW_OP_breg0;
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr,
tmp)) {
int64_t breg_offset = opcodes.GetSLEB128(&offset);
tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
tmp.ClearContext();
stack.push_back(tmp);
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} else
return false;
} break;
// OPCODE: DW_OP_bregx
// OPERANDS: 2
// ULEB128 literal operand that encodes the register.
// SLEB128 offset from register N
// DESCRIPTION: Value is in memory at the address specified by register
// N plus an offset.
case DW_OP_bregx: {
reg_num = opcodes.GetULEB128(&offset);
if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr,
tmp)) {
int64_t breg_offset = opcodes.GetSLEB128(&offset);
tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
tmp.ClearContext();
stack.push_back(tmp);
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} else
return false;
} break;
case DW_OP_fbreg:
if (exe_ctx) {
if (frame) {
Scalar value;
if (frame->GetFrameBaseValue(value, error_ptr)) {
int64_t fbreg_offset = opcodes.GetSLEB128(&offset);
value += fbreg_offset;
stack.push_back(value);
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} else
return false;
} else {
if (error_ptr)
error_ptr->SetErrorString(
"Invalid stack frame in context for DW_OP_fbreg opcode.");
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"NULL execution context for DW_OP_fbreg.\n");
return false;
}
break;
// OPCODE: DW_OP_nop
// OPERANDS: none
// DESCRIPTION: A place holder. It has no effect on the location stack
// or any of its values.
case DW_OP_nop:
break;
// OPCODE: DW_OP_piece
// OPERANDS: 1
// ULEB128: byte size of the piece
// DESCRIPTION: The operand describes the size in bytes of the piece of
// the object referenced by the DWARF expression whose result is at the top
// of the stack. If the piece is located in a register, but does not occupy
// the entire register, the placement of the piece within that register is
// defined by the ABI.
//
// Many compilers store a single variable in sets of registers, or store a
// variable partially in memory and partially in registers. DW_OP_piece
// provides a way of describing how large a part of a variable a particular
// DWARF expression refers to.
case DW_OP_piece: {
const uint64_t piece_byte_size = opcodes.GetULEB128(&offset);
if (piece_byte_size > 0) {
Value curr_piece;
if (stack.empty()) {
// In a multi-piece expression, this means that the current piece is
// not available. Fill with zeros for now by resizing the data and
// appending it
curr_piece.ResizeData(piece_byte_size);
::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size);
pieces.AppendDataToHostBuffer(curr_piece);
} else {
Status error;
// Extract the current piece into "curr_piece"
Value curr_piece_source_value(stack.back());
stack.pop_back();
const Value::ValueType curr_piece_source_value_type =
curr_piece_source_value.GetValueType();
switch (curr_piece_source_value_type) {
case Value::eValueTypeLoadAddress:
if (process) {
if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) {
lldb::addr_t load_addr =
curr_piece_source_value.GetScalar().ULongLong(
LLDB_INVALID_ADDRESS);
if (process->ReadMemory(
load_addr, curr_piece.GetBuffer().GetBytes(),
piece_byte_size, error) != piece_byte_size) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"failed to read memory DW_OP_piece(%" PRIu64
") from 0x%" PRIx64,
piece_byte_size, load_addr);
return false;
}
} else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"failed to resize the piece memory buffer for "
"DW_OP_piece(%" PRIu64 ")",
piece_byte_size);
return false;
}
}
break;
case Value::eValueTypeFileAddress:
case Value::eValueTypeHostAddress:
if (error_ptr) {
lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong(
LLDB_INVALID_ADDRESS);
error_ptr->SetErrorStringWithFormat(
"failed to read memory DW_OP_piece(%" PRIu64
") from %s address 0x%" PRIx64,
piece_byte_size, curr_piece_source_value.GetValueType() ==
Value::eValueTypeFileAddress
? "file"
: "host",
addr);
}
return false;
case Value::eValueTypeScalar: {
uint32_t bit_size = piece_byte_size * 8;
uint32_t bit_offset = 0;
if (!curr_piece_source_value.GetScalar().ExtractBitfield(
bit_size, bit_offset)) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"unable to extract %" PRIu64 " bytes from a %" PRIu64
" byte scalar value.",
piece_byte_size,
(uint64_t)curr_piece_source_value.GetScalar()
.GetByteSize());
return false;
}
curr_piece = curr_piece_source_value;
} break;
case Value::eValueTypeVector: {
if (curr_piece_source_value.GetVector().length >= piece_byte_size)
curr_piece_source_value.GetVector().length = piece_byte_size;
else {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"unable to extract %" PRIu64 " bytes from a %" PRIu64
" byte vector value.",
piece_byte_size,
(uint64_t)curr_piece_source_value.GetVector().length);
return false;
}
} break;
}
// Check if this is the first piece?
if (op_piece_offset == 0) {
// This is the first piece, we should push it back onto the stack
// so subsequent pieces will be able to access this piece and add
// to it
if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
if (error_ptr)
error_ptr->SetErrorString("failed to append piece data");
return false;
}
} else {
// If this is the second or later piece there should be a value on
// the stack
if (pieces.GetBuffer().GetByteSize() != op_piece_offset) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"DW_OP_piece for offset %" PRIu64
" but top of stack is of size %" PRIu64,
op_piece_offset, pieces.GetBuffer().GetByteSize());
return false;
}
if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
if (error_ptr)
error_ptr->SetErrorString("failed to append piece data");
return false;
}
}
op_piece_offset += piece_byte_size;
}
}
} break;
case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3);
if (stack.size() < 1) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_bit_piece.");
return false;
} else {
const uint64_t piece_bit_size = opcodes.GetULEB128(&offset);
const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset);
switch (stack.back().GetValueType()) {
case Value::eValueTypeScalar: {
if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size,
piece_bit_offset)) {
if (error_ptr)
error_ptr->SetErrorStringWithFormat(
"unable to extract %" PRIu64 " bit value with %" PRIu64
" bit offset from a %" PRIu64 " bit scalar value.",
piece_bit_size, piece_bit_offset,
(uint64_t)(stack.back().GetScalar().GetByteSize() * 8));
return false;
}
} break;
case Value::eValueTypeFileAddress:
case Value::eValueTypeLoadAddress:
case Value::eValueTypeHostAddress:
if (error_ptr) {
error_ptr->SetErrorStringWithFormat(
"unable to extract DW_OP_bit_piece(bit_size = %" PRIu64
", bit_offset = %" PRIu64 ") from an address value.",
piece_bit_size, piece_bit_offset);
}
return false;
case Value::eValueTypeVector:
if (error_ptr) {
error_ptr->SetErrorStringWithFormat(
"unable to extract DW_OP_bit_piece(bit_size = %" PRIu64
", bit_offset = %" PRIu64 ") from a vector value.",
piece_bit_size, piece_bit_offset);
}
return false;
}
}
break;
// OPCODE: DW_OP_push_object_address
// OPERANDS: none
// DESCRIPTION: Pushes the address of the object currently being
// evaluated as part of evaluation of a user presented expression. This
// object may correspond to an independent variable described by its own
// DIE or it may be a component of an array, structure, or class whose
// address has been dynamically determined by an earlier step during user
// expression evaluation.
case DW_OP_push_object_address:
if (object_address_ptr)
stack.push_back(*object_address_ptr);
else {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_push_object_address used without "
"specifying an object address");
return false;
}
break;
// OPCODE: DW_OP_call2
// OPERANDS:
// uint16_t compile unit relative offset of a DIE
// DESCRIPTION: Performs subroutine calls during evaluation
// of a DWARF expression. The operand is the 2-byte unsigned offset of a
// debugging information entry in the current compilation unit.
//
// Operand interpretation is exactly like that for DW_FORM_ref2.
//
// This operation transfers control of DWARF expression evaluation to the
// DW_AT_location attribute of the referenced DIE. If there is no such
// attribute, then there is no effect. Execution of the DWARF expression of
// a DW_AT_location attribute may add to and/or remove from values on the
// stack. Execution returns to the point following the call when the end of
// the attribute is reached. Values on the stack at the time of the call
// may be used as parameters by the called expression and values left on
// the stack by the called expression may be used as return values by prior
// agreement between the calling and called expressions.
case DW_OP_call2:
if (error_ptr)
error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2.");
return false;
// OPCODE: DW_OP_call4
// OPERANDS: 1
// uint32_t compile unit relative offset of a DIE
// DESCRIPTION: Performs a subroutine call during evaluation of a DWARF
// expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of
// a debugging information entry in the current compilation unit.
//
// Operand interpretation DW_OP_call4 is exactly like that for
// DW_FORM_ref4.
//
// This operation transfers control of DWARF expression evaluation to the
// DW_AT_location attribute of the referenced DIE. If there is no such
// attribute, then there is no effect. Execution of the DWARF expression of
// a DW_AT_location attribute may add to and/or remove from values on the
// stack. Execution returns to the point following the call when the end of
// the attribute is reached. Values on the stack at the time of the call
// may be used as parameters by the called expression and values left on
// the stack by the called expression may be used as return values by prior
// agreement between the calling and called expressions.
case DW_OP_call4:
if (error_ptr)
error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4.");
return false;
// OPCODE: DW_OP_stack_value
// OPERANDS: None
// DESCRIPTION: Specifies that the object does not exist in memory but
// rather is a constant value. The value from the top of the stack is the
// value to be used. This is the actual object value and not the location.
case DW_OP_stack_value:
stack.back().SetValueType(Value::eValueTypeScalar);
break;
// OPCODE: DW_OP_convert
// OPERANDS: 1
// A ULEB128 that is either a DIE offset of a
// DW_TAG_base_type or 0 for the generic (pointer-sized) type.
//
// DESCRIPTION: Pop the top stack element, convert it to a
// different type, and push the result.
case DW_OP_convert: {
if (stack.size() < 1) {
if (error_ptr)
error_ptr->SetErrorString(
"Expression stack needs at least 1 item for DW_OP_convert.");
return false;
}
const uint64_t die_offset = opcodes.GetULEB128(&offset);
Scalar::Type type = Scalar::e_void;
uint64_t bit_size;
if (die_offset == 0) {
// The generic type has the size of an address on the target
// machine and an unspecified signedness. Scalar has no
// "unspecified signedness", so we use unsigned types.
if (!module_sp) {
if (error_ptr)
error_ptr->SetErrorString("No module");
return false;
}
bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8;
if (!bit_size) {
if (error_ptr)
error_ptr->SetErrorString("unspecified architecture");
return false;
}
type = Scalar::GetBestTypeForBitSize(bit_size, false);
} else {
// Retrieve the type DIE that the value is being converted to.
// FIXME: the constness has annoying ripple effects.
DWARFDIE die = const_cast<DWARFUnit *>(dwarf_cu)->GetDIE(die_offset);
if (!die) {
if (error_ptr)
error_ptr->SetErrorString("Cannot resolve DW_OP_convert type DIE");
return false;
}
uint64_t encoding =
die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user);
bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
if (!bit_size)
bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0);
if (!bit_size) {
if (error_ptr)
error_ptr->SetErrorString("Unsupported type size in DW_OP_convert");
return false;
}
switch (encoding) {
case DW_ATE_signed:
case DW_ATE_signed_char:
type = Scalar::GetBestTypeForBitSize(bit_size, true);
break;
case DW_ATE_unsigned:
case DW_ATE_unsigned_char:
type = Scalar::GetBestTypeForBitSize(bit_size, false);
break;
default:
if (error_ptr)
error_ptr->SetErrorString("Unsupported encoding in DW_OP_convert");
return false;
}
}
if (type == Scalar::e_void) {
if (error_ptr)
error_ptr->SetErrorString("Unsupported pointer size");
return false;
}
Scalar &top = stack.back().ResolveValue(exe_ctx);
top.TruncOrExtendTo(type, bit_size);
break;
}
// OPCODE: DW_OP_call_frame_cfa
// OPERANDS: None
// DESCRIPTION: Specifies a DWARF expression that pushes the value of
// the canonical frame address consistent with the call frame information
// located in .debug_frame (or in the FDEs of the eh_frame section).
case DW_OP_call_frame_cfa:
if (frame) {
// Note that we don't have to parse FDEs because this DWARF expression
// is commonly evaluated with a valid stack frame.
StackID id = frame->GetStackID();
addr_t cfa = id.GetCallFrameAddress();
if (cfa != LLDB_INVALID_ADDRESS) {
stack.push_back(Scalar(cfa));
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} else if (error_ptr)
error_ptr->SetErrorString("Stack frame does not include a canonical "
"frame address for DW_OP_call_frame_cfa "
"opcode.");
} else {
if (error_ptr)
error_ptr->SetErrorString("Invalid stack frame in context for "
"DW_OP_call_frame_cfa opcode.");
return false;
}
break;
// OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension
// opcode, DW_OP_GNU_push_tls_address)
// OPERANDS: none
// DESCRIPTION: Pops a TLS offset from the stack, converts it to
// an address in the current thread's thread-local storage block, and
// pushes it on the stack.
case DW_OP_form_tls_address:
case DW_OP_GNU_push_tls_address: {
if (stack.size() < 1) {
if (error_ptr) {
if (op == DW_OP_form_tls_address)
error_ptr->SetErrorString(
"DW_OP_form_tls_address needs an argument.");
else
error_ptr->SetErrorString(
"DW_OP_GNU_push_tls_address needs an argument.");
}
return false;
}
if (!exe_ctx || !module_sp) {
if (error_ptr)
error_ptr->SetErrorString("No context to evaluate TLS within.");
return false;
}
Thread *thread = exe_ctx->GetThreadPtr();
if (!thread) {
if (error_ptr)
error_ptr->SetErrorString("No thread to evaluate TLS within.");
return false;
}
// Lookup the TLS block address for this thread and module.
const addr_t tls_file_addr =
stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
const addr_t tls_load_addr =
thread->GetThreadLocalData(module_sp, tls_file_addr);
if (tls_load_addr == LLDB_INVALID_ADDRESS) {
if (error_ptr)
error_ptr->SetErrorString(
"No TLS data currently exists for this thread.");
return false;
}
stack.back().GetScalar() = tls_load_addr;
stack.back().SetValueType(Value::eValueTypeLoadAddress);
} break;
// OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.)
// OPERANDS: 1
// ULEB128: index to the .debug_addr section
// DESCRIPTION: Pushes an address to the stack from the .debug_addr
// section with the base address specified by the DW_AT_addr_base attribute
// and the 0 based index is the ULEB128 encoded index.
case DW_OP_addrx:
case DW_OP_GNU_addr_index: {
if (!dwarf_cu) {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a "
"compile unit being specified");
return false;
}
uint64_t index = opcodes.GetULEB128(&offset);
lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index);
stack.push_back(Scalar(value));
stack.back().SetValueType(Value::eValueTypeFileAddress);
} break;
// OPCODE: DW_OP_GNU_const_index
// OPERANDS: 1
// ULEB128: index to the .debug_addr section
// DESCRIPTION: Pushes an constant with the size of a machine address to
// the stack from the .debug_addr section with the base address specified
// by the DW_AT_addr_base attribute and the 0 based index is the ULEB128
// encoded index.
case DW_OP_GNU_const_index: {
if (!dwarf_cu) {
if (error_ptr)
error_ptr->SetErrorString("DW_OP_GNU_const_index found without a "
"compile unit being specified");
return false;
}
uint64_t index = opcodes.GetULEB128(&offset);
lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index);
stack.push_back(Scalar(value));
} break;
+ case DW_OP_entry_value: {
+ if (!Evaluate_DW_OP_entry_value(stack, exe_ctx, reg_ctx, opcodes, offset,
+ error_ptr, log)) {
+ LLDB_ERRORF(error_ptr, "Could not evaluate %s.",
+ DW_OP_value_to_name(op));
+ return false;
+ }
+ break;
+ }
+
default:
LLDB_LOGF(log, "Unhandled opcode %s in DWARFExpression.",
DW_OP_value_to_name(op));
break;
}
}
if (stack.empty()) {
// Nothing on the stack, check if we created a piece value from DW_OP_piece
// or DW_OP_bit_piece opcodes
if (pieces.GetBuffer().GetByteSize()) {
result = pieces;
} else {
if (error_ptr)
error_ptr->SetErrorString("Stack empty after evaluation.");
return false;
}
} else {
if (log && log->GetVerbose()) {
size_t count = stack.size();
LLDB_LOGF(log, "Stack after operation has %" PRIu64 " values:",
(uint64_t)count);
for (size_t i = 0; i < count; ++i) {
StreamString new_value;
new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
stack[i].Dump(&new_value);
LLDB_LOGF(log, " %s", new_value.GetData());
}
}
result = stack.back();
}
return true; // Return true on success
}
bool DWARFExpression::AddressRangeForLocationListEntry(
const DWARFUnit *dwarf_cu, const DataExtractor &debug_loc_data,
lldb::offset_t *offset_ptr, lldb::addr_t &low_pc, lldb::addr_t &high_pc) {
if (!debug_loc_data.ValidOffset(*offset_ptr))
return false;
DWARFExpression::LocationListFormat format =
dwarf_cu->GetSymbolFileDWARF().GetLocationListFormat();
switch (format) {
case NonLocationList:
return false;
case RegularLocationList:
low_pc = debug_loc_data.GetAddress(offset_ptr);
high_pc = debug_loc_data.GetAddress(offset_ptr);
return true;
case SplitDwarfLocationList:
case LocLists:
switch (debug_loc_data.GetU8(offset_ptr)) {
case DW_LLE_end_of_list:
return false;
case DW_LLE_startx_endx: {
uint64_t index = debug_loc_data.GetULEB128(offset_ptr);
low_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index);
index = debug_loc_data.GetULEB128(offset_ptr);
high_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index);
return true;
}
case DW_LLE_startx_length: {
uint64_t index = debug_loc_data.GetULEB128(offset_ptr);
low_pc = ReadAddressFromDebugAddrSection(dwarf_cu, index);
uint64_t length = (format == LocLists)
? debug_loc_data.GetULEB128(offset_ptr)
: debug_loc_data.GetU32(offset_ptr);
high_pc = low_pc + length;
return true;
}
case DW_LLE_start_length: {
low_pc = debug_loc_data.GetAddress(offset_ptr);
high_pc = low_pc + debug_loc_data.GetULEB128(offset_ptr);
return true;
}
case DW_LLE_start_end: {
low_pc = debug_loc_data.GetAddress(offset_ptr);
high_pc = debug_loc_data.GetAddress(offset_ptr);
return true;
}
default:
// Not supported entry type
lldbassert(false && "Not supported location list type");
return false;
}
}
assert(false && "Not supported location list type");
return false;
}
static bool print_dwarf_exp_op(Stream &s, const DataExtractor &data,
lldb::offset_t *offset_ptr, int address_size,
int dwarf_ref_size) {
uint8_t opcode = data.GetU8(offset_ptr);
DRC_class opcode_class;
uint64_t uint;
int64_t sint;
int size;
opcode_class = DW_OP_value_to_class(opcode) & (~DRC_DWARFv3);
s.Printf("%s ", DW_OP_value_to_name(opcode));
/* Does this take zero parameters? If so we can shortcut this function. */
if (opcode_class == DRC_ZEROOPERANDS)
return true;
if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_bregx) {
uint = data.GetULEB128(offset_ptr);
sint = data.GetSLEB128(offset_ptr);
s.Printf("%" PRIu64 " %" PRIi64, uint, sint);
return true;
}
+ if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_entry_value) {
+ uint = data.GetULEB128(offset_ptr);
+ s.Printf("%" PRIu64 " ", uint);
+ return true;
+ }
if (opcode_class != DRC_ONEOPERAND) {
s.Printf("UNKNOWN OP %u", opcode);
return false;
}
switch (opcode) {
case DW_OP_addr:
size = address_size;
break;
case DW_OP_const1u:
size = 1;
break;
case DW_OP_const1s:
size = -1;
break;
case DW_OP_const2u:
size = 2;
break;
case DW_OP_const2s:
size = -2;
break;
case DW_OP_const4u:
size = 4;
break;
case DW_OP_const4s:
size = -4;
break;
case DW_OP_const8u:
size = 8;
break;
case DW_OP_const8s:
size = -8;
break;
case DW_OP_constu:
size = 128;
break;
case DW_OP_consts:
size = -128;
break;
case DW_OP_fbreg:
size = -128;
break;
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31:
size = -128;
break;
case DW_OP_pick:
case DW_OP_deref_size:
case DW_OP_xderef_size:
size = 1;
break;
case DW_OP_skip:
case DW_OP_bra:
size = -2;
break;
case DW_OP_call2:
size = 2;
break;
case DW_OP_call4:
size = 4;
break;
case DW_OP_call_ref:
size = dwarf_ref_size;
break;
case DW_OP_addrx:
case DW_OP_piece:
case DW_OP_plus_uconst:
case DW_OP_regx:
case DW_OP_GNU_addr_index:
case DW_OP_GNU_const_index:
+ case DW_OP_entry_value:
size = 128;
break;
default:
s.Printf("UNKNOWN ONE-OPERAND OPCODE, #%u", opcode);
return false;
}
switch (size) {
case -1:
sint = (int8_t)data.GetU8(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case -2:
sint = (int16_t)data.GetU16(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case -4:
sint = (int32_t)data.GetU32(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case -8:
sint = (int64_t)data.GetU64(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case -128:
sint = data.GetSLEB128(offset_ptr);
s.Printf("%+" PRIi64, sint);
break;
case 1:
uint = data.GetU8(offset_ptr);
s.Printf("0x%2.2" PRIx64, uint);
break;
case 2:
uint = data.GetU16(offset_ptr);
s.Printf("0x%4.4" PRIx64, uint);
break;
case 4:
uint = data.GetU32(offset_ptr);
s.Printf("0x%8.8" PRIx64, uint);
break;
case 8:
uint = data.GetU64(offset_ptr);
s.Printf("0x%16.16" PRIx64, uint);
break;
case 128:
uint = data.GetULEB128(offset_ptr);
s.Printf("0x%" PRIx64, uint);
break;
}
return true;
}
bool DWARFExpression::PrintDWARFExpression(Stream &s, const DataExtractor &data,
int address_size, int dwarf_ref_size,
bool location_expression) {
int op_count = 0;
lldb::offset_t offset = 0;
while (data.ValidOffset(offset)) {
if (location_expression && op_count > 0)
return false;
if (op_count > 0)
s.PutCString(", ");
if (!print_dwarf_exp_op(s, data, &offset, address_size, dwarf_ref_size))
return false;
op_count++;
}
return true;
}
void DWARFExpression::PrintDWARFLocationList(
Stream &s, const DWARFUnit *cu, const DataExtractor &debug_loc_data,
lldb::offset_t offset) {
uint64_t start_addr, end_addr;
uint32_t addr_size = DWARFUnit::GetAddressByteSize(cu);
s.SetAddressByteSize(DWARFUnit::GetAddressByteSize(cu));
dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0;
while (debug_loc_data.ValidOffset(offset)) {
start_addr = debug_loc_data.GetMaxU64(&offset, addr_size);
end_addr = debug_loc_data.GetMaxU64(&offset, addr_size);
if (start_addr == 0 && end_addr == 0)
break;
s.PutCString("\n ");
s.Indent();
if (cu)
s.AddressRange(start_addr + base_addr, end_addr + base_addr,
cu->GetAddressByteSize(), nullptr, ": ");
uint32_t loc_length = debug_loc_data.GetU16(&offset);
DataExtractor locationData(debug_loc_data, offset, loc_length);
PrintDWARFExpression(s, locationData, addr_size, 4, false);
offset += loc_length;
}
}
bool DWARFExpression::GetOpAndEndOffsets(StackFrame &frame,
lldb::offset_t &op_offset,
lldb::offset_t &end_offset) {
SymbolContext sc = frame.GetSymbolContext(eSymbolContextFunction);
if (!sc.function) {
return false;
}
addr_t loclist_base_file_addr =
sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) {
return false;
}
addr_t pc_file_addr = frame.GetFrameCodeAddress().GetFileAddress();
lldb::offset_t opcodes_offset, opcodes_length;
if (!GetLocation(loclist_base_file_addr, pc_file_addr, opcodes_offset,
opcodes_length)) {
return false;
}
if (opcodes_length == 0) {
return false;
}
op_offset = opcodes_offset;
end_offset = opcodes_offset + opcodes_length;
return true;
}
bool DWARFExpression::MatchesOperand(StackFrame &frame,
const Instruction::Operand &operand) {
using namespace OperandMatchers;
lldb::offset_t op_offset;
lldb::offset_t end_offset;
if (!GetOpAndEndOffsets(frame, op_offset, end_offset)) {
return false;
}
if (!m_data.ValidOffset(op_offset) || op_offset >= end_offset) {
return false;
}
RegisterContextSP reg_ctx_sp = frame.GetRegisterContext();
if (!reg_ctx_sp) {
return false;
}
DataExtractor opcodes = m_data;
uint8_t opcode = opcodes.GetU8(&op_offset);
if (opcode == DW_OP_fbreg) {
int64_t offset = opcodes.GetSLEB128(&op_offset);
DWARFExpression *fb_expr = frame.GetFrameBaseExpression(nullptr);
if (!fb_expr) {
return false;
}
auto recurse = [&frame, fb_expr](const Instruction::Operand &child) {
return fb_expr->MatchesOperand(frame, child);
};
if (!offset &&
MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference),
recurse)(operand)) {
return true;
}
return MatchUnaryOp(
MatchOpType(Instruction::Operand::Type::Dereference),
MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
MatchImmOp(offset), recurse))(operand);
}
bool dereference = false;
const RegisterInfo *reg = nullptr;
int64_t offset = 0;
if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31) {
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_reg0);
} else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31) {
offset = opcodes.GetSLEB128(&op_offset);
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, opcode - DW_OP_breg0);
} else if (opcode == DW_OP_regx) {
uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset));
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
} else if (opcode == DW_OP_bregx) {
uint32_t reg_num = static_cast<uint32_t>(opcodes.GetULEB128(&op_offset));
offset = opcodes.GetSLEB128(&op_offset);
reg = reg_ctx_sp->GetRegisterInfo(m_reg_kind, reg_num);
} else {
return false;
}
if (!reg) {
return false;
}
if (dereference) {
if (!offset &&
MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference),
MatchRegOp(*reg))(operand)) {
return true;
}
return MatchUnaryOp(
MatchOpType(Instruction::Operand::Type::Dereference),
MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum),
MatchRegOp(*reg),
MatchImmOp(offset)))(operand);
} else {
return MatchRegOp(*reg)(operand);
}
}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
index 3bf0bb0..2ae1bbc 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp
@@ -1,402 +1,406 @@
//===-- DWARFDefines.cpp ----------------------------------------*- C++ -*-===//
//
// 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 "DWARFDefines.h"
#include "lldb/Utility/ConstString.h"
#include <cstdio>
#include <cstring>
#include <string>
namespace lldb_private {
const char *DW_TAG_value_to_name(uint32_t val) {
static char invalid[100];
if (val == 0)
return "NULL";
llvm::StringRef llvmstr = llvm::dwarf::TagString(val);
if (llvmstr.empty()) {
snprintf(invalid, sizeof(invalid), "Unknown DW_TAG constant: 0x%x", val);
return invalid;
}
return llvmstr.data();
}
const char *DW_AT_value_to_name(uint32_t val) {
static char invalid[100];
llvm::StringRef llvmstr = llvm::dwarf::AttributeString(val);
if (llvmstr.empty()) {
snprintf(invalid, sizeof(invalid), "Unknown DW_AT constant: 0x%x", val);
return invalid;
}
return llvmstr.data();
}
const char *DW_FORM_value_to_name(uint32_t val) {
static char invalid[100];
llvm::StringRef llvmstr = llvm::dwarf::FormEncodingString(val);
if (llvmstr.empty()) {
snprintf(invalid, sizeof(invalid), "Unknown DW_FORM constant: 0x%x", val);
return invalid;
}
return llvmstr.data();
}
const char *DW_OP_value_to_name(uint32_t val) {
static char invalid[100];
llvm::StringRef llvmstr = llvm::dwarf::OperationEncodingString(val);
if (llvmstr.empty()) {
snprintf(invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val);
return invalid;
}
return llvmstr.data();
}
DRC_class DW_OP_value_to_class(uint32_t val) {
+ // FIXME: If we just used llvm's DWARFExpression printer, we could delete
+ // all this code (and more in lldb's DWARFExpression.cpp).
switch (val) {
case 0x03:
return DRC_ONEOPERAND;
case 0x06:
return DRC_ZEROOPERANDS;
case 0x08:
return DRC_ONEOPERAND;
case 0x09:
return DRC_ONEOPERAND;
case 0x0a:
return DRC_ONEOPERAND;
case 0x0b:
return DRC_ONEOPERAND;
case 0x0c:
return DRC_ONEOPERAND;
case 0x0d:
return DRC_ONEOPERAND;
case 0x0e:
return DRC_ONEOPERAND;
case 0x0f:
return DRC_ONEOPERAND;
case 0x10:
return DRC_ONEOPERAND;
case 0x11:
return DRC_ONEOPERAND;
case 0x12:
return DRC_ZEROOPERANDS;
case 0x13:
return DRC_ZEROOPERANDS;
case 0x14:
return DRC_ZEROOPERANDS;
case 0x15:
return DRC_ONEOPERAND;
case 0x16:
return DRC_ZEROOPERANDS;
case 0x17:
return DRC_ZEROOPERANDS;
case 0x18:
return DRC_ZEROOPERANDS;
case 0x19:
return DRC_ZEROOPERANDS;
case 0x1a:
return DRC_ZEROOPERANDS;
case 0x1b:
return DRC_ZEROOPERANDS;
case 0x1c:
return DRC_ZEROOPERANDS;
case 0x1d:
return DRC_ZEROOPERANDS;
case 0x1e:
return DRC_ZEROOPERANDS;
case 0x1f:
return DRC_ZEROOPERANDS;
case 0x20:
return DRC_ZEROOPERANDS;
case 0x21:
return DRC_ZEROOPERANDS;
case 0x22:
return DRC_ZEROOPERANDS;
case 0x23:
return DRC_ONEOPERAND;
case 0x24:
return DRC_ZEROOPERANDS;
case 0x25:
return DRC_ZEROOPERANDS;
case 0x26:
return DRC_ZEROOPERANDS;
case 0x27:
return DRC_ZEROOPERANDS;
case 0x2f:
return DRC_ONEOPERAND;
case 0x28:
return DRC_ONEOPERAND;
case 0x29:
return DRC_ZEROOPERANDS;
case 0x2a:
return DRC_ZEROOPERANDS;
case 0x2b:
return DRC_ZEROOPERANDS;
case 0x2c:
return DRC_ZEROOPERANDS;
case 0x2d:
return DRC_ZEROOPERANDS;
case 0x2e:
return DRC_ZEROOPERANDS;
case 0x30:
return DRC_ZEROOPERANDS;
case 0x31:
return DRC_ZEROOPERANDS;
case 0x32:
return DRC_ZEROOPERANDS;
case 0x33:
return DRC_ZEROOPERANDS;
case 0x34:
return DRC_ZEROOPERANDS;
case 0x35:
return DRC_ZEROOPERANDS;
case 0x36:
return DRC_ZEROOPERANDS;
case 0x37:
return DRC_ZEROOPERANDS;
case 0x38:
return DRC_ZEROOPERANDS;
case 0x39:
return DRC_ZEROOPERANDS;
case 0x3a:
return DRC_ZEROOPERANDS;
case 0x3b:
return DRC_ZEROOPERANDS;
case 0x3c:
return DRC_ZEROOPERANDS;
case 0x3d:
return DRC_ZEROOPERANDS;
case 0x3e:
return DRC_ZEROOPERANDS;
case 0x3f:
return DRC_ZEROOPERANDS;
case 0x40:
return DRC_ZEROOPERANDS;
case 0x41:
return DRC_ZEROOPERANDS;
case 0x42:
return DRC_ZEROOPERANDS;
case 0x43:
return DRC_ZEROOPERANDS;
case 0x44:
return DRC_ZEROOPERANDS;
case 0x45:
return DRC_ZEROOPERANDS;
case 0x46:
return DRC_ZEROOPERANDS;
case 0x47:
return DRC_ZEROOPERANDS;
case 0x48:
return DRC_ZEROOPERANDS;
case 0x49:
return DRC_ZEROOPERANDS;
case 0x4a:
return DRC_ZEROOPERANDS;
case 0x4b:
return DRC_ZEROOPERANDS;
case 0x4c:
return DRC_ZEROOPERANDS;
case 0x4d:
return DRC_ZEROOPERANDS;
case 0x4e:
return DRC_ZEROOPERANDS;
case 0x4f:
return DRC_ZEROOPERANDS;
case 0x50:
return DRC_ZEROOPERANDS;
case 0x51:
return DRC_ZEROOPERANDS;
case 0x52:
return DRC_ZEROOPERANDS;
case 0x53:
return DRC_ZEROOPERANDS;
case 0x54:
return DRC_ZEROOPERANDS;
case 0x55:
return DRC_ZEROOPERANDS;
case 0x56:
return DRC_ZEROOPERANDS;
case 0x57:
return DRC_ZEROOPERANDS;
case 0x58:
return DRC_ZEROOPERANDS;
case 0x59:
return DRC_ZEROOPERANDS;
case 0x5a:
return DRC_ZEROOPERANDS;
case 0x5b:
return DRC_ZEROOPERANDS;
case 0x5c:
return DRC_ZEROOPERANDS;
case 0x5d:
return DRC_ZEROOPERANDS;
case 0x5e:
return DRC_ZEROOPERANDS;
case 0x5f:
return DRC_ZEROOPERANDS;
case 0x60:
return DRC_ZEROOPERANDS;
case 0x61:
return DRC_ZEROOPERANDS;
case 0x62:
return DRC_ZEROOPERANDS;
case 0x63:
return DRC_ZEROOPERANDS;
case 0x64:
return DRC_ZEROOPERANDS;
case 0x65:
return DRC_ZEROOPERANDS;
case 0x66:
return DRC_ZEROOPERANDS;
case 0x67:
return DRC_ZEROOPERANDS;
case 0x68:
return DRC_ZEROOPERANDS;
case 0x69:
return DRC_ZEROOPERANDS;
case 0x6a:
return DRC_ZEROOPERANDS;
case 0x6b:
return DRC_ZEROOPERANDS;
case 0x6c:
return DRC_ZEROOPERANDS;
case 0x6d:
return DRC_ZEROOPERANDS;
case 0x6e:
return DRC_ZEROOPERANDS;
case 0x6f:
return DRC_ZEROOPERANDS;
case 0x70:
return DRC_ONEOPERAND;
case 0x71:
return DRC_ONEOPERAND;
case 0x72:
return DRC_ONEOPERAND;
case 0x73:
return DRC_ONEOPERAND;
case 0x74:
return DRC_ONEOPERAND;
case 0x75:
return DRC_ONEOPERAND;
case 0x76:
return DRC_ONEOPERAND;
case 0x77:
return DRC_ONEOPERAND;
case 0x78:
return DRC_ONEOPERAND;
case 0x79:
return DRC_ONEOPERAND;
case 0x7a:
return DRC_ONEOPERAND;
case 0x7b:
return DRC_ONEOPERAND;
case 0x7c:
return DRC_ONEOPERAND;
case 0x7d:
return DRC_ONEOPERAND;
case 0x7e:
return DRC_ONEOPERAND;
case 0x7f:
return DRC_ONEOPERAND;
case 0x80:
return DRC_ONEOPERAND;
case 0x81:
return DRC_ONEOPERAND;
case 0x82:
return DRC_ONEOPERAND;
case 0x83:
return DRC_ONEOPERAND;
case 0x84:
return DRC_ONEOPERAND;
case 0x85:
return DRC_ONEOPERAND;
case 0x86:
return DRC_ONEOPERAND;
case 0x87:
return DRC_ONEOPERAND;
case 0x88:
return DRC_ONEOPERAND;
case 0x89:
return DRC_ONEOPERAND;
case 0x8a:
return DRC_ONEOPERAND;
case 0x8b:
return DRC_ONEOPERAND;
case 0x8c:
return DRC_ONEOPERAND;
case 0x8d:
return DRC_ONEOPERAND;
case 0x8e:
return DRC_ONEOPERAND;
case 0x8f:
return DRC_ONEOPERAND;
case 0x90:
return DRC_ONEOPERAND;
case 0x91:
return DRC_ONEOPERAND;
case 0x92:
return DRC_TWOOPERANDS;
case 0x93:
return DRC_ONEOPERAND;
case 0x94:
return DRC_ONEOPERAND;
case 0x95:
return DRC_ONEOPERAND;
case 0x96:
return DRC_ZEROOPERANDS;
case 0x97:
return DRC_DWARFv3 | DRC_ZEROOPERANDS;
case 0x98:
return DRC_DWARFv3 | DRC_ONEOPERAND;
case 0x99:
return DRC_DWARFv3 | DRC_ONEOPERAND;
case 0x9a:
return DRC_DWARFv3 | DRC_ONEOPERAND;
+ case 0xa3: /* DW_OP_entry_value */
+ return DRC_TWOOPERANDS;
case 0xf0:
return DRC_ZEROOPERANDS; /* DW_OP_APPLE_uninit */
case 0xe0:
return 0;
case 0xff:
return 0;
default:
return 0;
}
}
const char *DW_ATE_value_to_name(uint32_t val) {
static char invalid[100];
llvm::StringRef llvmstr = llvm::dwarf::AttributeEncodingString(val);
if (llvmstr.empty()) {
snprintf(invalid, sizeof(invalid), "Unknown DW_ATE constant: 0x%x", val);
return invalid;
}
return llvmstr.data();
}
const char *DW_LANG_value_to_name(uint32_t val) {
static char invalid[100];
llvm::StringRef llvmstr = llvm::dwarf::LanguageString(val);
if (llvmstr.empty()) {
snprintf(invalid, sizeof(invalid), "Unknown DW_LANG constant: 0x%x", val);
return invalid;
}
return llvmstr.data();
}
const char *DW_LNS_value_to_name(uint32_t val) {
static char invalid[100];
llvm::StringRef llvmstr = llvm::dwarf::LNStandardString(val);
if (llvmstr.empty()) {
snprintf(invalid, sizeof(invalid), "Unknown DW_LNS constant: 0x%x", val);
return invalid;
}
return llvmstr.data();
}
} // namespace lldb_private
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index f91c123..08b9434 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -1,3820 +1,3888 @@
//===-- SymbolFileDWARF.cpp ------------------------------------*- C++ -*-===//
//
// 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 "SymbolFileDWARF.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Threading.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleList.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/Value.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/RegularExpression.h"
#include "lldb/Utility/Scalar.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/Timer.h"
#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Interpreter/OptionValueFileSpecList.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/ClangUtil.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/CompilerDecl.h"
#include "lldb/Symbol/CompilerDeclContext.h"
#include "lldb/Symbol/DebugMacros.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Symbol/LocateSymbolFile.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Symbol/TypeMap.h"
#include "lldb/Symbol/TypeSystem.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/Language.h"
#include "lldb/Target/Target.h"
#include "AppleDWARFIndex.h"
#include "DWARFASTParser.h"
#include "DWARFASTParserClang.h"
#include "DWARFCompileUnit.h"
#include "DWARFDebugAbbrev.h"
#include "DWARFDebugAranges.h"
#include "DWARFDebugInfo.h"
#include "DWARFDebugMacro.h"
#include "DWARFDebugRanges.h"
#include "DWARFDeclContext.h"
#include "DWARFFormValue.h"
#include "DWARFTypeUnit.h"
#include "DWARFUnit.h"
#include "DebugNamesDWARFIndex.h"
#include "LogChannelDWARF.h"
#include "ManualDWARFIndex.h"
#include "SymbolFileDWARFDebugMap.h"
#include "SymbolFileDWARFDwo.h"
#include "SymbolFileDWARFDwp.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Support/FileSystem.h"
#include <algorithm>
#include <map>
#include <memory>
#include <ctype.h>
#include <string.h>
//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
#ifdef ENABLE_DEBUG_PRINTF
#include <stdio.h>
#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
#else
#define DEBUG_PRINTF(fmt, ...)
#endif
using namespace lldb;
using namespace lldb_private;
// static inline bool
// child_requires_parent_class_union_or_struct_to_be_completed (dw_tag_t tag)
//{
// switch (tag)
// {
// default:
// break;
// case DW_TAG_subprogram:
// case DW_TAG_inlined_subroutine:
// case DW_TAG_class_type:
// case DW_TAG_structure_type:
// case DW_TAG_union_type:
// return true;
// }
// return false;
//}
//
namespace {
#define LLDB_PROPERTIES_symbolfiledwarf
#include "SymbolFileDWARFProperties.inc"
enum {
#define LLDB_PROPERTIES_symbolfiledwarf
#include "SymbolFileDWARFPropertiesEnum.inc"
};
class PluginProperties : public Properties {
public:
static ConstString GetSettingName() {
return SymbolFileDWARF::GetPluginNameStatic();
}
PluginProperties() {
m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
m_collection_sp->Initialize(g_symbolfiledwarf_properties);
}
FileSpecList GetSymLinkPaths() {
const OptionValueFileSpecList *option_value =
m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(
nullptr, true, ePropertySymLinkPaths);
assert(option_value);
return option_value->GetCurrentValue();
}
bool IgnoreFileIndexes() const {
return m_collection_sp->GetPropertyAtIndexAsBoolean(
nullptr, ePropertyIgnoreIndexes, false);
}
};
typedef std::shared_ptr<PluginProperties> SymbolFileDWARFPropertiesSP;
static const SymbolFileDWARFPropertiesSP &GetGlobalPluginProperties() {
static const auto g_settings_sp(std::make_shared<PluginProperties>());
return g_settings_sp;
}
} // namespace
static const llvm::DWARFDebugLine::LineTable *
ParseLLVMLineTable(lldb_private::DWARFContext &context,
llvm::DWARFDebugLine &line, dw_offset_t line_offset,
dw_offset_t unit_offset) {
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
llvm::DWARFDataExtractor data = context.getOrLoadLineData().GetAsLLVM();
llvm::DWARFContext &ctx = context.GetAsLLVM();
llvm::Expected<const llvm::DWARFDebugLine::LineTable *> line_table =
line.getOrParseLineTable(
data, line_offset, ctx, nullptr, [&](llvm::Error e) {
LLDB_LOG_ERROR(log, std::move(e),
"SymbolFileDWARF::ParseLineTable failed to parse");
});
if (!line_table) {
LLDB_LOG_ERROR(log, line_table.takeError(),
"SymbolFileDWARF::ParseLineTable failed to parse");
return nullptr;
}
return *line_table;
}
static FileSpecList ParseSupportFilesFromPrologue(
const lldb::ModuleSP &module,
const llvm::DWARFDebugLine::Prologue &prologue, FileSpec::Style style,
llvm::StringRef compile_dir = {}, FileSpec first_file = {}) {
FileSpecList support_files;
support_files.Append(first_file);
const size_t number_of_files = prologue.FileNames.size();
for (size_t idx = 1; idx <= number_of_files; ++idx) {
std::string original_file;
if (!prologue.getFileNameByIndex(
idx, compile_dir,
llvm::DILineInfoSpecifier::FileLineInfoKind::Default, original_file,
style)) {
// Always add an entry so the indexes remain correct.
support_files.EmplaceBack();
continue;
}
std::string remapped_file;
if (!prologue.getFileNameByIndex(
idx, compile_dir,
llvm::DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
remapped_file, style)) {
// Always add an entry so the indexes remain correct.
support_files.EmplaceBack(original_file, style);
continue;
}
module->RemapSourceFile(llvm::StringRef(original_file), remapped_file);
support_files.EmplaceBack(remapped_file, style);
}
return support_files;
}
FileSpecList SymbolFileDWARF::GetSymlinkPaths() {
return GetGlobalPluginProperties()->GetSymLinkPaths();
}
void SymbolFileDWARF::Initialize() {
LogChannelDWARF::Initialize();
PluginManager::RegisterPlugin(GetPluginNameStatic(),
GetPluginDescriptionStatic(), CreateInstance,
DebuggerInitialize);
}
void SymbolFileDWARF::DebuggerInitialize(Debugger &debugger) {
if (!PluginManager::GetSettingForSymbolFilePlugin(
debugger, PluginProperties::GetSettingName())) {
const bool is_global_setting = true;
PluginManager::CreateSettingForSymbolFilePlugin(
debugger, GetGlobalPluginProperties()->GetValueProperties(),
ConstString("Properties for the dwarf symbol-file plug-in."),
is_global_setting);
}
}
void SymbolFileDWARF::Terminate() {
PluginManager::UnregisterPlugin(CreateInstance);
LogChannelDWARF::Terminate();
}
lldb_private::ConstString SymbolFileDWARF::GetPluginNameStatic() {
static ConstString g_name("dwarf");
return g_name;
}
const char *SymbolFileDWARF::GetPluginDescriptionStatic() {
return "DWARF and DWARF3 debug symbol file reader.";
}
SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFileSP objfile_sp) {
return new SymbolFileDWARF(std::move(objfile_sp),
/*dwo_section_list*/ nullptr);
}
TypeList &SymbolFileDWARF::GetTypeList() {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile())
return debug_map_symfile->GetTypeList();
return SymbolFile::GetTypeList();
}
void SymbolFileDWARF::GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset,
dw_offset_t max_die_offset, uint32_t type_mask,
TypeSet &type_set) {
if (die) {
const dw_offset_t die_offset = die.GetOffset();
if (die_offset >= max_die_offset)
return;
if (die_offset >= min_die_offset) {
const dw_tag_t tag = die.Tag();
bool add_type = false;
switch (tag) {
case DW_TAG_array_type:
add_type = (type_mask & eTypeClassArray) != 0;
break;
case DW_TAG_unspecified_type:
case DW_TAG_base_type:
add_type = (type_mask & eTypeClassBuiltin) != 0;
break;
case DW_TAG_class_type:
add_type = (type_mask & eTypeClassClass) != 0;
break;
case DW_TAG_structure_type:
add_type = (type_mask & eTypeClassStruct) != 0;
break;
case DW_TAG_union_type:
add_type = (type_mask & eTypeClassUnion) != 0;
break;
case DW_TAG_enumeration_type:
add_type = (type_mask & eTypeClassEnumeration) != 0;
break;
case DW_TAG_subroutine_type:
case DW_TAG_subprogram:
case DW_TAG_inlined_subroutine:
add_type = (type_mask & eTypeClassFunction) != 0;
break;
case DW_TAG_pointer_type:
add_type = (type_mask & eTypeClassPointer) != 0;
break;
case DW_TAG_rvalue_reference_type:
case DW_TAG_reference_type:
add_type = (type_mask & eTypeClassReference) != 0;
break;
case DW_TAG_typedef:
add_type = (type_mask & eTypeClassTypedef) != 0;
break;
case DW_TAG_ptr_to_member_type:
add_type = (type_mask & eTypeClassMemberPointer) != 0;
break;
}
if (add_type) {
const bool assert_not_being_parsed = true;
Type *type = ResolveTypeUID(die, assert_not_being_parsed);
if (type) {
if (type_set.find(type) == type_set.end())
type_set.insert(type);
}
}
}
for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid();
child_die = child_die.GetSibling()) {
GetTypes(child_die, min_die_offset, max_die_offset, type_mask, type_set);
}
}
}
size_t SymbolFileDWARF::GetTypes(SymbolContextScope *sc_scope,
TypeClass type_mask, TypeList &type_list)
{
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
TypeSet type_set;
CompileUnit *comp_unit = nullptr;
DWARFUnit *dwarf_cu = nullptr;
if (sc_scope)
comp_unit = sc_scope->CalculateSymbolContextCompileUnit();
if (comp_unit) {
dwarf_cu = GetDWARFCompileUnit(comp_unit);
if (dwarf_cu == nullptr)
return 0;
GetTypes(dwarf_cu->DIE(), dwarf_cu->GetOffset(),
dwarf_cu->GetNextUnitOffset(), type_mask, type_set);
} else {
DWARFDebugInfo *info = DebugInfo();
if (info) {
const size_t num_cus = info->GetNumUnits();
for (size_t cu_idx = 0; cu_idx < num_cus; ++cu_idx) {
dwarf_cu = info->GetUnitAtIndex(cu_idx);
if (dwarf_cu) {
GetTypes(dwarf_cu->DIE(), 0, UINT32_MAX, type_mask, type_set);
}
}
}
}
std::set<CompilerType> compiler_type_set;
size_t num_types_added = 0;
for (Type *type : type_set) {
CompilerType compiler_type = type->GetForwardCompilerType();
if (compiler_type_set.find(compiler_type) == compiler_type_set.end()) {
compiler_type_set.insert(compiler_type);
type_list.Insert(type->shared_from_this());
++num_types_added;
}
}
return num_types_added;
}
// Gets the first parent that is a lexical block, function or inlined
// subroutine, or compile unit.
DWARFDIE
SymbolFileDWARF::GetParentSymbolContextDIE(const DWARFDIE &child_die) {
DWARFDIE die;
for (die = child_die.GetParent(); die; die = die.GetParent()) {
dw_tag_t tag = die.Tag();
switch (tag) {
case DW_TAG_compile_unit:
case DW_TAG_partial_unit:
case DW_TAG_subprogram:
case DW_TAG_inlined_subroutine:
case DW_TAG_lexical_block:
return die;
}
}
return DWARFDIE();
}
SymbolFileDWARF::SymbolFileDWARF(ObjectFileSP objfile_sp,
SectionList *dwo_section_list)
: SymbolFile(std::move(objfile_sp)),
UserID(0x7fffffff00000000), // Used by SymbolFileDWARFDebugMap to
// when this class parses .o files to
// contain the .o file index/ID
m_debug_map_module_wp(), m_debug_map_symfile(nullptr),
m_context(m_objfile_sp->GetModule()->GetSectionList(), dwo_section_list),
m_data_debug_loc(), m_abbr(), m_info(), m_fetched_external_modules(false),
m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate),
m_unique_ast_type_map() {}
SymbolFileDWARF::~SymbolFileDWARF() {}
static ConstString GetDWARFMachOSegmentName() {
static ConstString g_dwarf_section_name("__DWARF");
return g_dwarf_section_name;
}
UniqueDWARFASTTypeMap &SymbolFileDWARF::GetUniqueDWARFASTTypeMap() {
SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
if (debug_map_symfile)
return debug_map_symfile->GetUniqueDWARFASTTypeMap();
else
return m_unique_ast_type_map;
}
llvm::Expected<TypeSystem &>
SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) {
if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile())
return debug_map_symfile->GetTypeSystemForLanguage(language);
auto type_system_or_err =
m_objfile_sp->GetModule()->GetTypeSystemForLanguage(language);
if (type_system_or_err) {
type_system_or_err->SetSymbolFile(this);
}
return type_system_or_err;
}
void SymbolFileDWARF::InitializeObject() {
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
if (!GetGlobalPluginProperties()->IgnoreFileIndexes()) {
DWARFDataExtractor apple_names, apple_namespaces, apple_types, apple_objc;
LoadSectionData(eSectionTypeDWARFAppleNames, apple_names);
LoadSectionData(eSectionTypeDWARFAppleNamespaces, apple_namespaces);
LoadSectionData(eSectionTypeDWARFAppleTypes, apple_types);
LoadSectionData(eSectionTypeDWARFAppleObjC, apple_objc);
m_index = AppleDWARFIndex::Create(
*GetObjectFile()->GetModule(), apple_names, apple_namespaces,
apple_types, apple_objc, m_context.getOrLoadStrData());
if (m_index)
return;
DWARFDataExtractor debug_names;
LoadSectionData(eSectionTypeDWARFDebugNames, debug_names);
if (debug_names.GetByteSize() > 0) {
llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> index_or =
DebugNamesDWARFIndex::Create(
*GetObjectFile()->GetModule(), debug_names,
m_context.getOrLoadStrData(), DebugInfo());
if (index_or) {
m_index = std::move(*index_or);
return;
}
LLDB_LOG_ERROR(log, index_or.takeError(),
"Unable to read .debug_names data: {0}");
}
}
m_index = std::make_unique<ManualDWARFIndex>(*GetObjectFile()->GetModule(),
DebugInfo());
}
bool SymbolFileDWARF::SupportedVersion(uint16_t version) {
return version >= 2 && version <= 5;
}
uint32_t SymbolFileDWARF::CalculateAbilities() {
uint32_t abilities = 0;
if (m_objfile_sp != nullptr) {
const Section *section = nullptr;
const SectionList *section_list = m_objfile_sp->GetSectionList();
if (section_list == nullptr)
return 0;
uint64_t debug_abbrev_file_size = 0;
uint64_t debug_info_file_size = 0;
uint64_t debug_line_file_size = 0;
section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get();
if (section)
section_list = &section->GetChildren();
section =
section_list->FindSectionByType(eSectionTypeDWARFDebugInfo, true).get();
if (section != nullptr) {
debug_info_file_size = section->GetFileSize();
section =
section_list->FindSectionByType(eSectionTypeDWARFDebugAbbrev, true)
.get();
if (section)
debug_abbrev_file_size = section->GetFileSize();
DWARFDebugAbbrev *abbrev = DebugAbbrev();
if (abbrev) {
std::set<dw_form_t> invalid_forms;
abbrev->GetUnsupportedForms(invalid_forms);
if (!invalid_forms.empty()) {
StreamString error;
error.Printf("unsupported DW_FORM value%s:",
invalid_forms.size() > 1 ? "s" : "");
for (auto form : invalid_forms)
error.Printf(" %#x", form);
m_objfile_sp->GetModule()->ReportWarning(
"%s", error.GetString().str().c_str());
return 0;
}
}
section =
section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true)
.get();
if (section)
debug_line_file_size = section->GetFileSize();
} else {
const char *symfile_dir_cstr =
m_objfile_sp->GetFileSpec().GetDirectory().GetCString();
if (symfile_dir_cstr) {
if (strcasestr(symfile_dir_cstr, ".dsym")) {
if (m_objfile_sp->GetType() == ObjectFile::eTypeDebugInfo) {
// We have a dSYM file that didn't have a any debug info. If the
// string table has a size of 1, then it was made from an
// executable with no debug info, or from an executable that was
// stripped.
section =
section_list->FindSectionByType(eSectionTypeDWARFDebugStr, true)
.get();
if (section && section->GetFileSize() == 1) {
m_objfile_sp->GetModule()->ReportWarning(
"empty dSYM file detected, dSYM was created with an "
"executable with no debug info.");
}
}
}
}
}
if (debug_abbrev_file_size > 0 && debug_info_file_size > 0)
abilities |= CompileUnits | Functions | Blocks | GlobalVariables |
LocalVariables | VariableTypes;
if (debug_line_file_size > 0)
abilities |= LineTables;
}
return abilities;
}
const DWARFDataExtractor &
SymbolFileDWARF::GetCachedSectionData(lldb::SectionType sect_type,
DWARFDataSegment &data_segment) {
llvm::call_once(data_segment.m_flag, [this, sect_type, &data_segment] {
this->LoadSectionData(sect_type, std::ref(data_segment.m_data));
});
return data_segment.m_data;
}
void SymbolFileDWARF::LoadSectionData(lldb::SectionType sect_type,
DWARFDataExtractor &data) {
ModuleSP module_sp(m_objfile_sp->GetModule());
const SectionList *section_list = module_sp->GetSectionList();
if (!section_list)
return;
SectionSP section_sp(section_list->FindSectionByType(sect_type, true));
if (!section_sp)
return;
data.Clear();
m_objfile_sp->ReadSectionData(section_sp.get(), data);
}
const DWARFDataExtractor &SymbolFileDWARF::DebugLocData() {
const DWARFDataExtractor &debugLocData = get_debug_loc_data();
if (debugLocData.GetByteSize() > 0)
return debugLocData;
return get_debug_loclists_data();
}
const DWARFDataExtractor &SymbolFileDWARF::get_debug_loc_data() {
return GetCachedSectionData(eSectionTypeDWARFDebugLoc, m_data_debug_loc);
}
const DWARFDataExtractor &SymbolFileDWARF::get_debug_loclists_data() {
return GetCachedSectionData(eSectionTypeDWARFDebugLocLists,
m_data_debug_loclists);
}
DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() {
if (m_abbr)
return m_abbr.get();
const DWARFDataExtractor &debug_abbrev_data = m_context.getOrLoadAbbrevData();
if (debug_abbrev_data.GetByteSize() == 0)
return nullptr;
auto abbr = std::make_unique<DWARFDebugAbbrev>();
llvm::Error error = abbr->parse(debug_abbrev_data);
if (error) {
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
LLDB_LOG_ERROR(log, std::move(error),
"Unable to read .debug_abbrev section: {0}");
return nullptr;
}
m_abbr = std::move(abbr);
return m_abbr.get();
}
const DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() const {
return m_abbr.get();
}
DWARFDebugInfo *SymbolFileDWARF::DebugInfo() {
if (m_info == nullptr) {
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
static_cast<void *>(this));
if (m_context.getOrLoadDebugInfoData().GetByteSize() > 0)
m_info = std::make_unique<DWARFDebugInfo>(*this, m_context);
}
return m_info.get();
}
const DWARFDebugInfo *SymbolFileDWARF::DebugInfo() const {
return m_info.get();
}
DWARFUnit *
SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) {
if (!comp_unit)
return nullptr;
DWARFDebugInfo *info = DebugInfo();
if (info) {
// The compile unit ID is the index of the DWARF unit.
DWARFUnit *dwarf_cu = info->GetUnitAtIndex(comp_unit->GetID());
if (dwarf_cu && dwarf_cu->GetUserData() == nullptr)
dwarf_cu->SetUserData(comp_unit);
return dwarf_cu;
}
return nullptr;
}
DWARFDebugRanges *SymbolFileDWARF::GetDebugRanges() {
if (!m_ranges) {
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
static_cast<void *>(this));
if (m_context.getOrLoadRangesData().GetByteSize() > 0)
m_ranges.reset(new DWARFDebugRanges());
if (m_ranges)
m_ranges->Extract(m_context);
}
return m_ranges.get();
}
DWARFDebugRngLists *SymbolFileDWARF::GetDebugRngLists() {
if (!m_rnglists) {
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
static_cast<void *>(this));
if (m_context.getOrLoadRngListsData().GetByteSize() > 0)
m_rnglists.reset(new DWARFDebugRngLists());
if (m_rnglists)
m_rnglists->Extract(m_context);
}
return m_rnglists.get();
}
lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) {
CompUnitSP cu_sp;
CompileUnit *comp_unit = (CompileUnit *)dwarf_cu.GetUserData();
if (comp_unit) {
// We already parsed this compile unit, had out a shared pointer to it
cu_sp = comp_unit->shared_from_this();
} else {
if (&dwarf_cu.GetSymbolFileDWARF() != this) {
return dwarf_cu.GetSymbolFileDWARF().ParseCompileUnit(dwarf_cu);
} else if (dwarf_cu.GetOffset() == 0 && GetDebugMapSymfile()) {
// Let the debug map create the compile unit
cu_sp = m_debug_map_symfile->GetCompileUnit(this);
dwarf_cu.SetUserData(cu_sp.get());
} else {
ModuleSP module_sp(m_objfile_sp->GetModule());
if (module_sp) {
const DWARFDIE cu_die = dwarf_cu.DIE();
if (cu_die) {
FileSpec cu_file_spec(cu_die.GetName(), dwarf_cu.GetPathStyle());
if (cu_file_spec) {
// If we have a full path to the compile unit, we don't need to
// resolve the file. This can be expensive e.g. when the source
// files are NFS mounted.
cu_file_spec.MakeAbsolute(dwarf_cu.GetCompilationDirectory());
std::string remapped_file;
if (module_sp->RemapSourceFile(cu_file_spec.GetPath(),
remapped_file))
cu_file_spec.SetFile(remapped_file, FileSpec::Style::native);
}
LanguageType cu_language = DWARFUnit::LanguageTypeFromDWARF(
cu_die.GetAttributeValueAsUnsigned(DW_AT_language, 0));
bool is_optimized = dwarf_cu.GetIsOptimized();
BuildCuTranslationTable();
cu_sp = std::make_shared<CompileUnit>(
module_sp, &dwarf_cu, cu_file_spec,
*GetDWARFUnitIndex(dwarf_cu.GetID()), cu_language,
is_optimized ? eLazyBoolYes : eLazyBoolNo);
dwarf_cu.SetUserData(cu_sp.get());
SetCompileUnitAtIndex(dwarf_cu.GetID(), cu_sp);
}
}
}
}
return cu_sp;
}
void SymbolFileDWARF::BuildCuTranslationTable() {
if (!m_lldb_cu_to_dwarf_unit.empty())
return;
DWARFDebugInfo *info = DebugInfo();
if (!info)
return;
if (!info->ContainsTypeUnits()) {
// We can use a 1-to-1 mapping. No need to build a translation table.
return;
}
for (uint32_t i = 0, num = info->GetNumUnits(); i < num; ++i) {
if (auto *cu = llvm::dyn_cast<DWARFCompileUnit>(info->GetUnitAtIndex(i))) {
cu->SetID(m_lldb_cu_to_dwarf_unit.size());
m_lldb_cu_to_dwarf_unit.push_back(i);
}
}
}
llvm::Optional<uint32_t> SymbolFileDWARF::GetDWARFUnitIndex(uint32_t cu_idx) {
BuildCuTranslationTable();
if (m_lldb_cu_to_dwarf_unit.empty())
return cu_idx;
if (cu_idx >= m_lldb_cu_to_dwarf_unit.size())
return llvm::None;
return m_lldb_cu_to_dwarf_unit[cu_idx];
}
uint32_t SymbolFileDWARF::CalculateNumCompileUnits() {
DWARFDebugInfo *info = DebugInfo();
if (!info)
return 0;
BuildCuTranslationTable();
return m_lldb_cu_to_dwarf_unit.empty() ? info->GetNumUnits()
: m_lldb_cu_to_dwarf_unit.size();
}
CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) {
ASSERT_MODULE_LOCK(this);
DWARFDebugInfo *info = DebugInfo();
if (!info)
return {};
if (llvm::Optional<uint32_t> dwarf_idx = GetDWARFUnitIndex(cu_idx)) {
if (auto *dwarf_cu = llvm::cast_or_null<DWARFCompileUnit>(
info->GetUnitAtIndex(*dwarf_idx)))
return ParseCompileUnit(*dwarf_cu);
}
return {};
}
Function *SymbolFileDWARF::ParseFunction(CompileUnit &comp_unit,
const DWARFDIE &die) {
ASSERT_MODULE_LOCK(this);
if (!die.IsValid())
return nullptr;
auto type_system_or_err =
GetTypeSystemForLanguage(die.GetCU()->GetLanguageType());
if (auto err = type_system_or_err.takeError()) {
LLDB_LOG_ERROR(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_SYMBOLS),
std::move(err), "Unable to parse function");
return nullptr;
}
DWARFASTParser *dwarf_ast = type_system_or_err->GetDWARFParser();
if (!dwarf_ast)
return nullptr;
return dwarf_ast->ParseFunctionFromDWARF(comp_unit, die);
}
bool SymbolFileDWARF::FixupAddress(Address &addr) {
SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
if (debug_map_symfile) {
return debug_map_symfile->LinkOSOAddress(addr);
}
// This is a normal DWARF file, no address fixups need to happen
return true;
}
lldb::LanguageType SymbolFileDWARF::ParseLanguage(CompileUnit &comp_unit) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
if (dwarf_cu)
return dwarf_cu->GetLanguageType();
else
return eLanguageTypeUnknown;
}
size_t SymbolFileDWARF::ParseFunctions(CompileUnit &comp_unit) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
if (!dwarf_cu)
return 0;
size_t functions_added = 0;
std::vector<DWARFDIE> function_dies;
dwarf_cu->AppendDIEsWithTag(DW_TAG_subprogram, function_dies);
for (const DWARFDIE &die : function_dies) {
if (comp_unit.FindFunctionByUID(die.GetID()))
continue;
if (ParseFunction(comp_unit, die))
++functions_added;
}
// FixupTypes();
return functions_added;
}
bool SymbolFileDWARF::ParseSupportFiles(CompileUnit &comp_unit,
FileSpecList &support_files) {
if (!comp_unit.GetLineTable())
ParseLineTable(comp_unit);
return true;
}
FileSpec SymbolFileDWARF::GetFile(DWARFUnit &unit, size_t file_idx) {
if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit)) {
if (CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(*dwarf_cu))
return lldb_cu->GetSupportFiles().GetFileSpecAtIndex(file_idx);
return FileSpec();
}
auto &tu = llvm::cast<DWARFTypeUnit>(unit);
return GetTypeUnitSupportFiles(tu).GetFileSpecAtIndex(file_idx);
}
const FileSpecList &
SymbolFileDWARF::GetTypeUnitSupportFiles(DWARFTypeUnit &tu) {
static FileSpecList empty_list;
dw_offset_t offset = tu.GetLineTableOffset();
if (offset == DW_INVALID_OFFSET ||
offset == llvm::DenseMapInfo<dw_offset_t>::getEmptyKey() ||
offset == llvm::DenseMapInfo<dw_offset_t>::getTombstoneKey())
return empty_list;
// Many type units can share a line table, so parse the support file list
// once, and cache it based on the offset field.
auto iter_bool = m_type_unit_support_files.try_emplace(offset);
FileSpecList &list = iter_bool.first->second;
if (iter_bool.second) {
uint64_t line_table_offset = offset;
llvm::DWARFDataExtractor data = m_context.getOrLoadLineData().GetAsLLVM();
llvm::DWARFContext &ctx = m_context.GetAsLLVM();
llvm::DWARFDebugLine::Prologue prologue;
llvm::Error error = prologue.parse(data, &line_table_offset, ctx);
if (error) {
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
LLDB_LOG_ERROR(log, std::move(error),
"SymbolFileDWARF::GetTypeUnitSupportFiles failed to parse "
"the line table prologue");
} else {
list = ParseSupportFilesFromPrologue(GetObjectFile()->GetModule(),
prologue, tu.GetPathStyle());
}
}
return list;
}
bool SymbolFileDWARF::ParseIsOptimized(CompileUnit &comp_unit) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
if (dwarf_cu)
return dwarf_cu->GetIsOptimized();
return false;
}
bool SymbolFileDWARF::ParseImportedModules(
const lldb_private::SymbolContext &sc,
std::vector<SourceModule> &imported_modules) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
assert(sc.comp_unit);
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit);
if (!dwarf_cu)
return false;
if (!ClangModulesDeclVendor::LanguageSupportsClangModules(
sc.comp_unit->GetLanguage()))
return false;
UpdateExternalModuleListIfNeeded();
const DWARFDIE die = dwarf_cu->DIE();
if (!die)
return false;
for (DWARFDIE child_die = die.GetFirstChild(); child_die;
child_die = child_die.GetSibling()) {
if (child_die.Tag() != DW_TAG_imported_declaration)
continue;
DWARFDIE module_die = child_die.GetReferencedDIE(DW_AT_import);
if (module_die.Tag() != DW_TAG_module)
continue;
if (const char *name =
module_die.GetAttributeValueAsString(DW_AT_name, nullptr)) {
SourceModule module;
module.path.push_back(ConstString(name));
DWARFDIE parent_die = module_die;
while ((parent_die = parent_die.GetParent())) {
if (parent_die.Tag() != DW_TAG_module)
break;
if (const char *name =
parent_die.GetAttributeValueAsString(DW_AT_name, nullptr))
module.path.push_back(ConstString(name));
}
std::reverse(module.path.begin(), module.path.end());
if (const char *include_path = module_die.GetAttributeValueAsString(
DW_AT_LLVM_include_path, nullptr))
module.search_path = ConstString(include_path);
if (const char *sysroot = module_die.GetAttributeValueAsString(
DW_AT_LLVM_isysroot, nullptr))
module.sysroot = ConstString(sysroot);
imported_modules.push_back(module);
}
}
return true;
}
bool SymbolFileDWARF::ParseLineTable(CompileUnit &comp_unit) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
if (comp_unit.GetLineTable() != nullptr)
return true;
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
if (!dwarf_cu)
return false;
const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly();
if (!dwarf_cu_die)
return false;
const dw_offset_t cu_line_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(
DW_AT_stmt_list, DW_INVALID_OFFSET);
if (cu_line_offset == DW_INVALID_OFFSET)
return false;
llvm::DWARFDebugLine line;
const llvm::DWARFDebugLine::LineTable *line_table = ParseLLVMLineTable(
m_context, line, cu_line_offset, dwarf_cu->GetOffset());
if (!line_table)
return false;
// FIXME: Rather than parsing the whole line table and then copying it over
// into LLDB, we should explore using a callback to populate the line table
// while we parse to reduce memory usage.
std::unique_ptr<LineTable> line_table_up =
std::make_unique<LineTable>(&comp_unit);
LineSequence *sequence = line_table_up->CreateLineSequenceContainer();
for (auto &row : line_table->Rows) {
line_table_up->AppendLineEntryToSequence(
sequence, row.Address.Address, row.Line, row.Column, row.File,
row.IsStmt, row.BasicBlock, row.PrologueEnd, row.EpilogueBegin,
row.EndSequence);
if (row.EndSequence) {
line_table_up->InsertSequence(sequence);
sequence = line_table_up->CreateLineSequenceContainer();
}
}
if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile()) {
// We have an object file that has a line table with addresses that are not
// linked. We need to link the line table and convert the addresses that
// are relative to the .o file into addresses for the main executable.
comp_unit.SetLineTable(
debug_map_symfile->LinkOSOLineTable(this, line_table_up.get()));
} else {
comp_unit.SetLineTable(line_table_up.release());
}
comp_unit.SetSupportFiles(ParseSupportFilesFromPrologue(
comp_unit.GetModule(), line_table->Prologue, dwarf_cu->GetPathStyle(),
dwarf_cu->GetCompilationDirectory().GetCString(), FileSpec(comp_unit)));
return true;
}
lldb_private::DebugMacrosSP
SymbolFileDWARF::ParseDebugMacros(lldb::offset_t *offset) {
auto iter = m_debug_macros_map.find(*offset);
if (iter != m_debug_macros_map.end())
return iter->second;
const DWARFDataExtractor &debug_macro_data = m_context.getOrLoadMacroData();
if (debug_macro_data.GetByteSize() == 0)
return DebugMacrosSP();
lldb_private::DebugMacrosSP debug_macros_sp(new lldb_private::DebugMacros());
m_debug_macros_map[*offset] = debug_macros_sp;
const DWARFDebugMacroHeader &header =
DWARFDebugMacroHeader::ParseHeader(debug_macro_data, offset);
DWARFDebugMacroEntry::ReadMacroEntries(
debug_macro_data, m_context.getOrLoadStrData(), header.OffsetIs64Bit(),
offset, this, debug_macros_sp);
return debug_macros_sp;
}
bool SymbolFileDWARF::ParseDebugMacros(CompileUnit &comp_unit) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
if (dwarf_cu == nullptr)
return false;
const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly();
if (!dwarf_cu_die)
return false;
lldb::offset_t sect_offset =
dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_macros, DW_INVALID_OFFSET);
if (sect_offset == DW_INVALID_OFFSET)
sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_macros,
DW_INVALID_OFFSET);
if (sect_offset == DW_INVALID_OFFSET)
return false;
comp_unit.SetDebugMacros(ParseDebugMacros(&sect_offset));
return true;
}
size_t SymbolFileDWARF::ParseBlocksRecursive(
lldb_private::CompileUnit &comp_unit, Block *parent_block,
const DWARFDIE &orig_die, addr_t subprogram_low_pc, uint32_t depth) {
size_t blocks_added = 0;
DWARFDIE die = orig_die;
while (die) {
dw_tag_t tag = die.Tag();
switch (tag) {
case DW_TAG_inlined_subroutine:
case DW_TAG_subprogram:
case DW_TAG_lexical_block: {
Block *block = nullptr;
if (tag == DW_TAG_subprogram) {
// Skip any DW_TAG_subprogram DIEs that are inside of a normal or
// inlined functions. These will be parsed on their own as separate
// entities.
if (depth > 0)
break;
block = parent_block;
} else {
BlockSP block_sp(new Block(die.GetID()));
parent_block->AddChild(block_sp);
block = block_sp.get();
}
DWARFRangeList ranges;
const char *name = nullptr;
const char *mangled_name = nullptr;
int decl_file = 0;
int decl_line = 0;
int decl_column = 0;
int call_file = 0;
int call_line = 0;
int call_column = 0;
if (die.GetDIENamesAndRanges(name, mangled_name, ranges, decl_file,
decl_line, decl_column, call_file, call_line,
call_column, nullptr)) {
if (tag == DW_TAG_subprogram) {
assert(subprogram_low_pc == LLDB_INVALID_ADDRESS);
subprogram_low_pc = ranges.GetMinRangeBase(0);
} else if (tag == DW_TAG_inlined_subroutine) {
// We get called here for inlined subroutines in two ways. The first
// time is when we are making the Function object for this inlined
// concrete instance. Since we're creating a top level block at
// here, the subprogram_low_pc will be LLDB_INVALID_ADDRESS. So we
// need to adjust the containing address. The second time is when we
// are parsing the blocks inside the function that contains the
// inlined concrete instance. Since these will be blocks inside the
// containing "real" function the offset will be for that function.
if (subprogram_low_pc == LLDB_INVALID_ADDRESS) {
subprogram_low_pc = ranges.GetMinRangeBase(0);
}
}
const size_t num_ranges = ranges.GetSize();
for (size_t i = 0; i < num_ranges; ++i) {
const DWARFRangeList::Entry &range = ranges.GetEntryRef(i);
const addr_t range_base = range.GetRangeBase();
if (range_base >= subprogram_low_pc)
block->AddRange(Block::Range(range_base - subprogram_low_pc,
range.GetByteSize()));
else {
GetObjectFile()->GetModule()->ReportError(
"0x%8.8" PRIx64 ": adding range [0x%" PRIx64 "-0x%" PRIx64
") which has a base that is less than the function's low PC "
"0x%" PRIx64 ". Please file a bug and attach the file at the "
"start of this error message",
block->GetID(), range_base, range.GetRangeEnd(),
subprogram_low_pc);
}
}
block->FinalizeRanges();
if (tag != DW_TAG_subprogram &&
(name != nullptr || mangled_name != nullptr)) {
std::unique_ptr<Declaration> decl_up;
if (decl_file != 0 || decl_line != 0 || decl_column != 0)
decl_up.reset(new Declaration(
comp_unit.GetSupportFiles().GetFileSpecAtIndex(decl_file),
decl_line, decl_column));
std::unique_ptr<Declaration> call_up;
if (call_file != 0 || call_line != 0 || call_column != 0)
call_up.reset(new Declaration(
comp_unit.GetSupportFiles().GetFileSpecAtIndex(call_file),
call_line, call_column));
block->SetInlinedFunctionInfo(name, mangled_name, decl_up.get(),
call_up.get());
}
++blocks_added;
if (die.HasChildren()) {
blocks_added +=
ParseBlocksRecursive(comp_unit, block, die.GetFirstChild(),
subprogram_low_pc, depth + 1);
}
}
} break;
default:
break;
}
// Only parse siblings of the block if we are not at depth zero. A depth of
// zero indicates we are currently parsing the top level DW_TAG_subprogram
// DIE
if (depth == 0)
die.Clear();
else
die = die.GetSibling();
}
return blocks_added;
}
bool SymbolFileDWARF::ClassOrStructIsVirtual(const DWARFDIE &parent_die) {
if (parent_die) {
for (DWARFDIE die = parent_die.GetFirstChild(); die;
die = die.GetSibling()) {
dw_tag_t tag = die.Tag();
bool check_virtuality = false;
switch (tag) {
case DW_TAG_inheritance:
case DW_TAG_subprogram:
check_virtuality = true;
break;
default:
break;
}
if (check_virtuality) {
if (die.GetAttributeValueAsUnsigned(DW_AT_virtuality, 0) != 0)
return true;
}
}
}
return false;
}
void SymbolFileDWARF::ParseDeclsForContext(CompilerDeclContext decl_ctx) {
auto *type_system = decl_ctx.GetTypeSystem();
if (!type_system)
return;
DWARFASTParser *ast_parser = type_system->GetDWARFParser();
std::vector<DWARFDIE> decl_ctx_die_list =
ast_parser->GetDIEForDeclContext(decl_ctx);
for (DWARFDIE decl_ctx_die : decl_ctx_die_list)
for (DWARFDIE decl = decl_ctx_die.GetFirstChild(); decl;
decl = decl.GetSibling())
ast_parser->GetDeclForUIDFromDWARF(decl);
}
user_id_t SymbolFileDWARF::GetUID(DIERef ref) {
if (GetDebugMapSymfile())
return GetID() | ref.die_offset();
return user_id_t(GetDwoNum().getValueOr(0x7fffffff)) << 32 |
ref.die_offset() |
(lldb::user_id_t(ref.section() == DIERef::Section::DebugTypes) << 63);
}
llvm::Optional<SymbolFileDWARF::DecodedUID>
SymbolFileDWARF::DecodeUID(lldb::user_id_t uid) {
// This method can be called without going through the symbol vendor so we
// need to lock the module.
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
// Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API we
// must make sure we use the correct DWARF file when resolving things. On
// MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple
// SymbolFileDWARF classes, one for each .o file. We can often end up with
// references to other DWARF objects and we must be ready to receive a
// "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF
// instance.
if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) {
SymbolFileDWARF *dwarf = debug_map->GetSymbolFileByOSOIndex(
debug_map->GetOSOIndexFromUserID(uid));
return DecodedUID{
*dwarf, {llvm::None, DIERef::Section::DebugInfo, dw_offset_t(uid)}};
}
dw_offset_t die_offset = uid;
if (die_offset == DW_INVALID_OFFSET)
return llvm::None;
DIERef::Section section =
uid >> 63 ? DIERef::Section::DebugTypes : DIERef::Section::DebugInfo;
llvm::Optional<uint32_t> dwo_num = uid >> 32 & 0x7fffffff;
if (*dwo_num == 0x7fffffff)
dwo_num = llvm::None;
return DecodedUID{*this, {dwo_num, section, die_offset}};
}
DWARFDIE
SymbolFileDWARF::GetDIE(lldb::user_id_t uid) {
// This method can be called without going through the symbol vendor so we
// need to lock the module.
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
llvm::Optional<DecodedUID> decoded = DecodeUID(uid);
if (decoded)
return decoded->dwarf.GetDIE(decoded->ref);
return DWARFDIE();
}
CompilerDecl SymbolFileDWARF::GetDeclForUID(lldb::user_id_t type_uid) {
// This method can be called without going through the symbol vendor so we
// need to lock the module.
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
// Anytime we have a lldb::user_id_t, we must get the DIE by calling
// SymbolFileDWARF::GetDIE(). See comments inside the
// SymbolFileDWARF::GetDIE() for details.
if (DWARFDIE die = GetDIE(type_uid))
return die.GetDecl();
return CompilerDecl();
}
CompilerDeclContext
SymbolFileDWARF::GetDeclContextForUID(lldb::user_id_t type_uid) {
// This method can be called without going through the symbol vendor so we
// need to lock the module.
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
// Anytime we have a lldb::user_id_t, we must get the DIE by calling
// SymbolFileDWARF::GetDIE(). See comments inside the
// SymbolFileDWARF::GetDIE() for details.
if (DWARFDIE die = GetDIE(type_uid))
return die.GetDeclContext();
return CompilerDeclContext();
}
CompilerDeclContext
SymbolFileDWARF::GetDeclContextContainingUID(lldb::user_id_t type_uid) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
// Anytime we have a lldb::user_id_t, we must get the DIE by calling
// SymbolFileDWARF::GetDIE(). See comments inside the
// SymbolFileDWARF::GetDIE() for details.
if (DWARFDIE die = GetDIE(type_uid))
return die.GetContainingDeclContext();
return CompilerDeclContext();
}
Type *SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
// Anytime we have a lldb::user_id_t, we must get the DIE by calling
// SymbolFileDWARF::GetDIE(). See comments inside the
// SymbolFileDWARF::GetDIE() for details.
if (DWARFDIE type_die = GetDIE(type_uid))
return type_die.ResolveType();
else
return nullptr;
}
llvm::Optional<SymbolFile::ArrayInfo>
SymbolFileDWARF::GetDynamicArrayInfoForUID(
lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
if (DWARFDIE type_die = GetDIE(type_uid))
return DWARFASTParser::ParseChildArrayInfo(type_die, exe_ctx);
else
return llvm::None;
}
Type *SymbolFileDWARF::ResolveTypeUID(const DIERef &die_ref) {
return ResolveType(GetDIE(die_ref), true);
}
Type *SymbolFileDWARF::ResolveTypeUID(const DWARFDIE &die,
bool assert_not_being_parsed) {
if (die) {
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO));
if (log)
GetObjectFile()->GetModule()->LogMessage(
log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s'",
die.GetOffset(), die.GetTagAsCString(), die.GetName());
// We might be coming in in the middle of a type tree (a class within a
// class, an enum within a class), so parse any needed parent DIEs before
// we get to this one...
DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(die);
if (decl_ctx_die) {
if (log) {
switch (decl_ctx_die.Tag()) {
case DW_TAG_structure_type:
case DW_TAG_union_type:
case DW_TAG_class_type: {
// Get the type, which could be a forward declaration
if (log)
GetObjectFile()->GetModule()->LogMessage(
log,
"SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' "
"resolve parent forward type for 0x%8.8x",
die.GetOffset(), die.GetTagAsCString(), die.GetName(),
decl_ctx_die.GetOffset());
} break;
default:
break;
}
}
}
return ResolveType(die);
}
return nullptr;
}
// This function is used when SymbolFileDWARFDebugMap owns a bunch of
// SymbolFileDWARF objects to detect if this DWARF file is the one that can
// resolve a compiler_type.
bool SymbolFileDWARF::HasForwardDeclForClangType(
const CompilerType &compiler_type) {
CompilerType compiler_type_no_qualifiers =
ClangUtil::RemoveFastQualifiers(compiler_type);
if (GetForwardDeclClangTypeToDie().count(
compiler_type_no_qualifiers.GetOpaqueQualType())) {
return true;
}
TypeSystem *type_system = compiler_type.GetTypeSystem();
ClangASTContext *clang_type_system =
llvm::dyn_cast_or_null<ClangASTContext>(type_system);
if (!clang_type_system)
return false;
DWARFASTParserClang *ast_parser =
static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser());
return ast_parser->GetClangASTImporter().CanImport(compiler_type);
}
bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
ClangASTContext *clang_type_system =
llvm::dyn_cast_or_null<ClangASTContext>(compiler_type.GetTypeSystem());
if (clang_type_system) {
DWARFASTParserClang *ast_parser =
static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser());
if (ast_parser &&
ast_parser->GetClangASTImporter().CanImport(compiler_type))
return ast_parser->GetClangASTImporter().CompleteType(compiler_type);