mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-05-15 02:16:40 +00:00

used by the JIT compiled expression, including the result of the expression. Also added a new class, ASTType, which encapsulates an opaque Clang type and its associated AST context. Refactored ClangExpressionDeclMap to use ASTTypes, significantly reducing the possibility of mixups of types from different AST contexts. llvm-svn: 108965
533 lines
17 KiB
C++
533 lines
17 KiB
C++
//===-- CommandObjectExpression.cpp -----------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CommandObjectExpression.h"
|
|
|
|
// C Includes
|
|
// C++ Includes
|
|
// Other libraries and framework includes
|
|
// Project includes
|
|
#include "lldb/Interpreter/Args.h"
|
|
#include "lldb/Core/Value.h"
|
|
#include "lldb/Core/InputReader.h"
|
|
#include "lldb/Expression/ClangExpression.h"
|
|
#include "lldb/Expression/ClangExpressionDeclMap.h"
|
|
#include "lldb/Expression/ClangExpressionVariable.h"
|
|
#include "lldb/Expression/DWARFExpression.h"
|
|
#include "lldb/Host/Host.h"
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
|
#include "lldb/Interpreter/CommandReturnObject.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Symbol/Variable.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/StackFrame.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
CommandObjectExpression::CommandOptions::CommandOptions () :
|
|
Options()
|
|
{
|
|
// Keep only one place to reset the values to their defaults
|
|
ResetOptionValues();
|
|
}
|
|
|
|
|
|
CommandObjectExpression::CommandOptions::~CommandOptions ()
|
|
{
|
|
}
|
|
|
|
Error
|
|
CommandObjectExpression::CommandOptions::SetOptionValue (int option_idx, const char *option_arg)
|
|
{
|
|
Error error;
|
|
|
|
char short_option = (char) m_getopt_table[option_idx].val;
|
|
|
|
switch (short_option)
|
|
{
|
|
case 'l':
|
|
if (language.SetLanguageFromCString (option_arg) == false)
|
|
{
|
|
error.SetErrorStringWithFormat("Invalid language option argument '%s'.\n", option_arg);
|
|
}
|
|
break;
|
|
|
|
case 'g':
|
|
debug = true;
|
|
break;
|
|
|
|
case 'f':
|
|
error = Args::StringToFormat(option_arg, format);
|
|
break;
|
|
|
|
case 'i':
|
|
use_ir = true;
|
|
break;
|
|
|
|
default:
|
|
error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void
|
|
CommandObjectExpression::CommandOptions::ResetOptionValues ()
|
|
{
|
|
Options::ResetOptionValues();
|
|
language.Clear();
|
|
debug = false;
|
|
format = eFormatDefault;
|
|
show_types = true;
|
|
show_summary = true;
|
|
use_ir = false;
|
|
}
|
|
|
|
const lldb::OptionDefinition*
|
|
CommandObjectExpression::CommandOptions::GetDefinitions ()
|
|
{
|
|
return g_option_table;
|
|
}
|
|
|
|
CommandObjectExpression::CommandObjectExpression () :
|
|
CommandObject (
|
|
"expression",
|
|
"Evaluate a C expression in the current program context, using variables currently in scope.",
|
|
"expression [<cmd-options>] <expr>"),
|
|
m_expr_line_count (0),
|
|
m_expr_lines ()
|
|
{
|
|
SetHelpLong(
|
|
"Examples: \n\
|
|
\n\
|
|
expr my_struct->a = my_array[3] \n\
|
|
expr -f bin -- (index * 8) + 5 \n\
|
|
expr char c[] = \"foo\"; c[0]\n");
|
|
}
|
|
|
|
CommandObjectExpression::~CommandObjectExpression ()
|
|
{
|
|
}
|
|
|
|
Options *
|
|
CommandObjectExpression::GetOptions ()
|
|
{
|
|
return &m_options;
|
|
}
|
|
|
|
|
|
bool
|
|
CommandObjectExpression::Execute
|
|
(
|
|
CommandInterpreter &interpreter,
|
|
Args& command,
|
|
CommandReturnObject &result
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
size_t
|
|
CommandObjectExpression::MultiLineExpressionCallback
|
|
(
|
|
void *baton,
|
|
InputReader &reader,
|
|
lldb::InputReaderAction notification,
|
|
const char *bytes,
|
|
size_t bytes_len
|
|
)
|
|
{
|
|
CommandObjectExpression *cmd_object_expr = (CommandObjectExpression *) baton;
|
|
|
|
switch (notification)
|
|
{
|
|
case eInputReaderActivate:
|
|
reader.GetDebugger().GetOutputStream().Printf("%s\n", "Enter expressions, then terminate with an empty line to evaluate:");
|
|
// Fall through
|
|
case eInputReaderReactivate:
|
|
//if (out_fh)
|
|
// reader.GetDebugger().GetOutputStream().Printf ("%3u: ", cmd_object_expr->m_expr_line_count);
|
|
break;
|
|
|
|
case eInputReaderDeactivate:
|
|
break;
|
|
|
|
case eInputReaderGotToken:
|
|
++cmd_object_expr->m_expr_line_count;
|
|
if (bytes && bytes_len)
|
|
{
|
|
cmd_object_expr->m_expr_lines.append (bytes, bytes_len + 1);
|
|
}
|
|
|
|
if (bytes_len == 0)
|
|
reader.SetIsDone(true);
|
|
//else if (out_fh && !reader->IsDone())
|
|
// ::fprintf (out_fh, "%3u: ", cmd_object_expr->m_expr_line_count);
|
|
break;
|
|
|
|
case eInputReaderDone:
|
|
{
|
|
bool bare = false;
|
|
cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(),
|
|
bare,
|
|
reader.GetDebugger().GetOutputStream(),
|
|
reader.GetDebugger().GetErrorStream());
|
|
}
|
|
break;
|
|
}
|
|
|
|
return bytes_len;
|
|
}
|
|
|
|
bool
|
|
CommandObjectExpression::EvaluateExpression (const char *expr, bool bare, Stream &output_stream, Stream &error_stream)
|
|
{
|
|
Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS);
|
|
|
|
////////////////////////////////////
|
|
// Set up the target and compiler
|
|
//
|
|
|
|
Target *target = m_exe_ctx.target;
|
|
|
|
if (!target)
|
|
{
|
|
error_stream.PutCString ("error: invalid target\n");
|
|
return false;
|
|
}
|
|
|
|
ConstString target_triple;
|
|
|
|
target->GetTargetTriple (target_triple);
|
|
|
|
if (!target_triple)
|
|
target_triple = Host::GetTargetTriple ();
|
|
|
|
if (!target_triple)
|
|
{
|
|
error_stream.PutCString ("error: invalid target triple\n");
|
|
return false;
|
|
}
|
|
|
|
ClangExpressionDeclMap expr_decl_map (&m_exe_ctx);
|
|
ClangExpression clang_expr (target_triple.AsCString (), &expr_decl_map);
|
|
|
|
//////////////////////////
|
|
// Parse the expression
|
|
//
|
|
|
|
unsigned num_errors;
|
|
|
|
if (bare)
|
|
num_errors = clang_expr.ParseBareExpression (llvm::StringRef (expr), error_stream);
|
|
else
|
|
num_errors = clang_expr.ParseExpression (expr, error_stream, m_options.use_ir);
|
|
|
|
if (num_errors)
|
|
{
|
|
error_stream.Printf ("error: %d errors parsing expression\n", num_errors);
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Convert the output of the parser to DWARF
|
|
//
|
|
|
|
StreamString dwarf_opcodes;
|
|
dwarf_opcodes.SetByteOrder (eByteOrderHost);
|
|
dwarf_opcodes.GetFlags ().Set (Stream::eBinary);
|
|
|
|
ClangExpressionVariableList expr_local_vars;
|
|
|
|
bool success;
|
|
bool canInterpret = false;
|
|
|
|
if (m_options.use_ir)
|
|
{
|
|
canInterpret = clang_expr.ConvertIRToDWARF (expr_local_vars, dwarf_opcodes);
|
|
|
|
if (canInterpret)
|
|
{
|
|
if (log)
|
|
log->Printf("Code can be interpreted.");
|
|
success = true;
|
|
}
|
|
else
|
|
{
|
|
if (log)
|
|
log->Printf("Code cannot be interpreted and must be run in the target.");
|
|
success = clang_expr.PrepareIRForTarget (expr_local_vars);
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
error_stream.PutCString ("error: expression couldn't be converted to IR\n");
|
|
return false;
|
|
}
|
|
|
|
if (canInterpret)
|
|
{
|
|
// TODO interpret IR
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (!clang_expr.JITFunction (m_exe_ctx, "___clang_expr"))
|
|
{
|
|
error_stream.PutCString ("error: IR could not be JIT compiled\n");
|
|
return false;
|
|
}
|
|
|
|
if (!clang_expr.WriteJITCode (m_exe_ctx))
|
|
{
|
|
error_stream.PutCString ("error: JIT code could not be written to the target\n");
|
|
return false;
|
|
}
|
|
|
|
lldb::addr_t function_address(clang_expr.GetFunctionAddress ("___clang_expr"));
|
|
|
|
if (function_address == LLDB_INVALID_ADDRESS)
|
|
{
|
|
error_stream.PutCString ("JIT compiled code's address couldn't be found\n");
|
|
return false;
|
|
}
|
|
|
|
Error err;
|
|
lldb::addr_t struct_address;
|
|
|
|
if (!expr_decl_map.Materialize(&m_exe_ctx, struct_address, err))
|
|
{
|
|
error_stream.Printf ("Couldn't materialize struct: %s\n", err.AsCString("unknown error"));
|
|
return false;
|
|
}
|
|
|
|
log->Printf("Function address : 0x%llx", (uint64_t)function_address);
|
|
log->Printf("Structure address : 0x%llx", (uint64_t)struct_address);
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
else
|
|
{
|
|
success = (clang_expr.ConvertExpressionToDWARF (expr_local_vars, dwarf_opcodes) == 0);
|
|
|
|
if (!success)
|
|
{
|
|
error_stream.PutCString ("error: expression couldn't be translated to DWARF\n");
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////
|
|
// Evaluate the generated DWARF opcodes
|
|
//
|
|
|
|
DataExtractor dwarf_opcodes_data (dwarf_opcodes.GetData (), dwarf_opcodes.GetSize (), eByteOrderHost, 8);
|
|
DWARFExpression dwarf_expr (dwarf_opcodes_data, 0, dwarf_opcodes_data.GetByteSize (), NULL);
|
|
|
|
dwarf_expr.SetExpressionLocalVariableList(&expr_local_vars);
|
|
|
|
if (log)
|
|
{
|
|
StreamString stream_string;
|
|
|
|
log->PutCString ("Expression parsed ok, dwarf opcodes:");
|
|
|
|
stream_string.PutCString ("\n");
|
|
stream_string.IndentMore ();
|
|
dwarf_expr.GetDescription (&stream_string, lldb::eDescriptionLevelVerbose);
|
|
stream_string.IndentLess ();
|
|
stream_string.EOL ();
|
|
|
|
log->PutCString (stream_string.GetString ().c_str ());
|
|
}
|
|
|
|
clang::ASTContext *ast_context = clang_expr.GetASTContext ();
|
|
Value expr_result;
|
|
Error expr_error;
|
|
success = dwarf_expr.Evaluate (&m_exe_ctx, ast_context, NULL, expr_result, &expr_error);
|
|
|
|
if (!success)
|
|
{
|
|
error_stream.Printf ("error: couldn't evaluate DWARF expression: %s\n", expr_error.AsCString ());
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
// Interpret the result and print it
|
|
//
|
|
|
|
lldb::Format format = m_options.format;
|
|
|
|
// Resolve any values that are possible
|
|
expr_result.ResolveValue (&m_exe_ctx, ast_context);
|
|
|
|
if (expr_result.GetContextType () == Value::eContextTypeInvalid &&
|
|
expr_result.GetValueType () == Value::eValueTypeScalar &&
|
|
format == eFormatDefault)
|
|
{
|
|
// The expression result is just a scalar with no special formatting
|
|
expr_result.GetScalar ().GetValue (&output_stream, m_options.show_types);
|
|
output_stream.EOL ();
|
|
return true;
|
|
}
|
|
|
|
// The expression result is more complext and requires special handling
|
|
DataExtractor data;
|
|
expr_error = expr_result.GetValueAsData (&m_exe_ctx, ast_context, data, 0);
|
|
|
|
if (!expr_error.Success ())
|
|
{
|
|
error_stream.Printf ("error: couldn't resolve result value: %s\n", expr_error.AsCString ());
|
|
return false;
|
|
}
|
|
|
|
if (format == eFormatDefault)
|
|
format = expr_result.GetValueDefaultFormat ();
|
|
|
|
void *clang_type = expr_result.GetValueOpaqueClangQualType ();
|
|
|
|
if (clang_type)
|
|
{
|
|
if (m_options.show_types)
|
|
Type::DumpClangTypeName (&output_stream, clang_type);
|
|
|
|
Type::DumpValue (&m_exe_ctx, // The execution context for memory and variable access
|
|
ast_context, // The ASTContext that the clang type belongs to
|
|
clang_type, // The opaque clang type we want to dump that value of
|
|
&output_stream, // Stream to dump to
|
|
format, // Format to use when dumping
|
|
data, // A buffer containing the bytes for the clang type
|
|
0, // Byte offset within "data" where value is
|
|
data.GetByteSize (), // Size in bytes of the value we are dumping
|
|
0, // Bitfield bit size
|
|
0, // Bitfield bit offset
|
|
m_options.show_types, // Show types?
|
|
m_options.show_summary, // Show summary?
|
|
m_options.debug, // Debug logging output?
|
|
UINT32_MAX); // Depth to dump in case this is an aggregate type
|
|
}
|
|
else
|
|
{
|
|
data.Dump (&output_stream, // Stream to dump to
|
|
0, // Byte offset within "data"
|
|
format, // Format to use when dumping
|
|
data.GetByteSize (), // Size in bytes of each item we are dumping
|
|
1, // Number of items to dump
|
|
UINT32_MAX, // Number of items per line
|
|
LLDB_INVALID_ADDRESS, // Invalid address, don't show any offset/address context
|
|
0, // Bitfield bit size
|
|
0); // Bitfield bit offset
|
|
}
|
|
output_stream.EOL();
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
CommandObjectExpression::ExecuteRawCommandString
|
|
(
|
|
CommandInterpreter &interpreter,
|
|
const char *command,
|
|
CommandReturnObject &result
|
|
)
|
|
{
|
|
m_exe_ctx = interpreter.GetDebugger().GetExecutionContext();
|
|
|
|
m_options.ResetOptionValues();
|
|
|
|
const char * expr = NULL;
|
|
|
|
if (command[0] == '\0')
|
|
{
|
|
m_expr_lines.clear();
|
|
m_expr_line_count = 0;
|
|
|
|
InputReaderSP reader_sp (new InputReader(interpreter.GetDebugger()));
|
|
if (reader_sp)
|
|
{
|
|
Error err (reader_sp->Initialize (CommandObjectExpression::MultiLineExpressionCallback,
|
|
this, // baton
|
|
eInputReaderGranularityLine, // token size, to pass to callback function
|
|
NULL, // end token
|
|
NULL, // prompt
|
|
true)); // echo input
|
|
if (err.Success())
|
|
{
|
|
interpreter.GetDebugger().PushInputReader (reader_sp);
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
}
|
|
else
|
|
{
|
|
result.AppendError (err.AsCString());
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.AppendError("out of memory");
|
|
result.SetStatus (eReturnStatusFailed);
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
if (command[0] == '-')
|
|
{
|
|
// We have some options and these options MUST end with --.
|
|
const char *end_options = NULL;
|
|
const char *s = command;
|
|
while (s && s[0])
|
|
{
|
|
end_options = ::strstr (s, "--");
|
|
if (end_options)
|
|
{
|
|
end_options += 2; // Get past the "--"
|
|
if (::isspace (end_options[0]))
|
|
{
|
|
expr = end_options;
|
|
while (::isspace (*expr))
|
|
++expr;
|
|
break;
|
|
}
|
|
}
|
|
s = end_options;
|
|
}
|
|
|
|
if (end_options)
|
|
{
|
|
Args args (command, end_options - command);
|
|
if (!ParseOptions (interpreter, args, result))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (expr == NULL)
|
|
expr = command;
|
|
|
|
return EvaluateExpression (expr, false, result.GetOutputStream(), result.GetErrorStream());
|
|
}
|
|
|
|
lldb::OptionDefinition
|
|
CommandObjectExpression::CommandOptions::g_option_table[] =
|
|
{
|
|
{ LLDB_OPT_SET_ALL, false, "language", 'l', required_argument, NULL, 0, "[c|c++|objc|objc++]", "Sets the language to use when parsing the expression."},
|
|
{ LLDB_OPT_SET_ALL, false, "format", 'f', required_argument, NULL, 0, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]", "Specify the format that the expression output should use."},
|
|
{ LLDB_OPT_SET_ALL, false, "debug", 'g', no_argument, NULL, 0, NULL, "Enable verbose debug logging of the expression parsing and evaluation."},
|
|
{ LLDB_OPT_SET_ALL, false, "use-ir", 'i', no_argument, NULL, 0, NULL, "[Temporary] Instructs the expression evaluator to use IR instead of ASTs."},
|
|
{ 0, false, NULL, 0, 0, NULL, NULL, NULL, NULL }
|
|
};
|
|
|