Index: lldb/bindings/python/python-swigsafecast.swig =================================================================== --- lldb/bindings/python/python-swigsafecast.swig +++ lldb/bindings/python/python-swigsafecast.swig @@ -103,6 +103,11 @@ SWIGTYPE_p_lldb__SBAttachInfo); } +PythonObject ToSWIGWrapper(lldb::DataExtractorSP data_sp) { + return ToSWIGHelper(new lldb::DataExtractorSP(std::move(data_sp)), + SWIGTYPE_p_lldb__SBData); +} + ScopedPythonObject ToSWIGWrapper(CommandReturnObject &cmd_retobj) { return ScopedPythonObject( Index: lldb/examples/python/scripted_process/scripted_process.py =================================================================== --- lldb/examples/python/scripted_process/scripted_process.py +++ lldb/examples/python/scripted_process/scripted_process.py @@ -98,6 +98,21 @@ """ pass + def write_memory_at_address(self, addr, data, error): + """ Write a buffer to the scripted process memory. + + Args: + addr (int): Address from which we should start reading. + data (lldb.SBData): An `lldb.SBData` buffer to write to the + process memory. + error (lldb.SBError): Error object. + + Returns: + size (int): Size of the memory to read. + """ + error.SetErrorString("%s doesn't support memory writes." % self.__class__.__name__) + return 0 + def get_loaded_images(self): """ Get the list of loaded images for the scripted process. Index: lldb/include/lldb/Interpreter/ScriptedProcessInterface.h =================================================================== --- lldb/include/lldb/Interpreter/ScriptedProcessInterface.h +++ lldb/include/lldb/Interpreter/ScriptedProcessInterface.h @@ -55,6 +55,12 @@ return {}; } + virtual size_t WriteMemoryAtAddress(lldb::addr_t addr, + lldb::DataExtractorSP data_sp, + Status &error) { + return LLDB_INVALID_OFFSET; + }; + virtual StructuredData::ArraySP GetLoadedImages() { return {}; } virtual lldb::pid_t GetProcessID() { return LLDB_INVALID_PROCESS_ID; } Index: lldb/source/Plugins/Process/scripted/ScriptedProcess.h =================================================================== --- lldb/source/Plugins/Process/scripted/ScriptedProcess.h +++ lldb/source/Plugins/Process/scripted/ScriptedProcess.h @@ -69,6 +69,9 @@ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) override; + size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, + Status &error) override; + ArchSpec GetArchitecture(); Status Index: lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp =================================================================== --- lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp +++ lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp @@ -255,7 +255,31 @@ return ScriptedInterface::ErrorWithMessage( LLVM_PRETTY_FUNCTION, "Failed to copy read memory to buffer.", error); - return size; + // FIXME: We should use the diagnostic system to report a warning if the + // `bytes_copied` is different from `size`. + + return bytes_copied; +} + +size_t ScriptedProcess::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, + size_t size, Status &error) { + lldb::DataExtractorSP data_extractor_sp = std::make_shared( + buf, size, GetByteOrder(), GetAddressByteSize()); + + if (!data_extractor_sp || !data_extractor_sp->GetByteSize()) + return 0; + + size_t bytes_written = + GetInterface().WriteMemoryAtAddress(vm_addr, data_extractor_sp, error); + + if (!bytes_written || bytes_written == LLDB_INVALID_OFFSET) + return ScriptedInterface::ErrorWithMessage( + LLVM_PRETTY_FUNCTION, "Failed to copy write buffer to memory.", error); + + // FIXME: We should use the diagnostic system to report a warning if the + // `bytes_written` is different from `size`. + + return bytes_written; } ArchSpec ScriptedProcess::GetArchitecture() { Index: lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -79,6 +79,7 @@ PythonObject ToSWIGWrapper(lldb::ProcessAttachInfoSP attach_info_sp); PythonObject ToSWIGWrapper(lldb::ProcessLaunchInfoSP launch_info_sp); +PythonObject ToSWIGWrapper(lldb::DataExtractorSP data_extractor_sp); PythonObject ToSWIGWrapper(std::unique_ptr value_sb); PythonObject ToSWIGWrapper(std::unique_ptr stream_sb); Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h +++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h @@ -49,7 +49,9 @@ lldb::DataExtractorSP ReadMemoryAtAddress(lldb::addr_t address, size_t size, Status &error) override; - + + size_t WriteMemoryAtAddress(lldb::addr_t addr, lldb::DataExtractorSP data_sp, + Status &error) override; StructuredData::ArraySP GetLoadedImages() override; Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp +++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp @@ -135,6 +135,22 @@ return data_sp; } +size_t ScriptedProcessPythonInterface::WriteMemoryAtAddress( + lldb::addr_t addr, lldb::DataExtractorSP data_sp, Status &error) { + Status py_error; + StructuredData::ObjectSP obj = + Dispatch("write_memory_at_address", py_error, addr, data_sp, error); + + if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + return LLDB_INVALID_OFFSET; + + // If there was an error on the python call, surface it to the user. + if (py_error.Fail()) + error = py_error; + + return obj->GetIntegerValue(LLDB_INVALID_OFFSET); +} + StructuredData::ArraySP ScriptedProcessPythonInterface::GetLoadedImages() { Status error; StructuredData::ArraySP array = Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h +++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptedPythonInterface.h @@ -125,6 +125,10 @@ return python::ToSWIGWrapper(arg); } + python::PythonObject Transform(lldb::DataExtractorSP arg) { + return python::ToSWIGWrapper(arg); + } + template void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { // If U is not a PythonObject, don't touch it! Index: lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py =================================================================== --- lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py +++ lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py @@ -150,7 +150,6 @@ self.assertEqual(process_1.GetNumThreads(), 1) # ... then try reading from target #1 process ... - addr = 0x500000000 message = "Hello, target 1" buff = process_1.ReadCStringFromMemory(addr, len(message) + 1, error) self.assertSuccess(error) @@ -158,12 +157,22 @@ # ... now, reading again from target #0 process to make sure the call # gets dispatched to the right target. - addr = 0x500000000 message = "Hello, target 0" buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error) self.assertSuccess(error) self.assertEqual(buff, message) + # Let's write some memory. + message = "Hello, world!" + bytes_written = process_0.WriteCStringToMemory(addr, message, error) + self.assertSuccess(error) + self.assertEqual(bytes_written, len(message) + 1) + + # ... and check if that memory was saved properly. + buff = process_0.ReadCStringFromMemory(addr, len(message) + 1, error) + self.assertSuccess(error) + self.assertEqual(buff, message) + thread = process_0.GetSelectedThread() self.assertTrue(thread, "Invalid thread.") self.assertEqual(thread.GetThreadID(), 0x19) Index: lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py =================================================================== --- lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py +++ lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py @@ -7,20 +7,29 @@ from lldb.plugins.scripted_process import ScriptedThread class DummyScriptedProcess(ScriptedProcess): + memory = None + def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData): super().__init__(exe_ctx, args) self.threads[0] = DummyScriptedThread(self, None) - - def read_memory_at_address(self, addr: int, size: int, error: lldb.SBError) -> lldb.SBData: + self.memory = {} + addr = 0x500000000 debugger = self.target.GetDebugger() index = debugger.GetIndexOfTarget(self.target) + self.memory[addr] = "Hello, target " + str(index) + + def read_memory_at_address(self, addr: int, size: int, error: lldb.SBError) -> lldb.SBData: data = lldb.SBData().CreateDataFromCString( self.target.GetByteOrder(), self.target.GetCodeByteSize(), - "Hello, target " + str(index)) + self.memory[addr]) return data + def write_memory_at_address(self, addr, data, error): + self.memory[addr] = data.GetString(error, 0) + return len(self.memory[addr]) + def get_loaded_images(self): return self.loaded_images Index: lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp =================================================================== --- lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -286,3 +286,8 @@ lldb_private::python::ToSWIGWrapper(lldb::ProcessLaunchInfoSP) { return python::PythonObject(); } + +python::PythonObject +lldb_private::python::ToSWIGWrapper(lldb::DataExtractorSP) { + return python::PythonObject(); +}