Skip to content

[lldb] Add support for ScriptedProcess #3535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lldb/bindings/interface/SBMemoryRegionInfo.i
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public:

SBMemoryRegionInfo (const lldb::SBMemoryRegionInfo &rhs);

SBMemoryRegionInfo::SBMemoryRegionInfo(const char *name, lldb::addr_t begin,
lldb::addr_t end, uint32_t permissions, bool mapped, bool stack_memory);

~SBMemoryRegionInfo ();

void
Expand Down
3 changes: 3 additions & 0 deletions lldb/bindings/interface/SBMemoryRegionInfoList.i
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public:
uint32_t
GetSize () const;

bool
GetMemoryRegionContainingAddress (lldb::addr_t addr, SBMemoryRegionInfo &region_info);

bool
GetMemoryRegionAtIndex (uint32_t idx, SBMemoryRegionInfo &region_info);

Expand Down
85 changes: 77 additions & 8 deletions lldb/bindings/python/python-wrapper.swig
Original file line number Diff line number Diff line change
Expand Up @@ -322,16 +322,69 @@ LLDBSwigPythonCreateScriptedProcess

PythonObject result = {};
if (arg_info.get().max_positional_args == 2) {
if (args_impl != nullptr) {
error_string.assign("args passed, but __init__ does not take an args dictionary");
Py_RETURN_NONE;
}
result = pfunc(target_arg, dict);
} else if (arg_info.get().max_positional_args >= 3) {
PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(new lldb::SBStructuredData(args_impl)));
result = pfunc(target_arg, args_arg, dict);
result = pfunc(target_arg, args_arg);
} else {
error_string.assign("wrong number of arguments in __init__, should be 2 or 3 (not including self)");
error_string.assign("wrong number of arguments in __init__, should be 2 (not including self)");
Py_RETURN_NONE;
}

if (result.IsAllocated())
return result.release();
Py_RETURN_NONE;
}

SWIGEXPORT void*
LLDBSwigPythonCreateScriptedThread
(
const char *python_class_name,
const char *session_dictionary_name,
const lldb::ProcessSP& process_sp,
lldb_private::StructuredDataImpl *args_impl,
std::string &error_string
)
{
if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name)
Py_RETURN_NONE;

PyErr_Cleaner py_err_cleaner(true);

auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name);
auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name, dict);

if (!pfunc.IsAllocated()) {
error_string.append("could not find script class: ");
error_string.append(python_class_name);
return nullptr;
}

// I do not want the SBProcess to be deallocated when going out of scope
// because python has ownership of it and will manage memory for this
// object by itself
PythonObject process_arg(PyRefType::Owned, SBTypeToSWIGWrapper(new lldb::SBProcess(process_sp)));

if (!process_arg.IsAllocated())
Py_RETURN_NONE;

llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
if (!arg_info) {
llvm::handleAllErrors(
arg_info.takeError(),
[&](PythonException &E) {
error_string.append(E.ReadBacktrace());
},
[&](const llvm::ErrorInfoBase &E) {
error_string.append(E.message());
});
Py_RETURN_NONE;
}

PythonObject result = {};
if (arg_info.get().max_positional_args == 2) {
PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(new lldb::SBStructuredData(args_impl)));
result = pfunc(process_arg, args_arg);
} else {
error_string.assign("wrong number of arguments in __init__, should be 2 (not including self)");
Py_RETURN_NONE;
}

Expand Down Expand Up @@ -917,6 +970,22 @@ LLDBSWIGPython_CastPyObjectToSBValue
return sb_ptr;
}

SWIGEXPORT void*
LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo
(
PyObject* data
)
{
lldb::SBMemoryRegionInfo* sb_ptr = NULL;

int valid_cast = SWIG_ConvertPtr(data, (void**)&sb_ptr, SWIGTYPE_p_lldb__SBMemoryRegionInfo, 0);

if (valid_cast == -1)
return NULL;

return sb_ptr;
}

