Index: lldb/bindings/interface/SBProcess.i =================================================================== --- lldb/bindings/interface/SBProcess.i +++ lldb/bindings/interface/SBProcess.i @@ -417,6 +417,25 @@ lldb::SBProcessInfo GetProcessInfo(); + %feature("autodoc", " + Allocates a block of memory within the process, with size and + access permissions specified in the arguments. The permisssions + argument is an or-combination of zero or more of + lldb.ePermissionsWritable, lldb.ePermissionsReadable, and + lldb.ePermissionsExecutable. Returns the address + of the allocated buffer in the process, or + lldb.LLDB_INVALID_ADDRESS if the allocation failed.") AllocateMemory; + + lldb::addr_t + AllocateMemory(size_t size, uint32_t permissions, lldb::SBError &error); + + %feature("autodoc", " + Deallocates the block of memory (previously allocated using + AllocateMemory) given in the argument.") DeallocateMemory; + + lldb::SBError + DeallocateMemory(lldb::addr_t ptr); + STRING_EXTENSION(SBProcess) #ifdef SWIGPYTHON Index: lldb/include/lldb/API/SBProcess.h =================================================================== --- lldb/include/lldb/API/SBProcess.h +++ lldb/include/lldb/API/SBProcess.h @@ -370,6 +370,45 @@ /// valid. lldb::SBProcessInfo GetProcessInfo(); + /// Allocate memory within the process. + /// + /// This function will allocate memory in the process's address space. + /// + /// \param[in] size + /// The size of the allocation requested. + /// + /// \param[in] permissions + /// Or together any of the lldb::Permissions bits. The + /// permissions on a given memory allocation can't be changed + /// after allocation. Note that a block that isn't set writable + /// can still be written from lldb, just not by the process + /// itself. + /// + /// \param[out] error + /// An error object that gets filled in with any errors that + /// might occur when trying allocate. + /// + /// \return + /// The address of the allocated buffer in the process, or + /// LLDB_INVALID_ADDRESS if the allocation failed. + lldb::addr_t AllocateMemory(size_t size, uint32_t permissions, + lldb::SBError &error); + + /// Deallocate memory in the process. + /// + /// This function will deallocate memory in the process's address + /// space that was allocated with AllocateMemory. + /// + /// \param[in] ptr + /// A return value from AllocateMemory, pointing to the memory you + /// want to deallocate. + /// + /// \return + /// An error object describes any errors that occurred while + /// deallocating. + /// + lldb::SBError DeallocateMemory(lldb::addr_t ptr); + protected: friend class SBAddress; friend class SBBreakpoint; Index: lldb/source/API/SBProcess.cpp =================================================================== --- lldb/source/API/SBProcess.cpp +++ lldb/source/API/SBProcess.cpp @@ -1288,6 +1288,51 @@ return LLDB_RECORD_RESULT(sb_proc_info); } +lldb::addr_t SBProcess::AllocateMemory(size_t size, uint32_t permissions, + lldb::SBError &sb_error) { + LLDB_RECORD_METHOD(lldb::addr_t, SBProcess, AllocateMemory, + (size_t, uint32_t, lldb::SBError &), size, permissions, + sb_error); + + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + ProcessSP process_sp(GetSP()); + if (process_sp) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) { + std::lock_guard guard( + process_sp->GetTarget().GetAPIMutex()); + addr = process_sp->AllocateMemory(size, permissions, sb_error.ref()); + } else { + sb_error.SetErrorString("process is running"); + } + } else { + sb_error.SetErrorString("SBProcess is invalid"); + } + return addr; +} + +lldb::SBError SBProcess::DeallocateMemory(lldb::addr_t ptr) { + LLDB_RECORD_METHOD(lldb::SBError, SBProcess, DeallocateMemory, (lldb::addr_t), + ptr); + + lldb::SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) { + std::lock_guard guard( + process_sp->GetTarget().GetAPIMutex()); + Status error = process_sp->DeallocateMemory(ptr); + sb_error.SetError(error); + } else { + sb_error.SetErrorString("process is running"); + } + } else { + sb_error.SetErrorString("SBProcess is invalid"); + } + return sb_error; +} + namespace lldb_private { namespace repro { @@ -1417,6 +1462,10 @@ LLDB_REGISTER_METHOD(lldb::SBMemoryRegionInfoList, SBProcess, GetMemoryRegions, ()); LLDB_REGISTER_METHOD(lldb::SBProcessInfo, SBProcess, GetProcessInfo, ()); + LLDB_REGISTER_METHOD(lldb::addr_t, SBProcess, AllocateMemory, + (size_t, uint32_t, lldb::SBError &)); + LLDB_REGISTER_METHOD(lldb::SBError, SBProcess, DeallocateMemory, + (lldb::addr_t)); LLDB_REGISTER_CHAR_PTR_METHOD_CONST(size_t, SBProcess, GetSTDOUT); LLDB_REGISTER_CHAR_PTR_METHOD_CONST(size_t, SBProcess, GetSTDERR); Index: lldb/test/API/python_api/process/TestProcessAPI.py =================================================================== --- lldb/test/API/python_api/process/TestProcessAPI.py +++ lldb/test/API/python_api/process/TestProcessAPI.py @@ -398,3 +398,58 @@ "Process effective group ID is invalid") process_info.GetParentProcessID() + + def test_allocate_deallocate_memory(self): + """Test Python SBProcess.AllocateMemory() and SBProcess.DeallocateMemory() APIs.""" + self.build() + (target, process, main_thread, main_breakpoint) = lldbutil.run_to_source_breakpoint( + self, "// Set break point at this line", lldb.SBFileSpec("main.cpp")) + + # Allocate a block of memory in the target process + error = lldb.SBError() + addr = process.AllocateMemory(16384, lldb.ePermissionsReadable, error) + if not error.Success() or addr == lldb.LLDB_INVALID_ADDRESS: + self.fail("SBProcess.AllocateMemory() failed") + + # Now use WriteMemory() API to write 'a' into the allocated + # memory. Note that the debugger can do this even though the + # block is not set writable. + result = process.WriteMemory(addr, 'a', error) + if not error.Success() or result != 1: + self.fail("SBProcess.WriteMemory() failed") + + # Read from the memory location. This time it should be 'a'. + # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and + # expect to get a Python string as the result object! + content = process.ReadMemory(addr, 1, error) + if not error.Success(): + self.fail("SBProcess.ReadMemory() failed") + if self.TraceOn(): + print("memory content:", content) + + self.expect( + content, + "Result from SBProcess.ReadMemory() matches our expected output: 'a'", + exe=False, + startstr=b'a') + + # Verify that the process itself can read the allocated memory + frame = main_thread.GetFrameAtIndex(0) + val = frame.EvaluateExpression( + "test_read(reinterpret_cast({:#x}))".format(addr)) + self.expect(val.GetValue(), + "Result of test_read() matches expected output 'a'", + exe=False, + startstr="'a'") + + # Verify that the process cannot write into the block + val = frame.EvaluateExpression( + "test_write(reinterpret_cast({:#x}), 'b')".format(addr)) + if val.GetError().Success(): + self.fail( + "test_write() to allocated memory without write permission unexpectedly succeeded") + + # Deallocate the memory + error = process.DeallocateMemory(addr) + if not error.Success(): + self.fail("SBProcess.DeallocateMemory() failed") Index: lldb/test/API/python_api/process/main.cpp =================================================================== --- lldb/test/API/python_api/process/main.cpp +++ lldb/test/API/python_api/process/main.cpp @@ -21,3 +21,13 @@ return 0; // Set break point at this line and check variable 'my_char'. // Use lldb Python API to set memory content for my_int and check the result. } + +char test_read (char *ptr) +{ + return *ptr; +} + +void test_write (char *ptr, char c) +{ + *ptr = c; +}