[lldb] Add AllocateMemory/DeallocateMemory to the SBProcess API

This change adds AllocateMemory and DeallocateMemory methods to the SBProcess
API, so that clients can allocate and deallocate memory blocks within the
process being debugged (for storing JIT-compiled code or other uses).

(I am developing a debugger + REPL using the API; it will need to store
JIT-compiled code within the target.)

Reviewed By: clayborg, jingham

Differential Revision: https://reviews.llvm.org/D105389
This commit is contained in:
Peter S. Housel
2021-07-16 00:42:28 +02:00
committed by Raphael Isemann
parent bba8a76b87
commit 2e7ec447cc
5 changed files with 172 additions and 0 deletions

View File

@@ -417,6 +417,25 @@ public:
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

View File

@@ -370,6 +370,45 @@ public:
/// 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;

View File

@@ -1288,6 +1288,51 @@ lldb::SBProcessInfo SBProcess::GetProcessInfo() {
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<std::recursive_mutex> 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<std::recursive_mutex> 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 @@ void RegisterMethods<SBProcess>(Registry &R) {
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);

View File

@@ -398,3 +398,58 @@ class ProcessAPITestCase(TestBase):
"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<char *>({:#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<char *>({:#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")

View File

@@ -21,3 +21,13 @@ int main (int argc, char const *argv[])
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;
}