SWIGEXPORT bool
LLDBSwigPythonCallCommand
(
Expand Down
441 changes: 334 additions & 107 deletions lldb/bindings/python/static-binding/LLDBWrapPython.cpp

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions lldb/bindings/python/static-binding/lldb.py
Original file line number Diff line number Diff line change
Expand Up @@ -7065,6 +7065,7 @@ def __init__(self, *args):
r"""
__init__(SBMemoryRegionInfo self) -> SBMemoryRegionInfo
__init__(SBMemoryRegionInfo self, SBMemoryRegionInfo rhs) -> SBMemoryRegionInfo
__init__(SBMemoryRegionInfo self, char const * name, lldb::addr_t begin, lldb::addr_t end, uint32_t permissions, bool mapped, bool stack_memory) -> SBMemoryRegionInfo
"""
_lldb.SBMemoryRegionInfo_swiginit(self, _lldb.new_SBMemoryRegionInfo(*args))
__swig_destroy__ = _lldb.delete_SBMemoryRegionInfo
Expand Down Expand Up @@ -7182,6 +7183,10 @@ def GetSize(self):
r"""GetSize(SBMemoryRegionInfoList self) -> uint32_t"""
return _lldb.SBMemoryRegionInfoList_GetSize(self)

def GetMemoryRegionContainingAddress(self, addr, region_info):
r"""GetMemoryRegionContainingAddress(SBMemoryRegionInfoList self, lldb::addr_t addr, SBMemoryRegionInfo region_info) -> bool"""
return _lldb.SBMemoryRegionInfoList_GetMemoryRegionContainingAddress(self, addr, region_info)

def GetMemoryRegionAtIndex(self, idx, region_info):
r"""GetMemoryRegionAtIndex(SBMemoryRegionInfoList self, uint32_t idx, SBMemoryRegionInfo region_info) -> bool"""
return _lldb.SBMemoryRegionInfoList_GetMemoryRegionAtIndex(self, idx, region_info)
Expand Down
101 changes: 96 additions & 5 deletions lldb/examples/python/scripted_process/my_scripted_process.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import os
import os,struct,signal

from typing import Any, Dict

import lldb
from lldb.plugins.scripted_process import ScriptedProcess
from lldb.plugins.scripted_process import ScriptedThread

class MyScriptedProcess(ScriptedProcess):
memory_regions = [
lldb.SBMemoryRegionInfo("stack", 0x1040b2000, 0x1040b4000, 0b110, True,
True)
]

stack_memory_dump = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'main.stack-dump')

def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
super().__init__(target, args)

def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
return self.memory_regions[0]
for region in self.memory_regions:
if region.GetRegionBase() <= addr < region.GetRegionEnd():
return region
return None

def get_thread_with_id(self, tid: int):
return {}
Expand All @@ -17,10 +31,25 @@ def get_registers_for_thread(self, tid: int):
return {}

def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData:
data = lldb.SBData().CreateDataFromCString(
data = lldb.SBData()

with open(self.stack_memory_dump, 'rb') as f:
stack_mem = f.read(-1)
if not stack_mem:
return data

mem_region = self.get_memory_region_containing_address(addr)

if not mem_region or addr + size > mem_region.GetRegionEnd():
return data

offset = addr - mem_region.GetRegionBase()
shrunk_stack_mem = stack_mem[offset:offset + size]

error = lldb.SBError()
data.SetData(error, shrunk_stack_mem,
self.target.GetByteOrder(),
self.target.GetCodeByteSize(),
"Hello, world!")
self.target.GetAddressByteSize())
return data

def get_loaded_images(self):
Expand All @@ -35,6 +64,68 @@ def should_stop(self) -> bool:
def is_alive(self) -> bool:
return True

def get_scripted_thread_plugin(self):
return MyScriptedThread.__module__ + "." + MyScriptedThread.__name__


class MyScriptedThread(ScriptedThread):
register_ctx = {
"rax":0x00000000000006e4,
"rbx":0x00000001040b6060,
"rcx":0x00000001040b2e00,
"rdx":0x00000001040b2ba8,
"rdi":0x000000000000002a,
"rsi":0x00000001040b2b98,
"rbp":0x00000001040b2a20,
"rsp":0x00000001040b2a20,
"r8":0x00000000003e131e,
"r9":0xffffffff00000000,
"r10":0x0000000000000000,
"r11":0x0000000000000246,
"r12":0x000000010007c3a0,
"r13":0x00000001040b2b18,
"r14":0x0000000100003f90,
"r15":0x00000001040b2b88,
"rip":0x0000000100003f61,
"rflags":0x0000000000000206,
"cs":0x000000000000002b,
"fs":0x0000000000000000,
"gs":0x0000000000000000,
}

def __init__(self, process, args):
super().__init__(process, args)

def get_thread_id(self) -> int:
return 0x19

def get_name(self) -> str:
return MyScriptedThread.__name__ + ".thread-1"

def get_stop_reason(self) -> Dict[str, Any]:
return { "type": lldb.eStopReasonSignal, "data": {
"signal": signal.SIGINT
} }

def get_stackframes(self):
class ScriptedStackFrame:
def __init__(idx, cfa, pc, symbol_ctx):
self.idx = idx
self.cfa = cfa
self.pc = pc
self.symbol_ctx = symbol_ctx


symbol_ctx = lldb.SBSymbolContext()
frame_zero = ScriptedStackFrame(0, 0x42424242, 0x5000000, symbol_ctx)
self.frames.append(frame_zero)

return self.frame_zero[0:0]

def get_register_context(self) -> str:
return struct.pack("{}Q".format(len(self.register_ctx)), *self.register_ctx.values())


def __lldb_init_module(debugger, dict):
if not 'SKIP_SCRIPTED_PROCESS_LAUNCH' in os.environ:
debugger.HandleCommand(
Expand Down
Loading