diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp | 1786 |
1 files changed, 1786 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp new file mode 100644 index 000000000000..38bd3d179096 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Commands/CommandObjectMemory.cpp @@ -0,0 +1,1786 @@ +//===-- CommandObjectMemory.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 "CommandObjectMemory.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/DumpDataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/DataFormatters/ValueObjectPrinter.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupOutputFile.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Interpreter/OptionValueLanguage.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/MemoryHistory.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataBufferLLVM.h" +#include "lldb/Utility/StreamString.h" + +#include "lldb/lldb-private.h" + +#include <cinttypes> +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +#define LLDB_OPTIONS_memory_read +#include "CommandOptions.inc" + +class OptionGroupReadMemory : public OptionGroup { +public: + OptionGroupReadMemory() + : m_num_per_line(1, 1), m_output_as_binary(false), m_view_as_type(), + m_offset(0, 0), m_language_for_type(eLanguageTypeUnknown) {} + + ~OptionGroupReadMemory() override = default; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_memory_read_options); + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, + ExecutionContext *execution_context) override { + Status error; + const int short_option = g_memory_read_options[option_idx].short_option; + + switch (short_option) { + case 'l': + error = m_num_per_line.SetValueFromString(option_value); + if (m_num_per_line.GetCurrentValue() == 0) + error.SetErrorStringWithFormat( + "invalid value for --num-per-line option '%s'", + option_value.str().c_str()); + break; + + case 'b': + m_output_as_binary = true; + break; + + case 't': + error = m_view_as_type.SetValueFromString(option_value); + break; + + case 'r': + m_force = true; + break; + + case 'x': + error = m_language_for_type.SetValueFromString(option_value); + break; + + case 'E': + error = m_offset.SetValueFromString(option_value); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_num_per_line.Clear(); + m_output_as_binary = false; + m_view_as_type.Clear(); + m_force = false; + m_offset.Clear(); + m_language_for_type.Clear(); + } + + Status FinalizeSettings(Target *target, OptionGroupFormat &format_options) { + Status error; + OptionValueUInt64 &byte_size_value = format_options.GetByteSizeValue(); + OptionValueUInt64 &count_value = format_options.GetCountValue(); + const bool byte_size_option_set = byte_size_value.OptionWasSet(); + const bool num_per_line_option_set = m_num_per_line.OptionWasSet(); + const bool count_option_set = format_options.GetCountValue().OptionWasSet(); + + switch (format_options.GetFormat()) { + default: + break; + + case eFormatBoolean: + if (!byte_size_option_set) + byte_size_value = 1; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatCString: + break; + + case eFormatInstruction: + if (count_option_set) + byte_size_value = target->GetArchitecture().GetMaximumOpcodeByteSize(); + m_num_per_line = 1; + break; + + case eFormatAddressInfo: + if (!byte_size_option_set) + byte_size_value = target->GetArchitecture().GetAddressByteSize(); + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatPointer: + byte_size_value = target->GetArchitecture().GetAddressByteSize(); + if (!num_per_line_option_set) + m_num_per_line = 4; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatBinary: + case eFormatFloat: + case eFormatOctal: + case eFormatDecimal: + case eFormatEnum: + case eFormatUnicode8: + case eFormatUnicode16: + case eFormatUnicode32: + case eFormatUnsigned: + case eFormatHexFloat: + if (!byte_size_option_set) + byte_size_value = 4; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatBytes: + case eFormatBytesWithASCII: + if (byte_size_option_set) { + if (byte_size_value > 1) + error.SetErrorStringWithFormat( + "display format (bytes/bytes with ASCII) conflicts with the " + "specified byte size %" PRIu64 "\n" + "\tconsider using a different display format or don't specify " + "the byte size.", + byte_size_value.GetCurrentValue()); + } else + byte_size_value = 1; + if (!num_per_line_option_set) + m_num_per_line = 16; + if (!count_option_set) + format_options.GetCountValue() = 32; + break; + + case eFormatCharArray: + case eFormatChar: + case eFormatCharPrintable: + if (!byte_size_option_set) + byte_size_value = 1; + if (!num_per_line_option_set) + m_num_per_line = 32; + if (!count_option_set) + format_options.GetCountValue() = 64; + break; + + case eFormatComplex: + if (!byte_size_option_set) + byte_size_value = 8; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatComplexInteger: + if (!byte_size_option_set) + byte_size_value = 8; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + format_options.GetCountValue() = 8; + break; + + case eFormatHex: + if (!byte_size_option_set) + byte_size_value = 4; + if (!num_per_line_option_set) { + switch (byte_size_value) { + case 1: + case 2: + m_num_per_line = 8; + break; + case 4: + m_num_per_line = 4; + break; + case 8: + m_num_per_line = 2; + break; + default: + m_num_per_line = 1; + break; + } + } + if (!count_option_set) + count_value = 8; + break; + + case eFormatVectorOfChar: + case eFormatVectorOfSInt8: + case eFormatVectorOfUInt8: + case eFormatVectorOfSInt16: + case eFormatVectorOfUInt16: + case eFormatVectorOfSInt32: + case eFormatVectorOfUInt32: + case eFormatVectorOfSInt64: + case eFormatVectorOfUInt64: + case eFormatVectorOfFloat16: + case eFormatVectorOfFloat32: + case eFormatVectorOfFloat64: + case eFormatVectorOfUInt128: + if (!byte_size_option_set) + byte_size_value = 128; + if (!num_per_line_option_set) + m_num_per_line = 1; + if (!count_option_set) + count_value = 4; + break; + } + return error; + } + + bool AnyOptionWasSet() const { + return m_num_per_line.OptionWasSet() || m_output_as_binary || + m_view_as_type.OptionWasSet() || m_offset.OptionWasSet() || + m_language_for_type.OptionWasSet(); + } + + OptionValueUInt64 m_num_per_line; + bool m_output_as_binary; + OptionValueString m_view_as_type; + bool m_force; + OptionValueUInt64 m_offset; + OptionValueLanguage m_language_for_type; +}; + +// Read memory from the inferior process +class CommandObjectMemoryRead : public CommandObjectParsed { +public: + CommandObjectMemoryRead(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "memory read", + "Read from the memory of the current target process.", nullptr, + eCommandRequiresTarget | eCommandProcessMustBePaused), + m_option_group(), m_format_options(eFormatBytesWithASCII, 1, 8), + m_memory_options(), m_outfile_options(), m_varobj_options(), + m_next_addr(LLDB_INVALID_ADDRESS), m_prev_byte_size(0), + m_prev_format_options(eFormatBytesWithASCII, 1, 8), + m_prev_memory_options(), m_prev_outfile_options(), + m_prev_varobj_options() { + CommandArgumentEntry arg1; + CommandArgumentEntry arg2; + CommandArgumentData start_addr_arg; + CommandArgumentData end_addr_arg; + + // Define the first (and only) variant of this arg. + start_addr_arg.arg_type = eArgTypeAddressOrExpression; + start_addr_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the + // argument entry. + arg1.push_back(start_addr_arg); + + // Define the first (and only) variant of this arg. + end_addr_arg.arg_type = eArgTypeAddressOrExpression; + end_addr_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the + // argument entry. + arg2.push_back(end_addr_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back(arg1); + m_arguments.push_back(arg2); + + // Add the "--format" and "--count" options to group 1 and 3 + m_option_group.Append(&m_format_options, + OptionGroupFormat::OPTION_GROUP_FORMAT | + OptionGroupFormat::OPTION_GROUP_COUNT, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); + m_option_group.Append(&m_format_options, + OptionGroupFormat::OPTION_GROUP_GDB_FMT, + LLDB_OPT_SET_1 | LLDB_OPT_SET_3); + // Add the "--size" option to group 1 and 2 + m_option_group.Append(&m_format_options, + OptionGroupFormat::OPTION_GROUP_SIZE, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2); + m_option_group.Append(&m_memory_options); + m_option_group.Append(&m_outfile_options, LLDB_OPT_SET_ALL, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); + m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3); + m_option_group.Finalize(); + } + + ~CommandObjectMemoryRead() override = default; + + Options *GetOptions() override { return &m_option_group; } + + const char *GetRepeatCommand(Args ¤t_command_args, + uint32_t index) override { + return m_cmd_name.c_str(); + } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + // No need to check "target" for validity as eCommandRequiresTarget ensures + // it is valid + Target *target = m_exe_ctx.GetTargetPtr(); + + const size_t argc = command.GetArgumentCount(); + + if ((argc == 0 && m_next_addr == LLDB_INVALID_ADDRESS) || argc > 2) { + result.AppendErrorWithFormat("%s takes a start address expression with " + "an optional end address expression.\n", + m_cmd_name.c_str()); + result.AppendRawWarning("Expressions should be quoted if they contain " + "spaces or other special characters.\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + CompilerType compiler_type; + Status error; + + const char *view_as_type_cstr = + m_memory_options.m_view_as_type.GetCurrentValue(); + if (view_as_type_cstr && view_as_type_cstr[0]) { + // We are viewing memory as a type + + const bool exact_match = false; + TypeList type_list; + uint32_t reference_count = 0; + uint32_t pointer_count = 0; + size_t idx; + +#define ALL_KEYWORDS \ + KEYWORD("const") \ + KEYWORD("volatile") \ + KEYWORD("restrict") \ + KEYWORD("struct") \ + KEYWORD("class") \ + KEYWORD("union") + +#define KEYWORD(s) s, + static const char *g_keywords[] = {ALL_KEYWORDS}; +#undef KEYWORD + +#define KEYWORD(s) (sizeof(s) - 1), + static const int g_keyword_lengths[] = {ALL_KEYWORDS}; +#undef KEYWORD + +#undef ALL_KEYWORDS + + static size_t g_num_keywords = sizeof(g_keywords) / sizeof(const char *); + std::string type_str(view_as_type_cstr); + + // Remove all instances of g_keywords that are followed by spaces + for (size_t i = 0; i < g_num_keywords; ++i) { + const char *keyword = g_keywords[i]; + int keyword_len = g_keyword_lengths[i]; + + idx = 0; + while ((idx = type_str.find(keyword, idx)) != std::string::npos) { + if (type_str[idx + keyword_len] == ' ' || + type_str[idx + keyword_len] == '\t') { + type_str.erase(idx, keyword_len + 1); + idx = 0; + } else { + idx += keyword_len; + } + } + } + bool done = type_str.empty(); + // + idx = type_str.find_first_not_of(" \t"); + if (idx > 0 && idx != std::string::npos) + type_str.erase(0, idx); + while (!done) { + // Strip trailing spaces + if (type_str.empty()) + done = true; + else { + switch (type_str[type_str.size() - 1]) { + case '*': + ++pointer_count; + LLVM_FALLTHROUGH; + case ' ': + case '\t': + type_str.erase(type_str.size() - 1); + break; + + case '&': + if (reference_count == 0) { + reference_count = 1; + type_str.erase(type_str.size() - 1); + } else { + result.AppendErrorWithFormat("invalid type string: '%s'\n", + view_as_type_cstr); + result.SetStatus(eReturnStatusFailed); + return false; + } + break; + + default: + done = true; + break; + } + } + } + + llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files; + ConstString lookup_type_name(type_str.c_str()); + StackFrame *frame = m_exe_ctx.GetFramePtr(); + ModuleSP search_first; + if (frame) { + search_first = frame->GetSymbolContext(eSymbolContextModule).module_sp; + } + target->GetImages().FindTypes(search_first.get(), lookup_type_name, + exact_match, 1, searched_symbol_files, + type_list); + + if (type_list.GetSize() == 0 && lookup_type_name.GetCString()) { + LanguageType language_for_type = + m_memory_options.m_language_for_type.GetCurrentValue(); + std::set<LanguageType> languages_to_check; + if (language_for_type != eLanguageTypeUnknown) { + languages_to_check.insert(language_for_type); + } else { + languages_to_check = Language::GetSupportedLanguages(); + } + + std::set<CompilerType> user_defined_types; + for (auto lang : languages_to_check) { + if (auto *persistent_vars = + target->GetPersistentExpressionStateForLanguage(lang)) { + if (llvm::Optional<CompilerType> type = + persistent_vars->GetCompilerTypeFromPersistentDecl( + lookup_type_name)) { + user_defined_types.emplace(*type); + } + } + } + + if (user_defined_types.size() > 1) { + result.AppendErrorWithFormat( + "Mutiple types found matching raw type '%s', please disambiguate " + "by specifying the language with -x", + lookup_type_name.GetCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (user_defined_types.size() == 1) { + compiler_type = *user_defined_types.begin(); + } + } + + if (!compiler_type.IsValid()) { + if (type_list.GetSize() == 0) { + result.AppendErrorWithFormat("unable to find any types that match " + "the raw type '%s' for full type '%s'\n", + lookup_type_name.GetCString(), + view_as_type_cstr); + result.SetStatus(eReturnStatusFailed); + return false; + } else { + TypeSP type_sp(type_list.GetTypeAtIndex(0)); + compiler_type = type_sp->GetFullCompilerType(); + } + } + + while (pointer_count > 0) { + CompilerType pointer_type = compiler_type.GetPointerType(); + if (pointer_type.IsValid()) + compiler_type = pointer_type; + else { + result.AppendError("unable make a pointer type\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + --pointer_count; + } + + llvm::Optional<uint64_t> size = compiler_type.GetByteSize(nullptr); + if (!size) { + result.AppendErrorWithFormat( + "unable to get the byte size of the type '%s'\n", + view_as_type_cstr); + result.SetStatus(eReturnStatusFailed); + return false; + } + m_format_options.GetByteSizeValue() = *size; + + if (!m_format_options.GetCountValue().OptionWasSet()) + m_format_options.GetCountValue() = 1; + } else { + error = m_memory_options.FinalizeSettings(target, m_format_options); + } + + // Look for invalid combinations of settings + if (error.Fail()) { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + lldb::addr_t addr; + size_t total_byte_size = 0; + if (argc == 0) { + // Use the last address and byte size and all options as they were if no + // options have been set + addr = m_next_addr; + total_byte_size = m_prev_byte_size; + compiler_type = m_prev_compiler_type; + if (!m_format_options.AnyOptionWasSet() && + !m_memory_options.AnyOptionWasSet() && + !m_outfile_options.AnyOptionWasSet() && + !m_varobj_options.AnyOptionWasSet()) { + m_format_options = m_prev_format_options; + m_memory_options = m_prev_memory_options; + m_outfile_options = m_prev_outfile_options; + m_varobj_options = m_prev_varobj_options; + } + } + + size_t item_count = m_format_options.GetCountValue().GetCurrentValue(); + + // TODO For non-8-bit byte addressable architectures this needs to be + // revisited to fully support all lldb's range of formatting options. + // Furthermore code memory reads (for those architectures) will not be + // correctly formatted even w/o formatting options. + size_t item_byte_size = + target->GetArchitecture().GetDataByteSize() > 1 + ? target->GetArchitecture().GetDataByteSize() + : m_format_options.GetByteSizeValue().GetCurrentValue(); + + const size_t num_per_line = + m_memory_options.m_num_per_line.GetCurrentValue(); + + if (total_byte_size == 0) { + total_byte_size = item_count * item_byte_size; + if (total_byte_size == 0) + total_byte_size = 32; + } + + if (argc > 0) + addr = OptionArgParser::ToAddress(&m_exe_ctx, command[0].ref(), + LLDB_INVALID_ADDRESS, &error); + + if (addr == LLDB_INVALID_ADDRESS) { + result.AppendError("invalid start address expression."); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (argc == 2) { + lldb::addr_t end_addr = OptionArgParser::ToAddress( + &m_exe_ctx, command[1].ref(), LLDB_INVALID_ADDRESS, nullptr); + if (end_addr == LLDB_INVALID_ADDRESS) { + result.AppendError("invalid end address expression."); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } else if (end_addr <= addr) { + result.AppendErrorWithFormat( + "end address (0x%" PRIx64 + ") must be greater that the start address (0x%" PRIx64 ").\n", + end_addr, addr); + result.SetStatus(eReturnStatusFailed); + return false; + } else if (m_format_options.GetCountValue().OptionWasSet()) { + result.AppendErrorWithFormat( + "specify either the end address (0x%" PRIx64 + ") or the count (--count %" PRIu64 "), not both.\n", + end_addr, (uint64_t)item_count); + result.SetStatus(eReturnStatusFailed); + return false; + } + + total_byte_size = end_addr - addr; + item_count = total_byte_size / item_byte_size; + } + + uint32_t max_unforced_size = target->GetMaximumMemReadSize(); + + if (total_byte_size > max_unforced_size && !m_memory_options.m_force) { + result.AppendErrorWithFormat( + "Normally, \'memory read\' will not read over %" PRIu32 + " bytes of data.\n", + max_unforced_size); + result.AppendErrorWithFormat( + "Please use --force to override this restriction just once.\n"); + result.AppendErrorWithFormat("or set target.max-memory-read-size if you " + "will often need a larger limit.\n"); + return false; + } + + DataBufferSP data_sp; + size_t bytes_read = 0; + if (compiler_type.GetOpaqueQualType()) { + // Make sure we don't display our type as ASCII bytes like the default + // memory read + if (!m_format_options.GetFormatValue().OptionWasSet()) + m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault); + + llvm::Optional<uint64_t> size = compiler_type.GetByteSize(nullptr); + if (!size) { + result.AppendError("can't get size of type"); + return false; + } + bytes_read = *size * m_format_options.GetCountValue().GetCurrentValue(); + + if (argc > 0) + addr = addr + (*size * m_memory_options.m_offset.GetCurrentValue()); + } else if (m_format_options.GetFormatValue().GetCurrentValue() != + eFormatCString) { + data_sp = std::make_shared<DataBufferHeap>(total_byte_size, '\0'); + if (data_sp->GetBytes() == nullptr) { + result.AppendErrorWithFormat( + "can't allocate 0x%" PRIx32 + " bytes for the memory read buffer, specify a smaller size to read", + (uint32_t)total_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Address address(addr, nullptr); + bytes_read = target->ReadMemory(address, false, data_sp->GetBytes(), + data_sp->GetByteSize(), error); + if (bytes_read == 0) { + const char *error_cstr = error.AsCString(); + if (error_cstr && error_cstr[0]) { + result.AppendError(error_cstr); + } else { + result.AppendErrorWithFormat( + "failed to read memory from 0x%" PRIx64 ".\n", addr); + } + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (bytes_read < total_byte_size) + result.AppendWarningWithFormat( + "Not all bytes (%" PRIu64 "/%" PRIu64 + ") were able to be read from 0x%" PRIx64 ".\n", + (uint64_t)bytes_read, (uint64_t)total_byte_size, addr); + } else { + // we treat c-strings as a special case because they do not have a fixed + // size + if (m_format_options.GetByteSizeValue().OptionWasSet() && + !m_format_options.HasGDBFormat()) + item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue(); + else + item_byte_size = target->GetMaximumSizeOfStringSummary(); + if (!m_format_options.GetCountValue().OptionWasSet()) + item_count = 1; + data_sp = std::make_shared<DataBufferHeap>( + (item_byte_size + 1) * item_count, + '\0'); // account for NULLs as necessary + if (data_sp->GetBytes() == nullptr) { + result.AppendErrorWithFormat( + "can't allocate 0x%" PRIx64 + " bytes for the memory read buffer, specify a smaller size to read", + (uint64_t)((item_byte_size + 1) * item_count)); + result.SetStatus(eReturnStatusFailed); + return false; + } + uint8_t *data_ptr = data_sp->GetBytes(); + auto data_addr = addr; + auto count = item_count; + item_count = 0; + bool break_on_no_NULL = false; + while (item_count < count) { + std::string buffer; + buffer.resize(item_byte_size + 1, 0); + Status error; + size_t read = target->ReadCStringFromMemory(data_addr, &buffer[0], + item_byte_size + 1, error); + if (error.Fail()) { + result.AppendErrorWithFormat( + "failed to read memory from 0x%" PRIx64 ".\n", addr); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (item_byte_size == read) { + result.AppendWarningWithFormat( + "unable to find a NULL terminated string at 0x%" PRIx64 + ".Consider increasing the maximum read length.\n", + data_addr); + --read; + break_on_no_NULL = true; + } else + ++read; // account for final NULL byte + + memcpy(data_ptr, &buffer[0], read); + data_ptr += read; + data_addr += read; + bytes_read += read; + item_count++; // if we break early we know we only read item_count + // strings + + if (break_on_no_NULL) + break; + } + data_sp = + std::make_shared<DataBufferHeap>(data_sp->GetBytes(), bytes_read + 1); + } + + m_next_addr = addr + bytes_read; + m_prev_byte_size = bytes_read; + m_prev_format_options = m_format_options; + m_prev_memory_options = m_memory_options; + m_prev_outfile_options = m_outfile_options; + m_prev_varobj_options = m_varobj_options; + m_prev_compiler_type = compiler_type; + + std::unique_ptr<Stream> output_stream_storage; + Stream *output_stream_p = nullptr; + const FileSpec &outfile_spec = + m_outfile_options.GetFile().GetCurrentValue(); + + std::string path = outfile_spec.GetPath(); + if (outfile_spec) { + + auto open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate; + const bool append = m_outfile_options.GetAppend().GetCurrentValue(); + if (append) + open_options |= File::eOpenOptionAppend; + + auto outfile = FileSystem::Instance().Open(outfile_spec, open_options); + + if (outfile) { + auto outfile_stream_up = + std::make_unique<StreamFile>(std::move(outfile.get())); + if (m_memory_options.m_output_as_binary) { + const size_t bytes_written = + outfile_stream_up->Write(data_sp->GetBytes(), bytes_read); + if (bytes_written > 0) { + result.GetOutputStream().Printf( + "%zi bytes %s to '%s'\n", bytes_written, + append ? "appended" : "written", path.c_str()); + return true; + } else { + result.AppendErrorWithFormat("Failed to write %" PRIu64 + " bytes to '%s'.\n", + (uint64_t)bytes_read, path.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } else { + // We are going to write ASCII to the file just point the + // output_stream to our outfile_stream... + output_stream_storage = std::move(outfile_stream_up); + output_stream_p = output_stream_storage.get(); + } + } else { + result.AppendErrorWithFormat("Failed to open file '%s' for %s:\n", + path.c_str(), append ? "append" : "write"); + + result.AppendError(llvm::toString(outfile.takeError())); + result.SetStatus(eReturnStatusFailed); + return false; + } + } else { + output_stream_p = &result.GetOutputStream(); + } + + ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); + if (compiler_type.GetOpaqueQualType()) { + for (uint32_t i = 0; i < item_count; ++i) { + addr_t item_addr = addr + (i * item_byte_size); + Address address(item_addr); + StreamString name_strm; + name_strm.Printf("0x%" PRIx64, item_addr); + ValueObjectSP valobj_sp(ValueObjectMemory::Create( + exe_scope, name_strm.GetString(), address, compiler_type)); + if (valobj_sp) { + Format format = m_format_options.GetFormat(); + if (format != eFormatDefault) + valobj_sp->SetFormat(format); + + DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions( + eLanguageRuntimeDescriptionDisplayVerbosityFull, format)); + + valobj_sp->Dump(*output_stream_p, options); + } else { + result.AppendErrorWithFormat( + "failed to create a value object for: (%s) %s\n", + view_as_type_cstr, name_strm.GetData()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + return true; + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + DataExtractor data(data_sp, target->GetArchitecture().GetByteOrder(), + target->GetArchitecture().GetAddressByteSize(), + target->GetArchitecture().GetDataByteSize()); + + Format format = m_format_options.GetFormat(); + if (((format == eFormatChar) || (format == eFormatCharPrintable)) && + (item_byte_size != 1)) { + // if a count was not passed, or it is 1 + if (!m_format_options.GetCountValue().OptionWasSet() || item_count == 1) { + // this turns requests such as + // memory read -fc -s10 -c1 *charPtrPtr + // which make no sense (what is a char of size 10?) into a request for + // fetching 10 chars of size 1 from the same memory location + format = eFormatCharArray; + item_count = item_byte_size; + item_byte_size = 1; + } else { + // here we passed a count, and it was not 1 so we have a byte_size and + // a count we could well multiply those, but instead let's just fail + result.AppendErrorWithFormat( + "reading memory as characters of size %" PRIu64 " is not supported", + (uint64_t)item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + assert(output_stream_p); + size_t bytes_dumped = DumpDataExtractor( + data, output_stream_p, 0, format, item_byte_size, item_count, + num_per_line / target->GetArchitecture().GetDataByteSize(), addr, 0, 0, + exe_scope); + m_next_addr = addr + bytes_dumped; + output_stream_p->EOL(); + return true; + } + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options; + OptionGroupReadMemory m_memory_options; + OptionGroupOutputFile m_outfile_options; + OptionGroupValueObjectDisplay m_varobj_options; + lldb::addr_t m_next_addr; + lldb::addr_t m_prev_byte_size; + OptionGroupFormat m_prev_format_options; + OptionGroupReadMemory m_prev_memory_options; + OptionGroupOutputFile m_prev_outfile_options; + OptionGroupValueObjectDisplay m_prev_varobj_options; + CompilerType m_prev_compiler_type; +}; + +#define LLDB_OPTIONS_memory_find +#include "CommandOptions.inc" + +// Find the specified data in memory +class CommandObjectMemoryFind : public CommandObjectParsed { +public: + class OptionGroupFindMemory : public OptionGroup { + public: + OptionGroupFindMemory() : OptionGroup(), m_count(1), m_offset(0) {} + + ~OptionGroupFindMemory() override = default; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_memory_find_options); + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, + ExecutionContext *execution_context) override { + Status error; + const int short_option = g_memory_find_options[option_idx].short_option; + + switch (short_option) { + case 'e': + m_expr.SetValueFromString(option_value); + break; + + case 's': + m_string.SetValueFromString(option_value); + break; + + case 'c': + if (m_count.SetValueFromString(option_value).Fail()) + error.SetErrorString("unrecognized value for count"); + break; + + case 'o': + if (m_offset.SetValueFromString(option_value).Fail()) + error.SetErrorString("unrecognized value for dump-offset"); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_expr.Clear(); + m_string.Clear(); + m_count.Clear(); + } + + OptionValueString m_expr; + OptionValueString m_string; + OptionValueUInt64 m_count; + OptionValueUInt64 m_offset; + }; + + CommandObjectMemoryFind(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "memory find", + "Find a value in the memory of the current target process.", + nullptr, eCommandRequiresProcess | eCommandProcessMustBeLaunched), + m_option_group(), 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 = eArgTypeAddressOrExpression; + 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 = eArgTypeAddressOrExpression; + value_arg.arg_repetition = eArgRepeatPlain; + + // 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); + m_option_group.Finalize(); + } + + ~CommandObjectMemoryFind() override = default; + + Options *GetOptions() override { return &m_option_group; } + +protected: + class ProcessMemoryIterator { + public: + ProcessMemoryIterator(ProcessSP process_sp, lldb::addr_t base) + : m_process_sp(process_sp), m_base_addr(base), m_is_valid(true) { + lldbassert(process_sp.get() != nullptr); + } + + bool IsValid() { return m_is_valid; } + + uint8_t operator[](lldb::addr_t offset) { + if (!IsValid()) + return 0; + + uint8_t retval = 0; + Status error; + if (0 == + m_process_sp->ReadMemory(m_base_addr + offset, &retval, 1, error)) { + m_is_valid = false; + return 0; + } + + return retval; + } + + private: + ProcessSP m_process_sp; + lldb::addr_t m_base_addr; + bool m_is_valid; + }; + bool DoExecute(Args &command, CommandReturnObject &result) override { + // No need to check "process" for validity as eCommandRequiresProcess + // ensures it is valid + Process *process = m_exe_ctx.GetProcessPtr(); + + const size_t argc = command.GetArgumentCount(); + + if (argc != 2) { + result.AppendError("two addresses needed for memory find"); + return false; + } + + Status error; + lldb::addr_t low_addr = OptionArgParser::ToAddress( + &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); + if (low_addr == LLDB_INVALID_ADDRESS || error.Fail()) { + result.AppendError("invalid low address"); + return false; + } + lldb::addr_t high_addr = OptionArgParser::ToAddress( + &m_exe_ctx, command[1].ref(), LLDB_INVALID_ADDRESS, &error); + if (high_addr == LLDB_INVALID_ADDRESS || error.Fail()) { + result.AppendError("invalid high 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()); + else if (m_memory_options.m_expr.OptionWasSet()) { + StackFrame *frame = m_exe_ctx.GetFramePtr(); + ValueObjectSP result_sp; + if ((eExpressionCompleted == + process->GetTarget().EvaluateExpression( + m_memory_options.m_expr.GetStringValue(), frame, result_sp)) && + result_sp) { + uint64_t value = result_sp->GetValueAsUnsigned(0); + llvm::Optional<uint64_t> size = + result_sp->GetCompilerType().GetByteSize(nullptr); + if (!size) + return false; + switch (*size) { + 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( + "result size larger than 8 bytes. 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 = FastSearch(found_location, high_addr, buffer.GetBytes(), + buffer.GetByteSize()); + if (found_location == LLDB_INVALID_ADDRESS) { + if (!ever_found) { + result.AppendMessage("data not found within the range.\n"); + result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); + } else + result.AppendMessage("no more matches within the range.\n"); + break; + } + result.AppendMessageWithFormat("data found at location: 0x%" PRIx64 "\n", + found_location); + + DataBufferHeap dumpbuffer(32, 0); + process->ReadMemory( + found_location + m_memory_options.m_offset.GetCurrentValue(), + dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), error); + if (!error.Fail()) { + DataExtractor data(dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), + process->GetByteOrder(), + process->GetAddressByteSize()); + DumpDataExtractor( + data, &result.GetOutputStream(), 0, lldb::eFormatBytesWithASCII, 1, + dumpbuffer.GetByteSize(), 16, + found_location + m_memory_options.m_offset.GetCurrentValue(), 0, 0); + result.GetOutputStream().EOL(); + } + + --count; + found_location++; + ever_found = true; + } + + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); + return true; + } + + lldb::addr_t FastSearch(lldb::addr_t low, lldb::addr_t high, uint8_t *buffer, + size_t buffer_size) { + const size_t region_size = high - low; + + if (region_size < buffer_size) + return LLDB_INVALID_ADDRESS; + + std::vector<size_t> bad_char_heuristic(256, buffer_size); + ProcessSP process_sp = m_exe_ctx.GetProcessSP(); + ProcessMemoryIterator iterator(process_sp, low); + + for (size_t idx = 0; idx < buffer_size - 1; idx++) { + decltype(bad_char_heuristic)::size_type bcu_idx = buffer[idx]; + bad_char_heuristic[bcu_idx] = buffer_size - idx - 1; + } + for (size_t s = 0; s <= (region_size - buffer_size);) { + int64_t j = buffer_size - 1; + while (j >= 0 && buffer[j] == iterator[s + j]) + j--; + if (j < 0) + return low + s; + else + s += bad_char_heuristic[iterator[s + buffer_size - 1]]; + } + + return LLDB_INVALID_ADDRESS; + } + + OptionGroupOptions m_option_group; + OptionGroupFindMemory m_memory_options; +}; + +#define LLDB_OPTIONS_memory_write +#include "CommandOptions.inc" + +// Write memory to the inferior process +class CommandObjectMemoryWrite : public CommandObjectParsed { +public: + class OptionGroupWriteMemory : public OptionGroup { + public: + OptionGroupWriteMemory() : OptionGroup() {} + + ~OptionGroupWriteMemory() override = default; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_memory_write_options); + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, + ExecutionContext *execution_context) override { + Status error; + const int short_option = g_memory_write_options[option_idx].short_option; + + switch (short_option) { + case 'i': + m_infile.SetFile(option_value, FileSpec::Style::native); + FileSystem::Instance().Resolve(m_infile); + if (!FileSystem::Instance().Exists(m_infile)) { + m_infile.Clear(); + error.SetErrorStringWithFormat("input file does not exist: '%s'", + option_value.str().c_str()); + } + break; + + case 'o': { + if (option_value.getAsInteger(0, m_infile_offset)) { + m_infile_offset = 0; + error.SetErrorStringWithFormat("invalid offset string '%s'", + option_value.str().c_str()); + } + } break; + + default: + llvm_unreachable("Unimplemented option"); + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_infile.Clear(); + m_infile_offset = 0; + } + + FileSpec m_infile; + off_t m_infile_offset; + }; + + CommandObjectMemoryWrite(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "memory write", + "Write to the memory of the current target process.", nullptr, + eCommandRequiresProcess | eCommandProcessMustBeLaunched), + m_option_group(), m_format_options(eFormatBytes, 1, UINT64_MAX), + 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_format_options, + OptionGroupFormat::OPTION_GROUP_FORMAT, + LLDB_OPT_SET_1); + m_option_group.Append(&m_format_options, + OptionGroupFormat::OPTION_GROUP_SIZE, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2); + m_option_group.Append(&m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2); + m_option_group.Finalize(); + } + + ~CommandObjectMemoryWrite() override = default; + + Options *GetOptions() override { return &m_option_group; } + + bool UIntValueIsValidForSize(uint64_t uval64, size_t total_byte_size) { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1; + return uval64 <= max; + } + + bool SIntValueIsValidForSize(int64_t sval64, size_t total_byte_size) { + if (total_byte_size > 8) + return false; + + if (total_byte_size == 8) + return true; + + const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1; + const int64_t min = ~(max); + return min <= sval64 && sval64 <= max; + } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + // No need to check "process" for validity as eCommandRequiresProcess + // ensures it is valid + Process *process = m_exe_ctx.GetProcessPtr(); + + const size_t argc = command.GetArgumentCount(); + + if (m_memory_options.m_infile) { + if (argc < 1) { + result.AppendErrorWithFormat( + "%s takes a destination address when writing file contents.\n", + m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } else if (argc < 2) { + result.AppendErrorWithFormat( + "%s takes a destination address and at least one value.\n", + m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + StreamString buffer( + Stream::eBinary, + process->GetTarget().GetArchitecture().GetAddressByteSize(), + process->GetTarget().GetArchitecture().GetByteOrder()); + + OptionValueUInt64 &byte_size_value = m_format_options.GetByteSizeValue(); + size_t item_byte_size = byte_size_value.GetCurrentValue(); + + Status error; + lldb::addr_t addr = OptionArgParser::ToAddress( + &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); + + if (addr == LLDB_INVALID_ADDRESS) { + result.AppendError("invalid address expression\n"); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (m_memory_options.m_infile) { + size_t length = SIZE_MAX; + if (item_byte_size > 1) + length = item_byte_size; + auto data_sp = FileSystem::Instance().CreateDataBuffer( + m_memory_options.m_infile.GetPath(), length, + m_memory_options.m_infile_offset); + if (data_sp) { + length = data_sp->GetByteSize(); + if (length > 0) { + Status error; + size_t bytes_written = + process->WriteMemory(addr, data_sp->GetBytes(), length, error); + + if (bytes_written == length) { + // All bytes written + result.GetOutputStream().Printf( + "%" PRIu64 " bytes were written to 0x%" PRIx64 "\n", + (uint64_t)bytes_written, addr); + result.SetStatus(eReturnStatusSuccessFinishResult); + } else if (bytes_written > 0) { + // Some byte written + result.GetOutputStream().Printf( + "%" PRIu64 " bytes of %" PRIu64 + " requested were written to 0x%" PRIx64 "\n", + (uint64_t)bytes_written, (uint64_t)length, addr); + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendErrorWithFormat("Memory write to 0x%" PRIx64 + " failed: %s.\n", + addr, error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + } + } else { + result.AppendErrorWithFormat("Unable to read contents of file.\n"); + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); + } else if (item_byte_size == 0) { + if (m_format_options.GetFormat() == eFormatPointer) + item_byte_size = buffer.GetAddressByteSize(); + else + item_byte_size = 1; + } + + command.Shift(); // shift off the address argument + uint64_t uval64; + int64_t sval64; + bool success = false; + for (auto &entry : command) { + switch (m_format_options.GetFormat()) { + case kNumFormats: + case eFormatFloat: // TODO: add support for floats soon + case eFormatCharPrintable: + case eFormatBytesWithASCII: + case eFormatComplex: + case eFormatEnum: + case eFormatUnicode8: + case eFormatUnicode16: + case eFormatUnicode32: + case eFormatVectorOfChar: + case eFormatVectorOfSInt8: + case eFormatVectorOfUInt8: + case eFormatVectorOfSInt16: + case eFormatVectorOfUInt16: + case eFormatVectorOfSInt32: + case eFormatVectorOfUInt32: + case eFormatVectorOfSInt64: + case eFormatVectorOfUInt64: + case eFormatVectorOfFloat16: + case eFormatVectorOfFloat32: + case eFormatVectorOfFloat64: + case eFormatVectorOfUInt128: + case eFormatOSType: + case eFormatComplexInteger: + case eFormatAddressInfo: + case eFormatHexFloat: + case eFormatInstruction: + case eFormatVoid: + result.AppendError("unsupported format for writing memory"); + result.SetStatus(eReturnStatusFailed); + return false; + + case eFormatDefault: + case eFormatBytes: + case eFormatHex: + case eFormatHexUppercase: + case eFormatPointer: + { + // Decode hex bytes + // Be careful, getAsInteger with a radix of 16 rejects "0xab" so we + // have to special case that: + bool success = false; + if (entry.ref().startswith("0x")) + success = !entry.ref().getAsInteger(0, uval64); + if (!success) + success = !entry.ref().getAsInteger(16, uval64); + if (!success) { + result.AppendErrorWithFormat( + "'%s' is not a valid hex string value.\n", entry.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) { + result.AppendErrorWithFormat("Value 0x%" PRIx64 + " is too large to fit in a %" PRIu64 + " byte unsigned integer value.\n", + uval64, (uint64_t)item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64(uval64, item_byte_size); + break; + } + case eFormatBoolean: + uval64 = OptionArgParser::ToBoolean(entry.ref(), false, &success); + if (!success) { + result.AppendErrorWithFormat( + "'%s' is not a valid boolean string value.\n", entry.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64(uval64, item_byte_size); + break; + + case eFormatBinary: + if (entry.ref().getAsInteger(2, uval64)) { + result.AppendErrorWithFormat( + "'%s' is not a valid binary string value.\n", entry.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) { + result.AppendErrorWithFormat("Value 0x%" PRIx64 + " is too large to fit in a %" PRIu64 + " byte unsigned integer value.\n", + uval64, (uint64_t)item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64(uval64, item_byte_size); + break; + + case eFormatCharArray: + case eFormatChar: + case eFormatCString: { + if (entry.ref().empty()) + break; + + size_t len = entry.ref().size(); + // Include the NULL for C strings... + if (m_format_options.GetFormat() == eFormatCString) + ++len; + Status error; + if (process->WriteMemory(addr, entry.c_str(), len, error) == len) { + addr += len; + } else { + result.AppendErrorWithFormat("Memory write to 0x%" PRIx64 + " failed: %s.\n", + addr, error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + break; + } + case eFormatDecimal: + if (entry.ref().getAsInteger(0, sval64)) { + result.AppendErrorWithFormat( + "'%s' is not a valid signed decimal value.\n", entry.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } else if (!SIntValueIsValidForSize(sval64, item_byte_size)) { + result.AppendErrorWithFormat( + "Value %" PRIi64 " is too large or small to fit in a %" PRIu64 + " byte signed integer value.\n", + sval64, (uint64_t)item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64(sval64, item_byte_size); + break; + + case eFormatUnsigned: + + if (!entry.ref().getAsInteger(0, uval64)) { + result.AppendErrorWithFormat( + "'%s' is not a valid unsigned decimal string value.\n", + entry.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) { + result.AppendErrorWithFormat("Value %" PRIu64 + " is too large to fit in a %" PRIu64 + " byte unsigned integer value.\n", + uval64, (uint64_t)item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64(uval64, item_byte_size); + break; + + case eFormatOctal: + if (entry.ref().getAsInteger(8, uval64)) { + result.AppendErrorWithFormat( + "'%s' is not a valid octal string value.\n", entry.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) { + result.AppendErrorWithFormat("Value %" PRIo64 + " is too large to fit in a %" PRIu64 + " byte unsigned integer value.\n", + uval64, (uint64_t)item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64(uval64, item_byte_size); + break; + } + } + + if (!buffer.GetString().empty()) { + Status error; + if (process->WriteMemory(addr, buffer.GetString().data(), + buffer.GetString().size(), + error) == buffer.GetString().size()) + return true; + else { + result.AppendErrorWithFormat("Memory write to 0x%" PRIx64 + " failed: %s.\n", + addr, error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + return true; + } + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options; + OptionGroupWriteMemory m_memory_options; +}; + +// Get malloc/free history of a memory address. +class CommandObjectMemoryHistory : public CommandObjectParsed { +public: + CommandObjectMemoryHistory(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "memory history", "Print recorded stack traces for " + "allocation/deallocation events " + "associated with an address.", + nullptr, + eCommandRequiresTarget | eCommandRequiresProcess | + eCommandProcessMustBePaused | eCommandProcessMustBeLaunched) { + CommandArgumentEntry arg1; + CommandArgumentData addr_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); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back(arg1); + } + + ~CommandObjectMemoryHistory() override = default; + + const char *GetRepeatCommand(Args ¤t_command_args, + uint32_t index) override { + return m_cmd_name.c_str(); + } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + const size_t argc = command.GetArgumentCount(); + + if (argc == 0 || argc > 1) { + result.AppendErrorWithFormat("%s takes an address expression", + m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Status error; + lldb::addr_t addr = OptionArgParser::ToAddress( + &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); + + if (addr == LLDB_INVALID_ADDRESS) { + result.AppendError("invalid address expression"); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Stream *output_stream = &result.GetOutputStream(); + + const ProcessSP &process_sp = m_exe_ctx.GetProcessSP(); + const MemoryHistorySP &memory_history = + MemoryHistory::FindPlugin(process_sp); + + if (!memory_history) { + result.AppendError("no available memory history provider"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + HistoryThreads thread_list = memory_history->GetHistoryThreads(addr); + + const bool stop_format = false; + for (auto thread : thread_list) { + thread->GetStatus(*output_stream, 0, UINT32_MAX, 0, stop_format); + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + + return true; + } +}; + +// CommandObjectMemoryRegion +#pragma mark CommandObjectMemoryRegion + +class CommandObjectMemoryRegion : public CommandObjectParsed { +public: + CommandObjectMemoryRegion(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "memory region", + "Get information on the memory region containing " + "an address in the current target process.", + "memory region ADDR", + eCommandRequiresProcess | eCommandTryTargetAPILock | + eCommandProcessMustBeLaunched), + m_prev_end_addr(LLDB_INVALID_ADDRESS) {} + + ~CommandObjectMemoryRegion() override = default; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + ProcessSP process_sp = m_exe_ctx.GetProcessSP(); + if (process_sp) { + Status error; + lldb::addr_t load_addr = m_prev_end_addr; + m_prev_end_addr = LLDB_INVALID_ADDRESS; + + const size_t argc = command.GetArgumentCount(); + if (argc > 1 || (argc == 0 && load_addr == LLDB_INVALID_ADDRESS)) { + result.AppendErrorWithFormat("'%s' takes one argument:\nUsage: %s\n", + m_cmd_name.c_str(), m_cmd_syntax.c_str()); + result.SetStatus(eReturnStatusFailed); + } else { + if (command.GetArgumentCount() == 1) { + auto load_addr_str = command[0].ref(); + load_addr = OptionArgParser::ToAddress(&m_exe_ctx, load_addr_str, + LLDB_INVALID_ADDRESS, &error); + if (error.Fail() || load_addr == LLDB_INVALID_ADDRESS) { + result.AppendErrorWithFormat( + "invalid address argument \"%s\": %s\n", command[0].c_str(), + error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + } + + lldb_private::MemoryRegionInfo range_info; + error = process_sp->GetMemoryRegionInfo(load_addr, range_info); + if (error.Success()) { + lldb_private::Address addr; + ConstString name = range_info.GetName(); + ConstString section_name; + if (process_sp->GetTarget().ResolveLoadAddress(load_addr, addr)) { + SectionSP section_sp(addr.GetSection()); + if (section_sp) { + // Got the top most section, not the deepest section + while (section_sp->GetParent()) + section_sp = section_sp->GetParent(); + section_name = section_sp->GetName(); + } + } + result.AppendMessageWithFormat( + "[0x%16.16" PRIx64 "-0x%16.16" PRIx64 ") %c%c%c%s%s%s%s\n", + range_info.GetRange().GetRangeBase(), + range_info.GetRange().GetRangeEnd(), + range_info.GetReadable() ? 'r' : '-', + range_info.GetWritable() ? 'w' : '-', + range_info.GetExecutable() ? 'x' : '-', + name ? " " : "", name.AsCString(""), + section_name ? " " : "", section_name.AsCString("")); + m_prev_end_addr = range_info.GetRange().GetRangeEnd(); + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.SetStatus(eReturnStatusFailed); + result.AppendErrorWithFormat("%s\n", error.AsCString()); + } + } + } else { + m_prev_end_addr = LLDB_INVALID_ADDRESS; + result.AppendError("invalid process"); + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); + } + + const char *GetRepeatCommand(Args ¤t_command_args, + uint32_t index) override { + // If we repeat this command, repeat it without any arguments so we can + // show the next memory range + return m_cmd_name.c_str(); + } + + lldb::addr_t m_prev_end_addr; +}; + +// CommandObjectMemory + +CommandObjectMemory::CommandObjectMemory(CommandInterpreter &interpreter) + : CommandObjectMultiword( + interpreter, "memory", + "Commands for operating on memory in the current target process.", + "memory <subcommand> [<subcommand-options>]") { + LoadSubCommand("find", + CommandObjectSP(new CommandObjectMemoryFind(interpreter))); + LoadSubCommand("read", + CommandObjectSP(new CommandObjectMemoryRead(interpreter))); + LoadSubCommand("write", + CommandObjectSP(new CommandObjectMemoryWrite(interpreter))); + LoadSubCommand("history", + CommandObjectSP(new CommandObjectMemoryHistory(interpreter))); + LoadSubCommand("region", + CommandObjectSP(new CommandObjectMemoryRegion(interpreter))); +} + +CommandObjectMemory::~CommandObjectMemory() = default; |