From 534684372dc9a71c253f9afabc04cb52591b039e Mon Sep 17 00:00:00 2001 From: Enrico Granata Date: Wed, 13 Nov 2013 02:18:44 +0000 Subject: [PATCH] Implement a "memory find" command for LLDB This is still fairly rough around the edges but works well enough for simple scenarios where a chunk of text or a number are to be found within a certain range of memory, as in mem find `buffer` `buffer+0x1000` -s "me" -c 5 -r llvm-svn: 194544 --- lldb/source/Commands/CommandObjectMemory.cpp | 313 ++++++++++++++++++- 1 file changed, 308 insertions(+), 5 deletions(-) diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index 2ee275e11f66..77186a2be2d2 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -914,6 +914,309 @@ protected: ClangASTType m_prev_clang_ast_type; }; +OptionDefinition +g_memory_find_option_table[] = +{ + { LLDB_OPT_SET_1, false, "expr", 'e', OptionParser::eRequiredArgument, NULL, 0, eArgTypeExpression, "Evaluate an expression to obtain a byte pattern."}, + { LLDB_OPT_SET_1, false, "string", 's', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Use text to find a byte pattern."}, + { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, NULL, 0, eArgTypeCount, "How many times to perform the search."}, + { LLDB_OPT_SET_1, false, "do-read", 'r', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Should we do a memory read at each match."}, +}; + + +//---------------------------------------------------------------------- +// Find the specified data in memory +//---------------------------------------------------------------------- +class CommandObjectMemoryFind : public CommandObjectParsed +{ +public: + + class OptionGroupFindMemory : public OptionGroup + { + public: + OptionGroupFindMemory () : + OptionGroup(), + m_count(1), + m_do_read(false) + { + } + + virtual + ~OptionGroupFindMemory () + { + } + + virtual uint32_t + GetNumDefinitions () + { + return sizeof (g_memory_find_option_table) / sizeof (OptionDefinition); + } + + virtual const OptionDefinition* + GetDefinitions () + { + return g_memory_find_option_table; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) + { + Error error; + const int short_option = g_memory_find_option_table[option_idx].short_option; + + switch (short_option) + { + case 'e': + m_expr.SetValueFromCString(option_arg); + break; + + case 's': + m_string.SetValueFromCString(option_arg); + break; + + case 'c': + if (m_count.SetValueFromCString(option_arg).Fail()) + error.SetErrorString("unrecognized value for count"); + break; + + + case 'r': + m_do_read.SetValueFromCString("true"); + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + return error; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + m_expr.Clear(); + m_string.Clear(); + m_count.Clear(); + m_do_read.Clear(); + } + + OptionValueString m_expr; + OptionValueString m_string; + OptionValueUInt64 m_count; + OptionValueBoolean m_do_read; + }; + + CommandObjectMemoryFind (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "memory find", + "Find a value in the memory of the process being debugged.", + NULL, + eFlagRequiresProcess | eFlagProcessMustBeLaunched), + m_option_group (interpreter), + m_memory_options () + { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData addr_arg; + CommandArgumentData value_arg; + + // Define the first (and only) variant of this arg. + addr_arg.arg_type = eArgTypeAddress; + addr_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (addr_arg); + + // Define the first (and only) variant of this arg. + value_arg.arg_type = eArgTypeValue; + value_arg.arg_repetition = eArgRepeatPlus; + + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (value_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + + m_option_group.Append (&m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectMemoryFind () + { + } + + Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + // No need to check "process" for validity as eFlagRequiresProcess ensures it is valid + Process *process = m_exe_ctx.GetProcessPtr(); + + const size_t argc = command.GetArgumentCount(); + + if (argc != 2) + { + result.AppendError("Two addressed needed for memory find"); + return false; + } + + Error error; + lldb::addr_t low_addr = Args::StringToAddress(&m_exe_ctx, command.GetArgumentAtIndex(0),LLDB_INVALID_ADDRESS,&error); + if (low_addr == LLDB_INVALID_ADDRESS || error.Fail()) + { + result.AppendError("invalid low address"); + return false; + } + lldb::addr_t high_addr = Args::StringToAddress(&m_exe_ctx, command.GetArgumentAtIndex(1),LLDB_INVALID_ADDRESS,&error); + if (high_addr == LLDB_INVALID_ADDRESS || error.Fail()) + { + result.AppendError("invalid low address"); + return false; + } + + if (high_addr <= low_addr) + { + result.AppendError("starting address must be smaller than ending address"); + return false; + } + + lldb::addr_t found_location = LLDB_INVALID_ADDRESS; + + DataBufferHeap buffer; + + if (m_memory_options.m_string.OptionWasSet()) + buffer.CopyData(m_memory_options.m_string.GetStringValue(), strlen(m_memory_options.m_string.GetStringValue())); + else if (m_memory_options.m_expr.OptionWasSet()) + { + StackFrame* frame = m_exe_ctx.GetFramePtr(); + ValueObjectSP result_sp; + if (process->GetTarget().EvaluateExpression(m_memory_options.m_expr.GetStringValue(), frame, result_sp) && result_sp.get()) + { + uint64_t value = result_sp->GetValueAsUnsigned(0); + switch (result_sp->GetClangType().GetByteSize()) + { + case 1: { + uint8_t byte = (uint8_t)value; + buffer.CopyData(&byte,1); + } + break; + case 2: { + uint16_t word = (uint16_t)value; + buffer.CopyData(&word,2); + } + break; + case 4: { + uint32_t lword = (uint32_t)value; + buffer.CopyData(&lword,4); + } + break; + case 8: { + buffer.CopyData(&value, 8); + } + break; + case 3: + case 5: + case 6: + case 7: + result.AppendError("unknown type. pass a string instead"); + return false; + default: + result.AppendError("do not know how to deal with larger than 8 byte result types. pass a string instead"); + return false; + } + } + else + { + result.AppendError("expression evaluation failed. pass a string instead?"); + return false; + } + } + else + { + result.AppendError("please pass either a block of text, or an expression to evaluate."); + return false; + } + + size_t count = m_memory_options.m_count.GetCurrentValue(); + found_location = low_addr; + bool ever_found = false; + while (count) + { + found_location = Search(found_location, high_addr, buffer.GetBytes(), buffer.GetByteSize()); + if (found_location == LLDB_INVALID_ADDRESS) + { + if (!ever_found) + { + result.AppendMessage("Your data was not found within the range.\n"); + result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); + } + else + result.AppendMessage("No more matches found within the range.\n"); + break; + } + result.AppendMessageWithFormat("Your data was found at location: 0x%" PRIx64 "\n", found_location); + if (m_memory_options.m_do_read.GetCurrentValue()) + { + StreamString cmd_buffer; + cmd_buffer.Printf("memory read 0x%" PRIx64, found_location); + m_interpreter.HandleCommand(cmd_buffer.GetData(), eLazyBoolNo, result); + } + --count; + found_location++; + ever_found = true; + } + + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); + return true; + } + + lldb::addr_t + Search (lldb::addr_t low, + lldb::addr_t high, + uint8_t* buffer, + size_t buffer_size) + { + Process *process = m_exe_ctx.GetProcessPtr(); + DataBufferHeap heap(buffer_size, 0); + lldb::addr_t fictional_ptr = low; + for (auto ptr = low; + low < high; + fictional_ptr++) + { + Error error; + if (ptr == low || buffer_size == 1) + process->ReadMemory(ptr, heap.GetBytes(), buffer_size, error); + else + { + memmove(heap.GetBytes(), heap.GetBytes()+1, buffer_size-1); + process->ReadMemory(ptr, heap.GetBytes()+buffer_size-1, 1, error); + } + if (error.Fail()) + return LLDB_INVALID_ADDRESS; + if (memcmp(heap.GetBytes(), buffer, buffer_size) == 0) + return fictional_ptr; + if (ptr == low) + ptr += buffer_size; + else + ptr += 1; + } + return LLDB_INVALID_ADDRESS; + } + + OptionGroupOptions m_option_group; + OptionGroupFindMemory m_memory_options; +}; + OptionDefinition g_memory_write_option_table[] = @@ -922,7 +1225,6 @@ g_memory_write_option_table[] = { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, NULL, 0, eArgTypeOffset, "Start writng bytes from an offset within the input file."}, }; - //---------------------------------------------------------------------- // Write memory to the inferior process //---------------------------------------------------------------------- @@ -948,13 +1250,13 @@ public: { return sizeof (g_memory_write_option_table) / sizeof (OptionDefinition); } - + virtual const OptionDefinition* GetDefinitions () { return g_memory_write_option_table; } - + virtual Error SetOptionValue (CommandInterpreter &interpreter, uint32_t option_idx, @@ -962,7 +1264,7 @@ public: { Error error; const int short_option = g_memory_write_option_table[option_idx].short_option; - + switch (short_option) { case 'i': @@ -973,7 +1275,7 @@ public: error.SetErrorStringWithFormat("input file does not exist: '%s'", option_arg); } break; - + case 'o': { bool success; @@ -1374,6 +1676,7 @@ CommandObjectMemory::CommandObjectMemory (CommandInterpreter &interpreter) : "A set of commands for operating on memory.", "memory []") { + LoadSubCommand ("find", CommandObjectSP (new CommandObjectMemoryFind (interpreter))); LoadSubCommand ("read", CommandObjectSP (new CommandObjectMemoryRead (interpreter))); LoadSubCommand ("write", CommandObjectSP (new CommandObjectMemoryWrite (interpreter))); }